Content-Length: 480645 | pFad | https://github.com/sebadob/rauthy/commit/52c84c2343447698978be4e25e5f537aad0070e0

0B Merge pull request #155 from sebadob/impl-ephemeral-client-request · sebadob/rauthy@52c84c2 · GitHub
Skip to content

Commit

Permalink
Merge pull request #155 from sebadob/impl-ephemeral-client-request
Browse files Browse the repository at this point in the history
Impl ephemeral client request
  • Loading branch information
sebadob authored Nov 7, 2023
2 parents 9bbaeb2 + 6b3c9ea commit 52c84c2
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 16 deletions.
2 changes: 2 additions & 0 deletions rauthy-common/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ lazy_static! {
pub static ref RE_CODE_CHALLENGE: Regex = Regex::new(r"^[a-zA-Z0-9-\._~]{43,128}$").unwrap();
pub static ref RE_CODE_VERIFIER: Regex = Regex::new(r"^[a-zA-Z0-9-\._~+/=]+$").unwrap();
pub static ref RE_FLOWS: Regex = Regex::new(r"^(authorization_code|client_credentials|password|refresh_token)$").unwrap();
pub static ref RE_GRANT_TYPES: Regex = Regex::new(r"^(authorization_code|client_credentials|password|refresh_token)$").unwrap();
pub static ref RE_LOWERCASE: Regex = Regex::new(r"^[a-z0-9-_/]{2,128}$").unwrap();
pub static ref RE_LOWERCASE_SPACE: Regex = Regex::new(r"^[a-z0-9-_/\s]{2,128}$").unwrap();
pub static ref RE_GROUPS: Regex = Regex::new(r"^[a-z0-9-_/,]{2,32}$").unwrap();
pub static ref RE_MFA_CODE: Regex = Regex::new(r"^[a-zA-Z0-9]{48}$").unwrap();
pub static ref RE_URI: Regex = Regex::new(r"^[a-zA-Z0-9,.:/_\-&?=~#!$'()*+%]+$").unwrap();
Expand Down
133 changes: 119 additions & 14 deletions rauthy-models/src/entity/clients.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
use crate::app_state::{AppState, DbTxn};
use crate::entity::jwk::JwkKeyPairAlg;
use crate::entity::scopes::Scope;
use crate::entity::users::User;
use crate::request::NewClientRequest;
use crate::ListenScheme;
use std::str::FromStr;

use actix_multipart::Multipart;
use actix_web::http::header;
use actix_web::http::header::{HeaderName, HeaderValue};
use actix_web::http::{header, Uri};
use actix_web::{web, HttpRequest};
use futures_util::StreamExt;
use rauthy_common::constants::{CACHE_NAME_12HR, IDX_CLIENTS, IDX_CLIENT_LOGO, PROXY_MODE};
use rauthy_common::error_response::{ErrorResponse, ErrorResponseType};
use rauthy_common::utils::{cache_entry_client, get_client_ip, get_rand};
use rauthy_common::utils::{decrypt, encrypt};
use redhac::{
cache_del, cache_get, cache_get_from, cache_get_value, cache_insert, cache_put, cache_remove,
AckLevel,
};
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Row};
use std::str::FromStr;
use tracing::{debug, error, warn};
use utoipa::ToSchema;

use rauthy_common::constants::{CACHE_NAME_12HR, IDX_CLIENTS, IDX_CLIENT_LOGO, PROXY_MODE};
use rauthy_common::error_response::{ErrorResponse, ErrorResponseType};
use rauthy_common::utils::{cache_entry_client, get_client_ip, get_rand};
use rauthy_common::utils::{decrypt, encrypt};

use crate::app_state::{AppState, DbTxn};
use crate::entity::jwk::JwkKeyPairAlg;
use crate::entity::scopes::Scope;
use crate::entity::users::User;
use crate::request::{EphemeralClientRequest, NewClientRequest};
use crate::ListenScheme;

const RAUTHY_DEFAULT_LOGO: &str = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+Cjxzdmcgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDUxMiAxMzgiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgeG1sbnM6c2VyaWY9Imh0dHA6Ly93d3cuc2VyaWYuY29tLyIgc3R5bGU9ImZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxLjU7Ij4KICAgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDEsMCwwLDEsMCwtMTEpIj4KICAgICAgICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgxLDAsMCwxLDAsLTE3NikiPgogICAgICAgICAgICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCgwLjkyMDMyNSwwLDAsMS44NDE1MSw0NS45Mjc5LDI2LjQ1OSkiPgogICAgICAgICAgICAgICAgPHJlY3QgeD0iMjcuNzQxIiB5PSIxNTEuNTciIHdpZHRoPSIyMDAuNTE3IiBoZWlnaHQ9IjEwLjE0OCIgc3R5bGU9ImZpbGw6cmdiKDQsNywxMSk7Ii8+CiAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgPGcgdHJhbnNmb3JtPSJtYXRyaXgoMS45MzQ3MiwwLDAsMS44MjczMiw4LjM1NjE4LDI4Ljc1MzMpIj4KICAgICAgICAgICAgICAgIDxyZWN0IHg9IjMzLjMwNyIgeT0iOTcuMTUiIHdpZHRoPSI5NC42OTMiIGhlaWdodD0iNTQuNDIiIHN0eWxlPSJmaWxsOnJnYig0LDcsMTEpO3N0cm9rZTpyZ2IoNCw3LDExKTtzdHJva2Utd2lkdGg6MS4wNnB4OyIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDEuODI3MzIsMCwwLDEuODI3MzIsLTE2MC44MjIsNzAuMTgwNikiPgogICAgICAgICAgICAgICAgPGcgdHJhbnNmb3JtPSJtYXRyaXgoNzIsMCwwLDcyLDIyNy4xNzQsMTIzLjQxNykiPgogICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgPHRleHQgeD0iMTI4Ljk4MnB4IiB5PSIxMjMuNDE3cHgiIHN0eWxlPSJmb250LWZhbWlseTonQ2FsaWJyaS1Cb2xkJywgJ0NhbGlicmknLCBzYW5zLXNlcmlmO2ZvbnQtd2VpZ2h0OjcwMDtmb250LXNpemU6NzJweDtmaWxsOndoaXRlOyI+cjx0c3BhbiB4PSIxNTIuOTk0cHggMTg4LjUzN3B4ICIgeT0iMTIzLjQxN3B4IDEyMy40MTdweCAiPmF1PC90c3Bhbj48L3RleHQ+CiAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgPGcgdHJhbnNmb3JtPSJtYXRyaXgoMSwwLDAsMS4wMTYxNywtMS40MjEwOWUtMTQsLTUuMjQ0OTIpIj4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik00NDAuOTM2LDMyMi42NDNMNDM5LjIwNCwzMjQuMjY2TDI1NS40ODIsMzI0LjI2NkwyNTUuNDgyLDMwNS43MjFMNDQwLjkzNiwzMDUuNzIxTDQ0MC45MzYsMzIyLjY0M1oiIHN0eWxlPSJmaWxsOnVybCgjX0xpbmVhcjEpOyIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDAuOTIwMTkxLDAsMCwxLjg0MTIxLDQ2LjI0NjQsLTkxLjMzODMpIj4KICAgICAgICAgICAgICAgIDxyZWN0IHg9IjI3Ljc0MSIgeT0iMTUxLjU3IiB3aWR0aD0iMjAwLjUxNyIgaGVpZ2h0PSIxMC4xNDgiIHN0eWxlPSJmaWxsOnVybCgjX0xpbmVhcjIpOyIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDEuOTc1OTgsMCwwLDEuODQ2MTksMTkwLjE4NywyNi4wNjIpIj4KICAgICAgICAgICAgICAgIDxyZWN0IHg9IjMzLjMwNyIgeT0iOTcuMTUiIHdpZHRoPSI5NC42OTMiIGhlaWdodD0iNTQuNDIiIHN0eWxlPSJmaWxsOnJnYig0Myw2NSwxMDcpOyIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik00MzkuMjA0LDE4Ny43MzRMNDQwLjU1NywxODkuMDA3TDQ0MC41NTcsMjA2LjI3OUwyNTYsMjA2LjI3OUwyNTYsMTg3LjczNEw0MzkuMjA0LDE4Ny43MzRaIiBzdHlsZT0iZmlsbDpyZ2IoNDMsNjUsMTA3KTsiLz4KICAgICAgICAgICAgPGcgdHJhbnNmb3JtPSJtYXRyaXgoMS44MjczMiwwLDAsMS44MjczMiwtMTU0LjY2MSw3MC4xODA2KSI+CiAgICAgICAgICAgICAgICA8ZyB0cmFuc2Zvcm09Im1hdHJpeCg3MiwwLDAsNzIsMzIzLjA0NSwxMjMuNDE3KSI+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8dGV4dCB4PSIyMjYuNjQ2cHgiIHk9IjEyMy40MTdweCIgc3R5bGU9ImZvbnQtZmFtaWx5OidDYWxpYnJpLUJvbGQnLCAnQ2FsaWJyaScsIHNhbnMtc2VyaWY7Zm9udC13ZWlnaHQ6NzAwO2ZvbnQtc2l6ZTo3MnB4O2ZpbGw6d2hpdGU7Ij50aDx0c3BhbiB4PSIyODguOTQzcHggIiB5PSIxMjMuNDE3cHggIj55PC90c3Bhbj48L3RleHQ+CiAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgPGcgdHJhbnNmb3JtPSJtYXRyaXgoMiwwLDAsMiwwLDApIj4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMTkuNjAyLDkzLjg2N0wyNTYsMTI4TDIxOS42MDIsMTYyLjEzM0wyMTkuNjAyLDkzLjg2N1oiIHN0eWxlPSJmaWxsOnJnYig0Myw2NSwxMDcpOyIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ibWF0cml4KDIsMCwwLDEuOTU3MzksMCwzLjk5OTk3KSI+CiAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMzYuMzk4LDkzLjg2N0wwLDkzLjg2N0wzNS45MDgsMTI4LjUyNEwwLDE2My42MTlMMzYuMzk4LDE2My42MTkiIHN0eWxlPSJmaWxsOnJnYig0LDcsMTEpOyIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgPC9nPgogICAgPGRlZnM+CiAgICAgICAgPGxpbmVhckdyYWRpZW50IGlkPSJfTGluZWFyMSIgeDE9IjAiIHkxPSIwIiB4Mj0iMSIgeTI9IjAiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDE4NS40NTQsMCwwLDE4LjU0NDMsMjU1LjQ4MiwzMTQuOTk0KSI+PHN0b3Agb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjpyZ2IoNCw3LDExKTtzdG9wLW9wYWNpdHk6MSIvPjxzdG9wIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6cmdiKDQzLDY1LDEwNyk7c3RvcC1vcGFjaXR5OjEiLz48L2xpbmVhckdyYWRpZW50PgogICAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iX0xpbmVhcjIiIHgxPSIwIiB5MT0iMCIgeDI9IjEiIHkyPSIwIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgyMDAuNTE3LDAsMCwxMC4xNDgzLDI3Ljc0MTQsMTU2LjY0NSkiPjxzdG9wIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6cmdiKDQsNywxMSk7c3RvcC1vcGFjaXR5OjEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOnJnYig0Myw2NSwxMDcpO3N0b3Atb3BhY2l0eToxIi8+PC9saW5lYXJHcmFkaWVudD4KICAgIDwvZGVmcz4KPC9zdmc+Cg==";

/**
Expand Down Expand Up @@ -776,6 +779,52 @@ impl Client {
}
}

impl From<Uri> for Client {
fn from(value: Uri) -> Self {
todo!()
}
}

impl From<EphemeralClientRequest> for Client {
fn from(value: EphemeralClientRequest) -> Self {
let scopes = if let Some(scopes) = value.scope {
scopes.replace(' ', ",")
} else {
"openid,email,profile,groups".to_string()
};
let flows_enabled = value.grant_types.join(",");
let refresh_token = flows_enabled.contains("refresh_token");

Self {
id: value.client_id,
name: value.client_name,
enabled: true,
confidential: false,
secret: None,
secret_kid: None,
redirect_uris: value.redirect_uris.join(","),
post_logout_redirect_uris: value.post_logout_redirect_uris.map(|uris| uris.join(",")),
allowed_origens: None,
flows_enabled,
access_token_alg: value
.access_token_signed_response_alg
.unwrap_or_default()
.to_string(),
id_token_alg: value
.id_token_signed_response_alg
.unwrap_or_default()
.to_string(),
refresh_token,
auth_code_lifetime: 60,
access_token_lifetime: value.default_max_age.unwrap_or(1800),
scopes: scopes.clone(),
default_scopes: scopes,
challenge: Some("S256".to_string()),
force_mfa: false, // TODO maybe set by config var?
}
}
}

#[derive(Debug)]
pub struct NewClient {
pub id: String,
Expand Down Expand Up @@ -881,11 +930,14 @@ pub fn is_origen_external<'a>(

#[cfg(test)]
mod tests {
use super::*;
use std::env;

use actix_web::http::header;
use actix_web::test::TestRequest;
use pretty_assertions::assert_eq;
use std::env;
use validator::Validate;

use super::*;

#[test]
fn test_client_impl() {
Expand Down Expand Up @@ -1095,4 +1147,57 @@ mod tests {
is_origen_external(&req, &ListenScheme::HttpHttps, pub_url).unwrap();
assert_eq!(is_ext, false);
}

#[test]
fn test_from_ephemeral_client() {
let example_client_res_resp = r#"{
"@context": [ "https://www.w3.org/ns/solid/oidc-context.jsonld" ],
"client_id": "https://decentphtos.example/webid#this",
"client_name": "DecentPhotos",
"redirect_uris": [ "https://decentphotos.example/callback" ],
"post_logout_redirect_uris": [ "https://decentphotos.example/logout" ],
"client_uri": "https://decentphotos.example/",
"logo_uri": "https://decentphotos.example/logo.png",
"tos_uri": "https://decentphotos.example/tos.html",
"scope": "openid webid offline_access",
"grant_types": [ "refresh_token", "authorization_code" ],
"response_types": [ "code" ],
"default_max_age": 3600,
"require_auth_time": true
}"#;
let payload: EphemeralClientRequest =
serde_json::from_str(example_client_res_resp).unwrap();
// make sure our validation is good
payload.validate().unwrap();

// try build a client from it
let client = Client::from(payload);
println!("Client from EphemeralClientRequest:\n{:?}", client);

assert_eq!(client.id.as_str(), "https://decentphtos.example/webid#this");
assert_eq!(client.name.as_deref(), Some("DecentPhotos"));

let uris = client.get_redirect_uris();
assert_eq!(uris.len(), 1);
assert_eq!(
uris.get(0).unwrap().as_str(),
"https://decentphotos.example/callback",
);

let uris = client.get_post_logout_uris().unwrap();
assert_eq!(uris.len(), 1);
assert_eq!(
uris.get(0).unwrap().as_str(),
"https://decentphotos.example/logout",
);

let scopes = client.get_scopes();
assert_eq!(scopes.len(), 3);

let flows = client.get_flows();
assert_eq!(flows.len(), 2);

assert!(client.refresh_token);
}
}
2 changes: 1 addition & 1 deletion rauthy-models/src/events/health_watch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::app_state::DbPool;
use crate::entity::is_db_alive;
use crate::events::event::Event;
use rauthy_common::constants::HA_MODE;
use redhac::{QuorumHealth, QuorumHealthState, QuorumState};
use redhac::{QuorumHealth, QuorumHealthState};
use std::time::Duration;
use tokio::sync::watch;
use tracing::debug;
Expand Down
48 changes: 47 additions & 1 deletion rauthy-models/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use css_color::Srgb;
use rauthy_common::constants::{
RE_ALNUM, RE_ALNUM_24, RE_ALNUM_48, RE_ALNUM_64, RE_ALNUM_SPACE, RE_API_KEY, RE_APP_ID,
RE_ATTR, RE_ATTR_DESC, RE_CHALLENGE, RE_CLIENT_NAME, RE_CODE_CHALLENGE, RE_CODE_VERIFIER,
RE_FLOWS, RE_GROUPS, RE_LOWERCASE, RE_MFA_CODE, RE_URI, RE_USER_NAME,
RE_FLOWS, RE_GRANT_TYPES, RE_GROUPS, RE_LOWERCASE, RE_LOWERCASE_SPACE, RE_MFA_CODE, RE_URI,
RE_USER_NAME,
};
use rauthy_common::error_response::{ErrorResponse, ErrorResponseType};
use rauthy_common::utils::base64_decode;
Expand Down Expand Up @@ -139,6 +140,38 @@ pub struct EncKeyMigrateRequest {
pub key_id: String,
}

//github.com/ This request is used for ephemeral clients, which are needed for Solid OIDC for instance.
#[derive(Debug, Serialize, Deserialize, Validate, ToSchema)]
pub struct EphemeralClientRequest {
//github.com/ Validation: `[a-zA-Z0-9,.:/_-&?=~#!$'()*+%]+$`
#[validate(regex(path = "RE_URI", code = "[a-zA-Z0-9,.:/_-&?=~#!$'()*+%]+$"))]
pub client_id: String,
//github.com/ Validation: `[a-zA-Z0-9À-ÿ-\\s]{2,128}`
#[validate(regex(path = "RE_CLIENT_NAME", code = "[a-zA-Z0-9À-ÿ-\\s]{2,128}"))]
pub client_name: Option<String>,
//github.com/ Validation: `Vec<^[a-zA-Z0-9,.:/_\\-&?=~#!$'()*+%]+$>`
#[validate(custom(function = "validate_vec_uri"))]
pub redirect_uris: Vec<String>,
//github.com/ Validation: `Vec<^[a-zA-Z0-9,.:/_\\-&?=~#!$'()*+%]+$>`
#[validate(custom(function = "validate_vec_uri"))]
pub post_logout_redirect_uris: Option<Vec<String>>,
//github.com/ Validation: `Vec<^(authorization_code|client_credentials|password|refresh_token)$>`
#[validate(custom(function = "validate_vec_grant_type"))]
pub grant_types: Vec<String>,
//github.com/ Validation: `60 <= access_token_lifetime <= 86400`
#[validate(range(min = 60, max = 86400))]
pub default_max_age: Option<i32>,
//github.com/ Validation: `Vec<^[a-z0-9-_/\s]{2,128}$>`
#[validate(regex(path = "RE_LOWERCASE_SPACE", code = "[a-z0-9-_/\\s]{2,128}"))]
pub scope: Option<String>,
pub require_auth_time: Option<bool>,

//github.com/ Validation: `^(RS256|RS384|RS512|EdDSA)$`
pub access_token_signed_response_alg: Option<JwkKeyPairAlg>,
//github.com/ Validation: `^(RS256|RS384|RS512|EdDSA)$`
pub id_token_signed_response_alg: Option<JwkKeyPairAlg>,
}

#[derive(Debug, Deserialize, Validate, ToSchema, IntoParams)]
pub struct EventsListenParams {
//github.com/ Validation: `0 <= latest <= 1000`
Expand Down Expand Up @@ -747,6 +780,19 @@ fn validate_vec_uri(value: &[String]) -> Result<(), ValidationError> {
Ok(())
}

fn validate_vec_grant_type(value: &[String]) -> Result<(), ValidationError> {
let mut err = None;
value.iter().for_each(|v| {
if !RE_GRANT_TYPES.is_match(v) {
err = Some("authorization_code|client_credentials|password|refresh_token");
}
});
if let Some(e) = err {
return Err(ValidationError::new(e));
}
Ok(())
}

fn validate_vec_groups(value: &[String]) -> Result<(), ValidationError> {
let mut err = None;
value.iter().for_each(|v| {
Expand Down

0 comments on commit 52c84c2

Please sign in to comment.








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: https://github.com/sebadob/rauthy/commit/52c84c2343447698978be4e25e5f537aad0070e0

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy