From cc79b2a205bd8c7912f603c4fd31614c7df66584 Mon Sep 17 00:00:00 2001 From: Firstyear Date: Sat, 30 Dec 2023 09:15:26 +1000 Subject: [PATCH] 20231222 piv authentication (#2398) Foundations of PIV authentication --- examples/insecure_server.toml | 1 + server/core/src/actors/v1_read.rs | 147 +++++----- server/core/src/actors/v1_scim.rs | 24 +- server/core/src/actors/v1_write.rs | 169 ++++++------ server/core/src/config.rs | 23 +- server/core/src/https/extractors/mod.rs | 146 ++++++++-- server/core/src/https/middleware/mod.rs | 33 +-- server/core/src/https/mod.rs | 167 +++++++++++- server/core/src/https/oauth2.rs | 41 ++- server/core/src/https/v1.rs | 332 ++++++++++++++++------- server/core/src/https/v1_oauth2.rs | 42 ++- server/core/src/https/v1_scim.rs | 38 ++- server/core/src/ldaps.rs | 4 +- server/daemon/run_insecure_dev_server.sh | 2 + server/lib/src/idm/audit.rs | 2 + server/lib/src/idm/authsession.rs | 14 +- server/lib/src/idm/credupdatesession.rs | 50 +++- server/lib/src/idm/ldap.rs | 61 ++++- server/lib/src/idm/mod.rs | 50 +++- server/lib/src/idm/oauth2.rs | 264 +++++++++--------- server/lib/src/idm/reauth.rs | 68 +++-- server/lib/src/idm/scim.rs | 14 +- server/lib/src/idm/server.rs | 187 ++++++++----- server/lib/src/idm/serviceaccount.rs | 8 +- server/lib/src/lib.rs | 1 + server/lib/src/server/identity.rs | 22 +- 26 files changed, 1247 insertions(+), 663 deletions(-) diff --git a/examples/insecure_server.toml b/examples/insecure_server.toml index aa1719c1d..150399154 100644 --- a/examples/insecure_server.toml +++ b/examples/insecure_server.toml @@ -5,6 +5,7 @@ db_fs_type = "zfs" db_path = "/tmp/kanidm/kanidm.db" tls_chain = "/tmp/kanidm/chain.pem" tls_key = "/tmp/kanidm/key.pem" +tls_client_ca = "/tmp/kanidm/client_ca" # The log level of the server. May be one of info, debug, trace # diff --git a/server/core/src/actors/v1_read.rs b/server/core/src/actors/v1_read.rs index c1d1a4b05..e58e646bc 100644 --- a/server/core/src/actors/v1_read.rs +++ b/server/core/src/actors/v1_read.rs @@ -36,6 +36,7 @@ use kanidmd_lib::{ }, idm::server::{IdmServer, IdmServerTransaction}, idm::serviceaccount::ListApiTokenEvent, + idm::ClientAuthInfo, }; // =========================================================== @@ -70,12 +71,12 @@ impl QueryServerReadV1 { #[instrument( level = "info", name = "search", - skip(self, uat, req, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_search( &self, - uat: Option, + client_auth_info: ClientAuthInfo, req: SearchRequest, eventid: Uuid, ) -> Result { @@ -83,7 +84,7 @@ impl QueryServerReadV1 { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(?e, "Invalid identity"); e @@ -114,7 +115,7 @@ impl QueryServerReadV1 { sessionid: Option, req: AuthRequest, eventid: Uuid, - ip_addr: IpAddr, + client_auth_info: ClientAuthInfo, ) -> Result { // This is probably the first function that really implements logic // "on top" of the db server concept. In this case we check if @@ -137,12 +138,10 @@ impl QueryServerReadV1 { // the session are enforced. idm_auth.expire_auth_sessions(ct).await; - let source = Source::Https(ip_addr); - // Generally things like auth denied are in Ok() msgs // so true errors should always trigger a rollback. let res = idm_auth - .auth(&ae, ct, source) + .auth(&ae, ct, client_auth_info) .await .and_then(|r| idm_auth.commit().map(|_| r)); @@ -159,17 +158,16 @@ impl QueryServerReadV1 { )] pub async fn handle_reauth( &self, - uat: Option, + client_auth_info: ClientAuthInfo, issue: AuthIssueSession, eventid: Uuid, - ip_addr: IpAddr, ) -> Result { let ct = duration_from_epoch_now(); let mut idm_auth = self.idms.auth().await; security_info!("Begin reauth event"); let ident = idm_auth - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info.clone(), ct) .map_err(|e| { admin_error!(?e, "Invalid identity"); e @@ -180,12 +178,10 @@ impl QueryServerReadV1 { // the session are enforced. idm_auth.expire_auth_sessions(ct).await; - let source = Source::Https(ip_addr); - // Generally things like auth denied are in Ok() msgs // so true errors should always trigger a rollback. let res = idm_auth - .reauth_init(ident, issue, ct, source) + .reauth_init(ident, issue, ct, client_auth_info) .await .and_then(|r| idm_auth.commit().map(|_| r)); @@ -325,12 +321,12 @@ impl QueryServerReadV1 { #[instrument( level = "info", name = "whoami", - skip(self, uat, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_whoami( &self, - uat: Option, + client_auth_info: ClientAuthInfo, eventid: Uuid, ) -> Result { // Begin a read @@ -344,7 +340,7 @@ impl QueryServerReadV1 { // then move this to core.rs, and don't allow Option to get // this far. let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(?e, "Invalid identity"); e @@ -371,12 +367,12 @@ impl QueryServerReadV1 { #[instrument( level = "info", name = "whoami_uat", - skip(self, uat, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_whoami_uat( &self, - uat: Option, + client_auth_info: ClientAuthInfo, eventid: Uuid, ) -> Result { let ct = duration_from_epoch_now(); @@ -389,7 +385,7 @@ impl QueryServerReadV1 { // then move this to core.rs, and don't allow Option to get // this far. idms_prox_read - .validate_and_parse_token_to_uat(uat.as_deref(), ct) + .validate_client_auth_info_to_uat(client_auth_info, ct) .map_err(|e| { admin_error!(?e, "Invalid identity"); e @@ -400,18 +396,18 @@ impl QueryServerReadV1 { /// pull an image so we can present it to the user pub async fn handle_oauth2_rs_image_get_image( &self, - uat: Option, + client_auth_info: ClientAuthInfo, rs: Filter, ) -> Result { let mut idms_prox_read = self.idms.proxy_read().await; let ct = duration_from_epoch_now(); let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity in handle_oauth2_rs_image_get_image {:?}", uat); - e - })?; + .validate_client_auth_info_to_ident(client_auth_info, ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity in handle_oauth2_rs_image_get_image"); + e + })?; let attrs = vec![Attribute::Image.to_string()]; let search = SearchEvent::from_internal_message( @@ -442,7 +438,7 @@ impl QueryServerReadV1 { )] pub async fn handle_internalsearch( &self, - uat: Option, + client_auth_info: ClientAuthInfo, filter: Filter, attrs: Option>, eventid: Uuid, @@ -450,7 +446,7 @@ impl QueryServerReadV1 { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -485,7 +481,7 @@ impl QueryServerReadV1 { )] pub async fn handle_internalsearchrecycled( &self, - uat: Option, + client_auth_info: ClientAuthInfo, filter: Filter, attrs: Option>, eventid: Uuid, @@ -494,7 +490,7 @@ impl QueryServerReadV1 { let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -529,14 +525,14 @@ impl QueryServerReadV1 { )] pub async fn handle_internalradiusread( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result, OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -589,7 +585,7 @@ impl QueryServerReadV1 { )] pub async fn handle_internalradiustokenread( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result { @@ -597,7 +593,7 @@ impl QueryServerReadV1 { let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -636,7 +632,7 @@ impl QueryServerReadV1 { )] pub async fn handle_internalunixusertokenread( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result { @@ -644,7 +640,7 @@ impl QueryServerReadV1 { let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -689,14 +685,14 @@ impl QueryServerReadV1 { )] pub async fn handle_internalunixgrouptokenread( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -735,14 +731,14 @@ impl QueryServerReadV1 { )] pub async fn handle_internalsshkeyread( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result, OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -797,7 +793,7 @@ impl QueryServerReadV1 { )] pub async fn handle_internalsshkeytagread( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, tag: String, eventid: Uuid, @@ -805,7 +801,7 @@ impl QueryServerReadV1 { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -862,14 +858,14 @@ impl QueryServerReadV1 { )] pub async fn handle_service_account_api_token_get( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result, OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -894,14 +890,14 @@ impl QueryServerReadV1 { )] pub async fn handle_account_user_auth_token_get( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result, OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -926,7 +922,7 @@ impl QueryServerReadV1 { )] pub async fn handle_user_identity_verification( &self, - uat: Option, + client_auth_info: ClientAuthInfo, eventid: Uuid, user_request: IdentifyUserRequest, other_id: String, @@ -935,7 +931,7 @@ impl QueryServerReadV1 { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -967,7 +963,7 @@ impl QueryServerReadV1 { )] pub async fn handle_idmaccountunixauth( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, cred: String, eventid: Uuid, @@ -976,7 +972,7 @@ impl QueryServerReadV1 { let mut idm_auth = self.idms.auth().await; // resolve the id let ident = idm_auth - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -1017,7 +1013,7 @@ impl QueryServerReadV1 { )] pub async fn handle_idmcredentialstatus( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result { @@ -1025,7 +1021,7 @@ impl QueryServerReadV1 { let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -1063,7 +1059,7 @@ impl QueryServerReadV1 { )] pub async fn handle_idmbackupcodeview( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result { @@ -1071,7 +1067,7 @@ impl QueryServerReadV1 { let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -1296,14 +1292,14 @@ impl QueryServerReadV1 { )] pub async fn handle_oauth2_basic_secret_read( &self, - uat: Option, + client_auth_info: ClientAuthInfo, filter: Filter, eventid: Uuid, ) -> Result, OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -1349,26 +1345,21 @@ impl QueryServerReadV1 { )] pub async fn handle_oauth2_authorise( &self, - uat: Option, + client_auth_info: ClientAuthInfo, auth_req: AuthorisationRequest, eventid: Uuid, ) -> Result { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; - let (ident, uat) = idms_prox_read - .validate_and_parse_uat(uat.as_deref(), ct) - .and_then(|uat| { - idms_prox_read - .process_uat_to_identity(&uat, ct) - .map(|ident| (ident, uat)) - }) + let ident = idms_prox_read + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); Oauth2Error::AuthenticationRequired })?; // Now we can send to the idm server for authorisation checking. - idms_prox_read.check_oauth2_authorisation(&ident, &uat, &auth_req, ct) + idms_prox_read.check_oauth2_authorisation(&ident, &auth_req, ct) } #[instrument( @@ -1378,25 +1369,20 @@ impl QueryServerReadV1 { )] pub async fn handle_oauth2_authorise_reject( &self, - uat: Option, + client_auth_info: ClientAuthInfo, consent_req: String, eventid: Uuid, ) -> Result { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; - let (ident, uat) = idms_prox_read - .validate_and_parse_uat(uat.as_deref(), ct) - .and_then(|uat| { - idms_prox_read - .process_uat_to_identity(&uat, ct) - .map(|ident| (ident, uat)) - }) + let ident = idms_prox_read + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e })?; - idms_prox_read.check_oauth2_authorise_reject(&ident, &uat, &consent_req, ct) + idms_prox_read.check_oauth2_authorise_reject(&ident, &consent_req, ct) } #[instrument( @@ -1467,13 +1453,13 @@ impl QueryServerReadV1 { )] pub async fn handle_list_applinks( &self, - uat: Option, + client_auth_info: ClientAuthInfo, eventid: Uuid, ) -> Result, OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -1500,19 +1486,17 @@ impl QueryServerReadV1 { )] pub async fn handle_auth_valid( &self, - uat: Option, + client_auth_info: ClientAuthInfo, eventid: Uuid, ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; - // parse_token_to_ident idms_prox_read - .validate_and_parse_uat(uat.as_deref(), ct) - .and_then(|uat| idms_prox_read.process_uat_to_identity(&uat, ct)) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map(|_| ()) .map_err(|e| { - admin_error!("Invalid token: {:?}", e); + admin_error!("Invalid identity: {:?}", e); e }) } @@ -1527,11 +1511,12 @@ impl QueryServerReadV1 { eventid: Uuid, protomsg: LdapMsg, uat: Option, + ip_addr: IpAddr, ) -> Option { let res = match ServerOps::try_from(protomsg) { Ok(server_op) => self .ldap - .do_op(&self.idms, server_op, uat, eventid) + .do_op(&self.idms, server_op, uat, ip_addr, eventid) .await .unwrap_or_else(|e| { admin_error!("do_op failed -> {:?}", e); diff --git a/server/core/src/actors/v1_scim.rs b/server/core/src/actors/v1_scim.rs index c99404f73..f7ea7c683 100644 --- a/server/core/src/actors/v1_scim.rs +++ b/server/core/src/actors/v1_scim.rs @@ -16,7 +16,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_sync_account_token_generate( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, label: String, eventid: Uuid, @@ -24,7 +24,7 @@ impl QueryServerWriteV1 { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -56,14 +56,14 @@ impl QueryServerWriteV1 { )] pub async fn handle_sync_account_token_destroy( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -89,14 +89,14 @@ impl QueryServerWriteV1 { )] pub async fn handle_sync_account_finalise( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -124,14 +124,14 @@ impl QueryServerWriteV1 { )] pub async fn handle_sync_account_terminate( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -159,7 +159,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_scim_sync_apply( &self, - bearer: Option, + client_auth_info: ClientAuthInfo, changes: ScimSyncRequest, eventid: Uuid, ) -> Result<(), OperationError> { @@ -167,7 +167,7 @@ impl QueryServerWriteV1 { let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = - idms_prox_write.validate_and_parse_sync_token_to_ident(bearer.as_deref(), ct)?; + idms_prox_write.validate_sync_client_auth_info_to_ident(client_auth_info, ct)?; let sse = ScimSyncUpdateEvent { ident }; @@ -185,13 +185,13 @@ impl QueryServerReadV1 { )] pub async fn handle_scim_sync_status( &self, - bearer: Option, + client_auth_info: ClientAuthInfo, eventid: Uuid, ) -> Result { let ct = duration_from_epoch_now(); let mut idms_prox_read = self.idms.proxy_read().await; - let ident = idms_prox_read.validate_and_parse_sync_token_to_ident(bearer.as_deref(), ct)?; + let ident = idms_prox_read.validate_sync_client_auth_info_to_ident(client_auth_info, ct)?; idms_prox_read.scim_sync_get_state(&ident) } diff --git a/server/core/src/actors/v1_write.rs b/server/core/src/actors/v1_write.rs index d562b4f92..b300fb81b 100644 --- a/server/core/src/actors/v1_write.rs +++ b/server/core/src/actors/v1_write.rs @@ -55,7 +55,7 @@ impl QueryServerWriteV1 { #[instrument(level = "debug", skip_all)] async fn modify_from_parts( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: &str, proto_ml: &ProtoModifyList, filter: Filter, @@ -64,7 +64,7 @@ impl QueryServerWriteV1 { let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -103,7 +103,7 @@ impl QueryServerWriteV1 { #[instrument(level = "debug", skip_all)] async fn modify_from_internal_parts( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: &str, ml: &ModifyList, filter: Filter, @@ -112,7 +112,7 @@ impl QueryServerWriteV1 { let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -158,7 +158,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_create( &self, - uat: Option, + client_auth_info: ClientAuthInfo, req: CreateRequest, eventid: Uuid, ) -> Result<(), OperationError> { @@ -166,7 +166,7 @@ impl QueryServerWriteV1 { let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -195,14 +195,14 @@ impl QueryServerWriteV1 { )] pub async fn handle_modify( &self, - uat: Option, + client_auth_info: ClientAuthInfo, req: ModifyRequest, eventid: Uuid, ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -231,14 +231,14 @@ impl QueryServerWriteV1 { )] pub async fn handle_delete( &self, - uat: Option, + client_auth_info: ClientAuthInfo, req: DeleteRequest, eventid: Uuid, ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -266,7 +266,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_internalpatch( &self, - uat: Option, + client_auth_info: ClientAuthInfo, filter: Filter, update: ProtoEntry, eventid: Uuid, @@ -275,7 +275,7 @@ impl QueryServerWriteV1 { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -310,14 +310,14 @@ impl QueryServerWriteV1 { )] pub async fn handle_internaldelete( &self, - uat: Option, + client_auth_info: ClientAuthInfo, filter: Filter, eventid: Uuid, ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -345,14 +345,14 @@ impl QueryServerWriteV1 { )] pub async fn handle_reviverecycled( &self, - uat: Option, + client_auth_info: ClientAuthInfo, filter: Filter, eventid: Uuid, ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -380,14 +380,14 @@ impl QueryServerWriteV1 { )] pub async fn handle_service_account_credential_generate( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -425,7 +425,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_service_account_api_token_generate( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, label: String, expiry: Option, @@ -435,7 +435,7 @@ impl QueryServerWriteV1 { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -469,7 +469,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_service_account_api_token_destroy( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, token_id: Uuid, eventid: Uuid, @@ -477,7 +477,7 @@ impl QueryServerWriteV1 { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -509,7 +509,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_account_user_auth_token_destroy( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, token_id: Uuid, eventid: Uuid, @@ -517,7 +517,7 @@ impl QueryServerWriteV1 { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -549,23 +549,22 @@ impl QueryServerWriteV1 { )] pub async fn handle_logout( &self, - uat: Option, + client_auth_info: ClientAuthInfo, eventid: Uuid, ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; // We specifically need a uat here to assess the auth type! - let (ident, uat) = idms_prox_write - .validate_and_parse_uat(uat.as_deref(), ct) - .and_then(|uat| { - idms_prox_write - .process_uat_to_identity(&uat, ct) - .map(|ident| (ident, uat)) + let ident = idms_prox_write + .validate_client_auth_info_to_ident(client_auth_info, ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity"); + e })?; - if uat.uuid == UUID_ANONYMOUS { - info!("Ignoring request to logout anonymous session - these sessions are not recorded"); + if !ident.can_logout() { + info!("Ignoring request to logout session - these sessions are not recorded"); return Ok(()); } @@ -594,14 +593,14 @@ impl QueryServerWriteV1 { )] pub async fn handle_idmcredentialupdate( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result<(CUSessionToken, CUStatus), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -642,7 +641,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_idmcredentialupdateintent( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, ttl: Option, eventid: Uuid, @@ -650,7 +649,7 @@ impl QueryServerWriteV1 { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -781,14 +780,14 @@ impl QueryServerWriteV1 { )] pub async fn handle_service_account_into_person( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result<(), OperationError> { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -813,14 +812,14 @@ impl QueryServerWriteV1 { )] pub async fn handle_regenerateradius( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, eventid: Uuid, ) -> Result { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -859,7 +858,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_purgeattribute( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, attr: String, filter: Filter, @@ -868,7 +867,7 @@ impl QueryServerWriteV1 { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -911,7 +910,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_removeattributevalues( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, attr: String, values: Vec, @@ -921,7 +920,7 @@ impl QueryServerWriteV1 { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -966,12 +965,12 @@ impl QueryServerWriteV1 { #[instrument( level = "info", name = "append_attribute", - skip(self, uat, uuid_or_name, attr, values, filter, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_appendattribute( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, attr: String, values: Vec, @@ -986,19 +985,19 @@ impl QueryServerWriteV1 { .map(|v| ProtoModify::Present(attr.clone(), v)) .collect(), ); - self.modify_from_parts(uat, &uuid_or_name, &proto_ml, filter) + self.modify_from_parts(client_auth_info, &uuid_or_name, &proto_ml, filter) .await } #[instrument( level = "info", name = "set_attribute", - skip(self, uat, uuid_or_name, attr, values, filter, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_setattribute( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, attr: String, values: Vec, @@ -1016,7 +1015,7 @@ impl QueryServerWriteV1 { ) .collect(), ); - self.modify_from_parts(uat, &uuid_or_name, &proto_ml, filter) + self.modify_from_parts(client_auth_info, &uuid_or_name, &proto_ml, filter) .await } @@ -1028,7 +1027,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_sshkeycreate( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, tag: &str, key: &str, @@ -1041,7 +1040,7 @@ impl QueryServerWriteV1 { // than relying on the proto ones. let ml = ModifyList::new_append(Attribute::SshPublicKey, v_sk); - self.modify_from_internal_parts(uat, &uuid_or_name, &ml, filter) + self.modify_from_internal_parts(client_auth_info, &uuid_or_name, &ml, filter) .await } @@ -1053,7 +1052,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_idmaccountunixextend( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, ux: AccountUnixExtend, eventid: Uuid, @@ -1088,19 +1087,19 @@ impl QueryServerWriteV1 { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Account.into())); - self.modify_from_internal_parts(uat, &uuid_or_name, &ml, filter) + self.modify_from_internal_parts(client_auth_info, &uuid_or_name, &ml, filter) .await } #[instrument( level = "info", name = "idm_group_unix_extend", - skip(self, uat, uuid_or_name, gx, eventid) + skip_all, fields(uuid = ?eventid) )] pub async fn handle_idmgroupunixextend( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, gx: GroupUnixExtend, eventid: Uuid, @@ -1131,7 +1130,7 @@ impl QueryServerWriteV1 { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Group.into())); - self.modify_from_internal_parts(uat, &uuid_or_name, &ml, filter) + self.modify_from_internal_parts(client_auth_info, &uuid_or_name, &ml, filter) .await } @@ -1142,7 +1141,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_idmaccountunixsetcred( &self, - uat: Option, + client_auth_info: ClientAuthInfo, uuid_or_name: String, cred: String, eventid: Uuid, @@ -1150,7 +1149,7 @@ impl QueryServerWriteV1 { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -1185,18 +1184,18 @@ impl QueryServerWriteV1 { #[instrument(level = "debug", skip_all)] pub async fn handle_oauth2_rs_image_delete( &self, - uat: Option, + client_auth_info: ClientAuthInfo, rs: Filter, ) -> Result<(), OperationError> { let mut idms_prox_write = self.idms.proxy_write(duration_from_epoch_now()).await; let ct = duration_from_epoch_now(); let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) - .map_err(|e| { - admin_error!(err = ?e, "Invalid identity in handle_oauth2_rs_image_delete {:?}", uat); - e - })?; + .validate_client_auth_info_to_ident(client_auth_info, ct) + .map_err(|e| { + admin_error!(err = ?e, "Invalid identity in handle_oauth2_rs_image_delete"); + e + })?; let ml = ModifyList::new_purge(Attribute::Image); let mdf = match ModifyEvent::from_internal_parts(ident, &ml, &rs, &idms_prox_write.qs_write) { @@ -1215,7 +1214,7 @@ impl QueryServerWriteV1 { #[instrument(level = "debug", skip_all)] pub async fn handle_oauth2_rs_image_update( &self, - uat: Option, + client_auth_info: ClientAuthInfo, rs: Filter, image: ImageValue, ) -> Result<(), OperationError> { @@ -1223,9 +1222,9 @@ impl QueryServerWriteV1 { let ct = duration_from_epoch_now(); let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { - admin_error!(err = ?e, "Invalid identity in handle_oauth2_rs_image_update {:?}", uat); + admin_error!(err = ?e, "Invalid identity in handle_oauth2_rs_image_update"); e })?; @@ -1255,7 +1254,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_oauth2_scopemap_update( &self, - uat: Option, + client_auth_info: ClientAuthInfo, group: String, scopes: Vec, filter: Filter, @@ -1267,7 +1266,7 @@ impl QueryServerWriteV1 { let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -1316,7 +1315,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_oauth2_scopemap_delete( &self, - uat: Option, + client_auth_info: ClientAuthInfo, group: String, filter: Filter, eventid: Uuid, @@ -1325,7 +1324,7 @@ impl QueryServerWriteV1 { let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -1370,7 +1369,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_oauth2_sup_scopemap_update( &self, - uat: Option, + client_auth_info: ClientAuthInfo, group: String, scopes: Vec, filter: Filter, @@ -1382,7 +1381,7 @@ impl QueryServerWriteV1 { let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -1431,7 +1430,7 @@ impl QueryServerWriteV1 { )] pub async fn handle_oauth2_sup_scopemap_delete( &self, - uat: Option, + client_auth_info: ClientAuthInfo, group: String, filter: Filter, eventid: Uuid, @@ -1440,7 +1439,7 @@ impl QueryServerWriteV1 { let mut idms_prox_write = self.idms.proxy_write(ct).await; let ident = idms_prox_write - .validate_and_parse_token_to_ident(uat.as_deref(), ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { admin_error!(err = ?e, "Invalid identity"); e @@ -1487,26 +1486,22 @@ impl QueryServerWriteV1 { )] pub async fn handle_oauth2_authorise_permit( &self, - uat: Option, + client_auth_info: ClientAuthInfo, consent_req: String, eventid: Uuid, ) -> Result { let ct = duration_from_epoch_now(); let mut idms_prox_write = self.idms.proxy_write(ct).await; - let (ident, uat) = idms_prox_write - .validate_and_parse_uat(uat.as_deref(), ct) - .and_then(|uat| { - idms_prox_write - .process_uat_to_identity(&uat, ct) - .map(|ident| (ident, uat)) - }) + + let ident = idms_prox_write + .validate_client_auth_info_to_ident(client_auth_info, ct) .map_err(|e| { - admin_error!("Invalid identity: {:?}", e); + admin_error!(err = ?e, "Invalid identity"); e })?; idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_req, ct) + .check_oauth2_authorise_permit(&ident, &consent_req, ct) .and_then(|r| idms_prox_write.commit().map(|()| r)) } diff --git a/server/core/src/config.rs b/server/core/src/config.rs index 094a44718..122371077 100644 --- a/server/core/src/config.rs +++ b/server/core/src/config.rs @@ -77,6 +77,7 @@ fn default_online_backup_versions() -> usize { pub struct TlsConfiguration { pub chain: String, pub key: String, + pub client_ca: Option, } /// This is the Server Configuration as read from `server.toml`. @@ -100,6 +101,9 @@ pub struct ServerConfig { /// The file path to the TLS Private Key pub tls_key: Option, + /// The directory path of the client ca and crl dir. + pub tls_client_ca: Option, + /// The listener address for the HTTPS server. /// /// eg. `[::]:8443` or `127.0.0.1:8443`. Defaults to [kanidm_proto::constants::DEFAULT_SERVER_ADDRESS] @@ -213,6 +217,9 @@ impl ServerConfig { "TLS_KEY" => { self.tls_key = Some(value.to_string()); } + "TLS_CLIENT_CA" => { + self.tls_client_ca = Some(value.to_string()); + } "BINDADDRESS" => { self.bindaddress = Some(value.to_string()); } @@ -571,7 +578,7 @@ impl Configuration { pub fn update_config_for_server_mode(&mut self, sconfig: &ServerConfig) { #[cfg(any(test, debug_assertions))] debug!("update_config_for_server_mode {:?}", sconfig); - self.update_tls(&sconfig.tls_chain, &sconfig.tls_key); + self.update_tls(&sconfig.tls_chain, &sconfig.tls_key, &sconfig.tls_client_ca); self.update_bind(&sconfig.bindaddress); self.update_ldapbind(&sconfig.ldapbindaddress); self.update_online_backup(&sconfig.online_backup); @@ -632,13 +639,23 @@ impl Configuration { self.repl_config = repl_config; } - pub fn update_tls(&mut self, chain: &Option, key: &Option) { + pub fn update_tls( + &mut self, + chain: &Option, + key: &Option, + client_ca: &Option, + ) { match (chain, key) { (None, None) => {} (Some(chainp), Some(keyp)) => { let chain = chainp.to_string(); let key = keyp.to_string(); - self.tls_config = Some(TlsConfiguration { chain, key }) + let client_ca = client_ca.clone(); + self.tls_config = Some(TlsConfiguration { + chain, + key, + client_ca, + }) } _ => { eprintln!("ERROR: Invalid TLS configuration - must provide chain and key!"); diff --git a/server/core/src/https/extractors/mod.rs b/server/core/src/https/extractors/mod.rs index 04d3b20a6..d1fda70eb 100644 --- a/server/core/src/https/extractors/mod.rs +++ b/server/core/src/https/extractors/mod.rs @@ -1,10 +1,15 @@ use axum::{ async_trait, - extract::{ConnectInfo, FromRequestParts}, - http::{header::HeaderName, request::Parts, StatusCode}, + extract::connect_info::{ConnectInfo, Connected}, + extract::FromRequestParts, + http::{ + header::HeaderName, header::AUTHORIZATION as AUTHORISATION, request::Parts, StatusCode, + }, RequestPartsExt, }; +use hyper::server::conn::AddrStream; use kanidm_proto::constants::X_FORWARDED_FOR; +use kanidmd_lib::prelude::{ClientAuthInfo, ClientCertInfo, Source}; use std::net::{IpAddr, SocketAddr}; @@ -24,9 +29,23 @@ impl FromRequestParts for TrustedClientIp { parts: &mut Parts, state: &ServerState, ) -> Result { - if state.trust_x_forward_for { + let ConnectInfo(ClientConnInfo { + addr, + client_cert: _, + }) = parts + .extract::>() + .await + .map_err(|_| { + error!("Connect info contains invalid data"); + ( + StatusCode::BAD_REQUEST, + "connect info contains invalid data", + ) + })?; + + let ip_addr = if state.trust_x_forward_for { if let Some(x_forward_for) = parts.headers.get(X_FORWARDED_FOR_HEADER) { - // X forward for may be comma separate. + // X forward for may be comma separated. let first = x_forward_for .to_str() .map(|s| @@ -39,41 +58,116 @@ impl FromRequestParts for TrustedClientIp { ) })?; - first.parse::().map(TrustedClientIp).map_err(|_| { + first.parse::().map_err(|_| { ( StatusCode::BAD_REQUEST, "X-Forwarded-For contains invalid ip addr", ) - }) + })? } else { - let ConnectInfo(addr) = - parts - .extract::>() - .await - .map_err(|_| { - error!("Connect info contains invalid IP address"); - ( - StatusCode::BAD_REQUEST, - "connect info contains invalid IP address", - ) - })?; - - Ok(TrustedClientIp(addr.ip())) + addr.ip() } } else { - let ConnectInfo(addr) = - parts - .extract::>() - .await + addr.ip() + }; + + Ok(TrustedClientIp(ip_addr)) + } +} + +pub struct VerifiedClientInformation(pub ClientAuthInfo); + +#[async_trait] +impl FromRequestParts for VerifiedClientInformation { + type Rejection = (StatusCode, &'static str); + + #[instrument(level = "debug", skip(state))] + async fn from_request_parts( + parts: &mut Parts, + state: &ServerState, + ) -> Result { + let ConnectInfo(ClientConnInfo { addr, client_cert }) = parts + .extract::>() + .await + .map_err(|_| { + error!("Connect info contains invalid data"); + ( + StatusCode::BAD_REQUEST, + "connect info contains invalid data", + ) + })?; + + let ip_addr = if state.trust_x_forward_for { + if let Some(x_forward_for) = parts.headers.get(X_FORWARDED_FOR_HEADER) { + // X forward for may be comma separated. + let first = x_forward_for + .to_str() + .map(|s| + // Split on an optional comma, return the first result. + s.split(',').next().unwrap_or(s)) .map_err(|_| { - error!("Connect info contains invalid IP address"); ( StatusCode::BAD_REQUEST, - "connect info contains invalid IP address", + "X-Forwarded-For contains invalid data", ) })?; - Ok(TrustedClientIp(addr.ip())) + first.parse::().map_err(|_| { + ( + StatusCode::BAD_REQUEST, + "X-Forwarded-For contains invalid ip addr", + ) + })? + } else { + addr.ip() + } + } else { + addr.ip() + }; + + let bearer_token = if let Some(header) = parts.headers.get(AUTHORISATION) { + header + .to_str() + .map_err(|err| { + warn!(?err, "Invalid bearer token, ignoring"); + }) + .ok() + .and_then(|s| s.split_once(' ')) + .map(|(_, s)| s.to_string()) + .or_else(|| { + warn!("bearer token format invalid, ignoring"); + None + }) + } else { + None + }; + + Ok(VerifiedClientInformation(ClientAuthInfo { + source: Source::Https(ip_addr), + bearer_token, + client_cert, + })) + } +} + +#[derive(Debug, Clone)] +pub struct ClientConnInfo { + pub addr: SocketAddr, + // Only set if the certificate is VALID + pub client_cert: Option, +} + +impl Connected for ClientConnInfo { + fn connect_info(target: ClientConnInfo) -> Self { + target + } +} + +impl<'a> Connected<&'a AddrStream> for ClientConnInfo { + fn connect_info(target: &'a AddrStream) -> Self { + ClientConnInfo { + addr: target.remote_addr(), + client_cert: None, } } } diff --git a/server/core/src/https/middleware/mod.rs b/server/core/src/https/middleware/mod.rs index 296581508..2b946cd94 100644 --- a/server/core/src/https/middleware/mod.rs +++ b/server/core/src/https/middleware/mod.rs @@ -1,9 +1,7 @@ use axum::{ - headers::{authorization::Bearer, Authorization}, http::{HeaderValue, Request}, middleware::Next, response::Response, - TypedHeader, }; use kanidm_proto::constants::{KOPID, KVERSION}; use uuid::Uuid; @@ -24,18 +22,9 @@ pub async fn version_middleware(request: Request, next: Next) -> Respon response } -#[derive(Clone, Debug)] -/// For holding onto the event ID and other handy request-based things -pub struct KOpId { - /// The event correlation ID - pub eventid: Uuid, - /// The User Access Token, if present - pub uat: Option, -} - #[cfg(any(test, debug_assertions))] /// This is a debug middleware to ensure that /v1/ endpoints only return JSON -#[instrument(name = "are_we_json_yet", skip_all)] +#[instrument(level = "debug", name = "are_we_json_yet", skip_all)] pub async fn are_we_json_yet(request: Request, next: Next) -> Response { let uri = request.uri().path().to_string(); @@ -55,21 +44,21 @@ pub async fn are_we_json_yet(request: Request, next: Next) -> Response response } +#[derive(Clone, Debug)] +/// For holding onto the event ID and other handy request-based things +pub struct KOpId { + /// The event correlation ID + pub eventid: Uuid, +} + /// This runs at the start of the request, adding an extension with `KOpId` which has useful things inside it. -#[instrument(name = "kopid_middleware", skip_all, level = "DEBUG")] -pub async fn kopid_middleware( - auth: Option>>, - mut request: Request, - next: Next, -) -> Response { +#[instrument(level = "debug", name = "kopid_middleware", skip_all)] +pub async fn kopid_middleware(mut request: Request, next: Next) -> Response { // generate the event ID let eventid = sketching::tracing_forest::id(); - // get the bearer token from the headers if present. - let uat = auth.map(|bearer| bearer.token().to_string()); - // insert the extension so we can pull it out later - request.extensions_mut().insert(KOpId { eventid, uat }); + request.extensions_mut().insert(KOpId { eventid }); let mut response = next.run(request).await; // This conversion *should never* fail. If it does, rather than panic, we warn and diff --git a/server/core/src/https/mod.rs b/server/core/src/https/mod.rs index d18ce8acd..728cc26a7 100644 --- a/server/core/src/https/mod.rs +++ b/server/core/src/https/mod.rs @@ -14,11 +14,12 @@ mod v1; mod v1_oauth2; mod v1_scim; +use self::extractors::ClientConnInfo; use self::javascript::*; use crate::actors::v1_read::QueryServerReadV1; use crate::actors::v1_write::QueryServerWriteV1; use crate::config::{Configuration, ServerRole, TlsConfiguration}; -use axum::extract::connect_info::{IntoMakeServiceWithConnectInfo, ResponseFuture}; +use axum::extract::connect_info::IntoMakeServiceWithConnectInfo; use axum::http::{HeaderMap, HeaderValue}; use axum::middleware::{from_fn, from_fn_with_state}; use axum::response::Redirect; @@ -31,15 +32,19 @@ use hashbrown::HashMap; use hyper::server::accept::Accept; use hyper::server::conn::{AddrStream, Http}; use kanidm_proto::constants::KSESSIONID; +use kanidmd_lib::idm::ClientCertInfo; use kanidmd_lib::status::StatusActor; -use openssl::ssl::{Ssl, SslAcceptor, SslFiletype, SslMethod}; +use openssl::nid; +use openssl::ssl::{Ssl, SslAcceptor, SslFiletype, SslMethod, SslSessionCacheMode, SslVerifyMode}; +use openssl::x509::X509; use sketching::*; use tokio_openssl::SslStream; use futures_util::future::poll_fn; use tokio::net::TcpListener; -use std::io::ErrorKind; +use std::fs; +use std::io::{ErrorKind, Read}; use std::path::PathBuf; use std::pin::Pin; use std::sync::Arc; @@ -311,7 +316,7 @@ pub async fn create_https_server( .layer(trace_layer) .with_state(state) // the connect_info bit here lets us pick up the remote address of the client - .into_make_service_with_connect_info::(); + .into_make_service_with_connect_info::(); let addr = SocketAddr::from_str(&config.address).map_err(|err| { error!( @@ -361,10 +366,10 @@ pub async fn create_https_server( async fn server_loop( tls_param: TlsConfiguration, listener: TcpListener, - app: IntoMakeServiceWithConnectInfo, + app: IntoMakeServiceWithConnectInfo, ) -> Result<(), std::io::Error> { let mut tls_builder = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls())?; - let mut app = app; + tls_builder .set_certificate_chain_file(tls_param.chain.clone()) .map_err(|err| { @@ -373,6 +378,7 @@ async fn server_loop( format!("Failed to create TLS listener: {:?}", err), ) })?; + tls_builder .set_private_key_file(tls_param.key.clone(), SslFiletype::PEM) .map_err(|err| { @@ -381,13 +387,113 @@ async fn server_loop( format!("Failed to create TLS listener: {:?}", err), ) })?; + tls_builder.check_private_key().map_err(|err| { std::io::Error::new( ErrorKind::Other, format!("Failed to create TLS listener: {:?}", err), ) })?; - let acceptor = tls_builder.build(); + + // If configured, setup TLS client authentication. + if let Some(client_ca) = tls_param.client_ca.as_ref() { + debug!("Configuring client certificates from {}", client_ca); + + let verify = SslVerifyMode::PEER; + // In future we may add a "require mTLS option" which would necesitate this. + // verify.insert(SslVerifyMode::FAIL_IF_NO_PEER_CERT); + tls_builder.set_verify(verify); + + // When client certs are available, we disable the TLS session cache. + // This is so that when the smartcard is *removed* on the client, it forces + // the client session to immediately expire. + // + // https://stackoverflow.com/questions/12393711/session-disconnect-the-client-after-smart-card-is-removed + // + // Alternately, on logout we need to trigger https://docs.rs/openssl/latest/openssl/ssl/struct.Ssl.html#method.set_ssl_context + // with https://docs.rs/openssl/latest/openssl/ssl/struct.Ssl.html#method.ssl_context + + // https://docs.rs/openssl/latest/openssl/ssl/struct.SslContextRef.html#method.remove_session + // + // Or we lower session time outs etc. + tls_builder.set_session_cache_mode(SslSessionCacheMode::OFF); + + let read_dir = fs::read_dir(client_ca).map_err(|err| { + std::io::Error::new( + ErrorKind::Other, + format!("Failed to create TLS listener: {:?}", err), + ) + })?; + + for cert_dir_ent in read_dir.filter_map(|item| item.ok()).filter(|item| { + item.file_name() + .to_str() + // Hashed certs end in .0 + // Hsahed crls are .r0 + .map(|fname| fname.ends_with(".0")) + .unwrap_or_default() + }) { + let mut cert_pem = String::new(); + fs::File::open(cert_dir_ent.path()) + .and_then(|mut file| file.read_to_string(&mut cert_pem)) + .map_err(|err| { + std::io::Error::new( + ErrorKind::Other, + format!("Failed to create TLS listener: {:?}", err), + ) + })?; + + let cert = X509::from_pem(cert_pem.as_bytes()).map_err(|err| { + std::io::Error::new( + ErrorKind::Other, + format!("Failed to create TLS listener: {:?}", err), + ) + })?; + + let cert_store = tls_builder.cert_store_mut(); + cert_store.add_cert(cert.clone()).map_err(|err| { + std::io::Error::new( + ErrorKind::Other, + format!("Failed to create TLS listener: {:?}", err), + ) + })?; + // This tells the client what CA's they should use. It DOES NOT + // verify them. That's the job of the cert store above! + tls_builder.add_client_ca(&cert).map_err(|err| { + std::io::Error::new( + ErrorKind::Other, + format!("Failed to create TLS listener: {:?}", err), + ) + })?; + } + + // TODO: Build our own CRL map HERE! + + // Allow dumping client cert chains for dev debugging + // In the case this is status=false, should we be dumping these anyway? + if enabled!(tracing::Level::TRACE) { + tls_builder.set_verify_callback(verify, |status, x509store| { + if let Some(current_cert) = x509store.current_cert() { + let cert_text_bytes = current_cert.to_text().unwrap_or_default(); + let cert_text = String::from_utf8_lossy(cert_text_bytes.as_slice()); + tracing::warn!(client_cert = %cert_text); + }; + + if let Some(chain) = x509store.chain() { + for cert in chain.iter() { + let cert_text_bytes = cert.to_text().unwrap_or_default(); + let cert_text = String::from_utf8_lossy(cert_text_bytes.as_slice()); + tracing::warn!(chain_cert = %cert_text); + } + } + + status + }); + } + + // End tls_client setup + } + + let tls_acceptor = tls_builder.build(); let protocol = Arc::new(Http::new()); let mut listener = @@ -399,9 +505,12 @@ async fn server_loop( })?; loop { if let Some(Ok(stream)) = poll_fn(|cx| Pin::new(&mut listener).poll_accept(cx)).await { - let acceptor = acceptor.clone(); - let svc = tower::MakeService::make_service(&mut app, &stream); - tokio::spawn(handle_conn(acceptor, stream, svc, protocol.clone())); + let tls_acceptor = tls_acceptor.clone(); + let app = app.clone(); + + // let svc = tower::MakeService::make_service(&mut app, &stream); + // tokio::spawn(handle_conn(tls_acceptor, stream, svc, protocol.clone())); + tokio::spawn(handle_conn(tls_acceptor, stream, app, protocol.clone())); } } } @@ -410,7 +519,8 @@ async fn server_loop( pub(crate) async fn handle_conn( acceptor: SslAcceptor, stream: AddrStream, - svc: ResponseFuture, + // svc: ResponseFuture, + mut app: IntoMakeServiceWithConnectInfo, protocol: Arc, ) -> Result<(), std::io::Error> { let ssl = Ssl::new(acceptor.context()).map_err(|e| { @@ -418,6 +528,8 @@ pub(crate) async fn handle_conn( std::io::Error::from(ErrorKind::ConnectionAborted) })?; + let addr = stream.remote_addr(); + let mut tls_stream = SslStream::new(ssl, stream).map_err(|e| { error!("Failed to create TLS stream: {:?}", e); std::io::Error::from(ErrorKind::ConnectionAborted) @@ -425,6 +537,39 @@ pub(crate) async fn handle_conn( match SslStream::accept(Pin::new(&mut tls_stream)).await { Ok(_) => { + // Process the client cert (if any) + let client_cert = if let Some(peer_cert) = tls_stream.ssl().peer_certificate() { + // TODO: This is where we should be checking the CRL!!! + + let subject_key_id = peer_cert + .subject_key_id() + .map(|ski| ski.as_slice().to_vec()); + + let cn = if let Some(cn) = peer_cert + .subject_name() + .entries_by_nid(nid::Nid::COMMONNAME) + .next() + { + String::from_utf8(cn.data().as_slice().to_vec()) + .map_err(|err| { + warn!(?err, "client certificate CN contains invalid utf-8 - the CN will be ignored!"); + }) + .ok() + } else { + None + }; + + Some(ClientCertInfo { subject_key_id, cn }) + } else { + None + }; + + let client_conn_info = ClientConnInfo { addr, client_cert }; + + debug!(?client_conn_info); + + let svc = tower::MakeService::make_service(&mut app, client_conn_info); + let svc = svc.await.map_err(|e| { error!("Failed to build HTTP response: {:?}", e); std::io::Error::from(ErrorKind::Other) diff --git a/server/core/src/https/oauth2.rs b/server/core/src/https/oauth2.rs index 2be4a4987..606ce4310 100644 --- a/server/core/src/https/oauth2.rs +++ b/server/core/src/https/oauth2.rs @@ -1,6 +1,7 @@ use super::errors::WebError; use super::middleware::KOpId; use super::ServerState; +use crate::https::extractors::VerifiedClientInformation; use axum::extract::{Path, Query, State}; use axum::http::header::{ ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_ORIGIN, AUTHORIZATION, CONTENT_TYPE, @@ -93,13 +94,13 @@ pub(crate) fn oauth2_id(rs_name: &str) -> Filter { /// pub(crate) async fn oauth2_image_get( State(state): State, - Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(rs_name): Path, ) -> Response { let rs_filter = oauth2_id(&rs_name); let res = state .qe_r_ref - .handle_oauth2_rs_image_get_image(kopid.uat, rs_filter) + .handle_oauth2_rs_image_get_image(client_auth_info, rs_filter) .await; match res { @@ -186,9 +187,10 @@ pub(crate) async fn oauth2_image_get( pub async fn oauth2_authorise_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(auth_req): Json, ) -> impl IntoResponse { - let mut res = oauth2_authorise(state, auth_req, kopid) + let mut res = oauth2_authorise(state, auth_req, kopid, client_auth_info) .await .into_response(); if res.status() == StatusCode::FOUND { @@ -202,20 +204,22 @@ pub async fn oauth2_authorise_post( pub async fn oauth2_authorise_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Query(auth_req): Query, ) -> impl IntoResponse { // Start the oauth2 authorisation flow to present to the user. - oauth2_authorise(state, auth_req, kopid).await + oauth2_authorise(state, auth_req, kopid, client_auth_info).await } async fn oauth2_authorise( state: ServerState, auth_req: AuthorisationRequest, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> impl IntoResponse { let res: Result = state .qe_r_ref - .handle_oauth2_authorise(kopid.uat.clone(), auth_req, kopid.eventid) + .handle_oauth2_authorise(client_auth_info, auth_req, kopid.eventid) .await; match res { @@ -321,9 +325,10 @@ async fn oauth2_authorise( pub async fn oauth2_authorise_permit_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(consent_req): Json, ) -> impl IntoResponse { - let mut res = oauth2_authorise_permit(state, consent_req, kopid) + let mut res = oauth2_authorise_permit(state, consent_req, kopid, client_auth_info) .await .into_response(); if res.status() == StatusCode::FOUND { @@ -342,19 +347,21 @@ pub async fn oauth2_authorise_permit_get( State(state): State, Query(token): Query, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> impl IntoResponse { // When this is called, this indicates consent to proceed from the user. - oauth2_authorise_permit(state, token.token, kopid).await + oauth2_authorise_permit(state, token.token, kopid, client_auth_info).await } async fn oauth2_authorise_permit( state: ServerState, consent_req: String, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> impl IntoResponse { let res = state .qe_w_ref - .handle_oauth2_authorise_permit(kopid.uat, consent_req, kopid.eventid) + .handle_oauth2_authorise_permit(client_auth_info, consent_req, kopid.eventid) .await; match res { @@ -404,17 +411,19 @@ async fn oauth2_authorise_permit( pub async fn oauth2_authorise_reject_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Form(consent_req): Form, ) -> Response { - oauth2_authorise_reject(state, consent_req.token, kopid).await + oauth2_authorise_reject(state, consent_req.token, kopid, client_auth_info).await } pub async fn oauth2_authorise_reject_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Query(consent_req): Query, ) -> Response { - oauth2_authorise_reject(state, consent_req.token, kopid).await + oauth2_authorise_reject(state, consent_req.token, kopid, client_auth_info).await } // // https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1 @@ -424,13 +433,14 @@ async fn oauth2_authorise_reject( state: ServerState, consent_req: String, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> Response { // Need to go back to the redir_uri // For this, we'll need to lookup where to go. let res = state .qe_r_ref - .handle_oauth2_authorise_reject(kopid.uat, consent_req, kopid.eventid) + .handle_oauth2_authorise_reject(client_auth_info, consent_req, kopid.eventid) .await; match res { @@ -525,9 +535,10 @@ pub async fn oauth2_openid_userinfo_get( State(state): State, Path(client_id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, HTTPOauth2Error> { // The token we want to inspect is in the authorisation header. - let client_token = match kopid.uat { + let client_token = match client_auth_info.bearer_token { Some(val) => val, None => { error!("Bearer Authentication Not Provided"); @@ -564,10 +575,11 @@ pub async fn oauth2_openid_publickey_get( pub async fn oauth2_token_introspect_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, headers: HeaderMap, Form(intr_req): Form, ) -> impl IntoResponse { - let client_authz = match kopid.uat { + let client_authz = match client_auth_info.bearer_token { Some(val) => val, None => { error!("Bearer Authentication Not Provided, trying basic"); @@ -652,10 +664,11 @@ pub async fn oauth2_token_introspect_post( pub async fn oauth2_token_revoke_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Form(intr_req): Form, ) -> impl IntoResponse { // TODO: we should handle the session-based auth bit here I think maybe possibly there's no tests - let client_authz = match kopid.uat { + let client_authz = match client_auth_info.bearer_token { Some(val) => val, None => { return ( diff --git a/server/core/src/https/v1.rs b/server/core/src/https/v1.rs index 1b56f4e6d..9520f1a7b 100644 --- a/server/core/src/https/v1.rs +++ b/server/core/src/https/v1.rs @@ -31,7 +31,7 @@ use super::middleware::caching::{cache_me, dont_cache_me}; use super::middleware::KOpId; use super::ServerState; use crate::https::apidocs::response_schema::{ApiResponseWithout200, DefaultApiResponse}; -use crate::https::extractors::TrustedClientIp; +use crate::https::extractors::{TrustedClientIp, VerifiedClientInformation}; #[derive(Serialize, Deserialize, Debug, Clone)] pub(crate) struct SessionId { @@ -52,11 +52,12 @@ pub(crate) struct SessionId { pub async fn raw_create( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(msg): Json, ) -> Result, WebError> { state .qe_w_ref - .handle_create(kopid.uat, msg, kopid.eventid) + .handle_create(client_auth_info, msg, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -76,11 +77,12 @@ pub async fn raw_create( pub async fn raw_modify( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(msg): Json, ) -> Result, WebError> { state .qe_w_ref - .handle_modify(kopid.uat, msg, kopid.eventid) + .handle_modify(client_auth_info, msg, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -100,11 +102,12 @@ pub async fn raw_modify( pub async fn raw_delete( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(msg): Json, ) -> Result, WebError> { state .qe_w_ref - .handle_delete(kopid.uat, msg, kopid.eventid) + .handle_delete(client_auth_info, msg, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -125,11 +128,12 @@ pub async fn raw_delete( pub async fn raw_search( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(msg): Json, ) -> Result, WebError> { state .qe_r_ref - .handle_search(kopid.uat, msg, kopid.eventid) + .handle_search(client_auth_info, msg, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -149,11 +153,12 @@ pub async fn raw_search( pub async fn whoami( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { // New event, feed current auth data from the token to it. state .qe_r_ref - .handle_whoami(kopid.uat, kopid.eventid) + .handle_whoami(client_auth_info, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -172,10 +177,11 @@ pub async fn whoami( pub async fn whoami_uat( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { state .qe_r_ref - .handle_whoami_uat(kopid.uat, kopid.eventid) + .handle_whoami_uat(client_auth_info, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -193,10 +199,11 @@ pub async fn whoami_uat( pub async fn logout( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { state .qe_w_ref - .handle_logout(kopid.uat, kopid.eventid) + .handle_logout(client_auth_info, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -210,10 +217,11 @@ pub async fn json_rest_event_get( attrs: Option>, filter: Filter, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> Result>, WebError> { state .qe_r_ref - .handle_internalsearch(kopid.uat, filter, attrs, kopid.eventid) + .handle_internalsearch(client_auth_info, filter, attrs, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -225,12 +233,13 @@ pub async fn json_rest_event_get_id( filter: Filter, attrs: Option>, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> Result>, WebError> { let filter = Filter::join_parts_and(filter, filter_all!(f_id(id.as_str()))); state .qe_r_ref - .handle_internalsearch(kopid.uat, filter, attrs, kopid.eventid) + .handle_internalsearch(client_auth_info, filter, attrs, kopid.eventid) .await .map(|mut r| r.pop()) .map(Json::from) @@ -242,11 +251,12 @@ pub async fn json_rest_event_delete_id( id: String, filter: Filter, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> Result, WebError> { let filter = Filter::join_parts_and(filter, filter_all!(f_id(id.as_str()))); state .qe_w_ref - .handle_internaldelete(kopid.uat, filter, kopid.eventid) + .handle_internaldelete(client_auth_info, filter, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -258,12 +268,13 @@ pub async fn json_rest_event_get_attr( attr: String, filter: Filter, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> Result>>, WebError> { let filter = Filter::join_parts_and(filter, filter_all!(f_id(id))); let attrs = Some(vec![attr.clone()]); state .qe_r_ref - .handle_internalsearch(kopid.uat, filter, attrs, kopid.eventid) + .handle_internalsearch(client_auth_info, filter, attrs, kopid.eventid) .await .map(|mut event_result| event_result.pop().and_then(|mut e| e.attrs.remove(&attr))) .map(Json::from) @@ -276,8 +287,9 @@ pub async fn json_rest_event_get_id_attr( attr: String, filter: Filter, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> Result>>, WebError> { - json_rest_event_get_attr(state, id.as_str(), attr, filter, kopid).await + json_rest_event_get_attr(state, id.as_str(), attr, filter, kopid, client_auth_info).await } pub async fn json_rest_event_post( @@ -285,6 +297,7 @@ pub async fn json_rest_event_post( classes: Vec, obj: ProtoEntry, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> Result, WebError> { debug_assert!(!classes.is_empty()); @@ -296,7 +309,7 @@ pub async fn json_rest_event_post( state .qe_w_ref - .handle_create(kopid.uat, msg, kopid.eventid) + .handle_create(client_auth_info, msg, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -309,10 +322,11 @@ pub async fn json_rest_event_post_id_attr( filter: Filter, values: Vec, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> Result, WebError> { state .qe_w_ref - .handle_appendattribute(kopid.uat, id, attr, values, filter, kopid.eventid) + .handle_appendattribute(client_auth_info, id, attr, values, filter, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -325,10 +339,11 @@ pub async fn json_rest_event_put_attr( filter: Filter, values: Vec, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> Result, WebError> { state .qe_w_ref - .handle_setattribute(kopid.uat, id, attr, values, filter, kopid.eventid) + .handle_setattribute(client_auth_info, id, attr, values, filter, kopid.eventid) .await .map_err(WebError::from) .map(Json::from) @@ -341,10 +356,11 @@ pub async fn json_rest_event_post_attr( filter: Filter, values: Vec, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> Result, WebError> { state .qe_w_ref - .handle_appendattribute(kopid.uat, id, attr, values, filter, kopid.eventid) + .handle_appendattribute(client_auth_info, id, attr, values, filter, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -366,8 +382,9 @@ pub async fn json_rest_event_put_id_attr( filter: Filter, values: Vec, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> Result, WebError> { - json_rest_event_put_attr(state, id, attr, filter, values, kopid).await + json_rest_event_put_attr(state, id, attr, filter, values, kopid, client_auth_info).await } pub async fn json_rest_event_delete_id_attr( @@ -377,8 +394,9 @@ pub async fn json_rest_event_delete_id_attr( filter: Filter, values: Option>, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> Result, WebError> { - json_rest_event_delete_attr(state, id, attr, filter, values, kopid).await + json_rest_event_delete_attr(state, id, attr, filter, values, kopid, client_auth_info).await } pub async fn json_rest_event_delete_attr( @@ -388,6 +406,7 @@ pub async fn json_rest_event_delete_attr( filter: Filter, values: Option>, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> Result, WebError> { let values = match values { Some(val) => val, @@ -397,13 +416,13 @@ pub async fn json_rest_event_delete_attr( if values.is_empty() { state .qe_w_ref - .handle_purgeattribute(kopid.uat, uuid_or_name, attr, filter, kopid.eventid) + .handle_purgeattribute(client_auth_info, uuid_or_name, attr, filter, kopid.eventid) .await } else { state .qe_w_ref .handle_removeattributevalues( - kopid.uat, + client_auth_info, uuid_or_name, attr, values, @@ -430,6 +449,7 @@ pub async fn json_rest_event_delete_attr( pub async fn schema_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { // NOTE: This is filter_all, because from_internal_message will still do the alterations // needed to make it safe. This is needed because there may be aci's that block access @@ -439,7 +459,7 @@ pub async fn schema_get( f_eq(Attribute::Class, EntryClass::AttributeType.into()), f_eq(Attribute::Class, EntryClass::ClassType.into()) ])); - json_rest_event_get(state, None, filter, kopid).await + json_rest_event_get(state, None, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -455,9 +475,10 @@ pub async fn schema_get( pub async fn schema_attributetype_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::AttributeType.into())); - json_rest_event_get(state, None, filter, kopid).await + json_rest_event_get(state, None, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -474,6 +495,7 @@ pub async fn schema_attributetype_get_id( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { // These can't use get_id because the attribute name and class name aren't ... well name. let filter = filter_all!(f_and!([ @@ -486,7 +508,7 @@ pub async fn schema_attributetype_get_id( state .qe_r_ref - .handle_internalsearch(kopid.uat, filter, None, kopid.eventid) + .handle_internalsearch(client_auth_info, filter, None, kopid.eventid) .await .map(|mut r| r.pop()) .map(Json::from) @@ -506,9 +528,10 @@ pub async fn schema_attributetype_get_id( pub async fn schema_classtype_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::ClassType.into())); - json_rest_event_get(state, None, filter, kopid).await + json_rest_event_get(state, None, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -524,6 +547,7 @@ pub async fn schema_classtype_get( pub async fn schema_classtype_get_id( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result>, WebError> { // These can't use get_id because they attribute name and class name aren't ... well name. @@ -533,7 +557,7 @@ pub async fn schema_classtype_get_id( ])); state .qe_r_ref - .handle_internalsearch(kopid.uat, filter, None, kopid.eventid) + .handle_internalsearch(client_auth_info, filter, None, kopid.eventid) .await .map(|mut r| r.pop()) .map(Json::from) @@ -553,9 +577,10 @@ pub async fn schema_classtype_get_id( pub async fn person_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Person.into())); - json_rest_event_get(state, None, filter, kopid).await + json_rest_event_get(state, None, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -572,6 +597,7 @@ pub async fn person_get( pub async fn person_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(obj): Json, ) -> Result, WebError> { let classes: Vec = vec![ @@ -579,7 +605,7 @@ pub async fn person_post( EntryClass::Account.into(), EntryClass::Object.into(), ]; - json_rest_event_post(state, classes, obj, kopid).await + json_rest_event_post(state, classes, obj, kopid, client_auth_info).await } #[utoipa::path( @@ -596,9 +622,10 @@ pub async fn person_id_get( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Person.into())); - json_rest_event_get_id(state, id, filter, None, kopid).await + json_rest_event_get_id(state, id, filter, None, kopid, client_auth_info).await } #[utoipa::path( @@ -614,9 +641,10 @@ pub async fn person_id_delete( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Person.into())); - json_rest_event_delete_id(state, id, filter, kopid).await + json_rest_event_delete_id(state, id, filter, kopid, client_auth_info).await } // // == account == @@ -634,9 +662,10 @@ pub async fn person_id_delete( pub async fn service_account_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::ServiceAccount.into())); - json_rest_event_get(state, None, filter, kopid).await + json_rest_event_get(state, None, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -652,6 +681,7 @@ pub async fn service_account_get( pub async fn service_account_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(obj): Json, ) -> Result, WebError> { let classes: Vec = vec![ @@ -659,7 +689,7 @@ pub async fn service_account_post( EntryClass::Account.into(), EntryClass::Object.into(), ]; - json_rest_event_post(state, classes, obj, kopid).await + json_rest_event_post(state, classes, obj, kopid, client_auth_info).await } #[utoipa::path( @@ -675,6 +705,7 @@ pub async fn service_account_post( pub async fn service_account_id_patch( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, Json(obj): Json, ) -> Result, WebError> { @@ -683,7 +714,7 @@ pub async fn service_account_id_patch( let filter = Filter::join_parts_and(filter, filter_all!(f_id(id.as_str()))); state .qe_w_ref - .handle_internalpatch(kopid.uat, filter, obj, kopid.eventid) + .handle_internalpatch(client_auth_info, filter, obj, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -703,9 +734,10 @@ pub async fn service_account_id_get( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::ServiceAccount.into())); - json_rest_event_get_id(state, id, filter, None, kopid).await + json_rest_event_get_id(state, id, filter, None, kopid, client_auth_info).await } #[utoipa::path( @@ -721,9 +753,10 @@ pub async fn service_account_id_delete( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::ServiceAccount.into())); - json_rest_event_delete_id(state, id, filter, kopid).await + json_rest_event_delete_id(state, id, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -740,10 +773,11 @@ pub async fn service_account_credential_generate( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { state .qe_w_ref - .handle_service_account_credential_generate(kopid.uat, id, kopid.eventid) + .handle_service_account_credential_generate(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -767,11 +801,12 @@ pub async fn service_account_credential_generate( pub async fn service_account_into_person( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result, WebError> { state .qe_w_ref - .handle_service_account_into_person(kopid.uat, id, kopid.eventid) + .handle_service_account_into_person(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -790,11 +825,12 @@ pub async fn service_account_into_person( pub async fn service_account_api_token_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result>, WebError> { state .qe_r_ref - .handle_service_account_api_token_get(kopid.uat, id, kopid.eventid) + .handle_service_account_api_token_get(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -814,13 +850,14 @@ pub async fn service_account_api_token_get( pub async fn service_account_api_token_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, Json(obj): Json, ) -> Result, WebError> { state .qe_w_ref .handle_service_account_api_token_generate( - kopid.uat, + client_auth_info, id, obj.label, obj.expiry, @@ -845,10 +882,11 @@ pub async fn service_account_api_token_delete( State(state): State, Path((id, token_id)): Path<(String, Uuid)>, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { state .qe_w_ref - .handle_service_account_api_token_destroy(kopid.uat, id, token_id, kopid.eventid) + .handle_service_account_api_token_destroy(client_auth_info, id, token_id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -869,9 +907,10 @@ pub async fn person_id_get_attr( State(state): State, Path((id, attr)): Path<(String, String)>, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Account.into())); - json_rest_event_get_attr(state, id.as_str(), attr, filter, kopid).await + json_rest_event_get_attr(state, id.as_str(), attr, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -888,9 +927,10 @@ pub async fn service_account_id_get_attr( State(state): State, Path((id, attr)): Path<(String, String)>, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Account.into())); - json_rest_event_get_attr(state, id.as_str(), attr, filter, kopid).await + json_rest_event_get_attr(state, id.as_str(), attr, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -907,10 +947,11 @@ pub async fn person_id_post_attr( State(state): State, Path((id, attr)): Path<(String, String)>, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(values): Json>, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Account.into())); - json_rest_event_post_id_attr(state, id, attr, filter, values, kopid).await + json_rest_event_post_id_attr(state, id, attr, filter, values, kopid, client_auth_info).await } #[utoipa::path( @@ -927,10 +968,11 @@ pub async fn service_account_id_post_attr( State(state): State, Path((id, attr)): Path<(String, String)>, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(values): Json>, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Account.into())); - json_rest_event_post_id_attr(state, id, attr, filter, values, kopid).await + json_rest_event_post_id_attr(state, id, attr, filter, values, kopid, client_auth_info).await } #[utoipa::path( @@ -946,9 +988,10 @@ pub async fn person_id_delete_attr( State(state): State, Path((id, attr)): Path<(String, String)>, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Account.into())); - json_rest_event_delete_id_attr(state, id, attr, filter, None, kopid).await + json_rest_event_delete_id_attr(state, id, attr, filter, None, kopid, client_auth_info).await } #[utoipa::path( @@ -964,9 +1007,10 @@ pub async fn service_account_id_delete_attr( State(state): State, Path((id, attr)): Path<(String, String)>, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Account.into())); - json_rest_event_delete_id_attr(state, id, attr, filter, None, kopid).await + json_rest_event_delete_id_attr(state, id, attr, filter, None, kopid, client_auth_info).await } #[utoipa::path( @@ -982,10 +1026,11 @@ pub async fn person_id_put_attr( State(state): State, Path((id, attr)): Path<(String, String)>, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(values): Json>, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Account.into())); - json_rest_event_put_attr(state, id, attr, filter, values, kopid).await + json_rest_event_put_attr(state, id, attr, filter, values, kopid, client_auth_info).await } #[utoipa::path( @@ -1002,10 +1047,11 @@ pub async fn service_account_id_put_attr( State(state): State, Path((id, attr)): Path<(String, String)>, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(values): Json>, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Account.into())); - json_rest_event_put_attr(state, id, attr, filter, values, kopid).await + json_rest_event_put_attr(state, id, attr, filter, values, kopid, client_auth_info).await } #[utoipa::path( @@ -1021,6 +1067,7 @@ pub async fn service_account_id_put_attr( pub async fn person_id_patch( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, Json(obj): Json, ) -> Result, WebError> { @@ -1029,7 +1076,7 @@ pub async fn person_id_patch( let filter = Filter::join_parts_and(filter, filter_all!(f_id(id.as_str()))); state .qe_w_ref - .handle_internalpatch(kopid.uat, filter, obj, kopid.eventid) + .handle_internalpatch(client_auth_info, filter, obj, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1048,11 +1095,12 @@ pub async fn person_id_patch( pub async fn person_id_credential_update_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result, WebError> { state .qe_w_ref - .handle_idmcredentialupdate(kopid.uat, id, kopid.eventid) + .handle_idmcredentialupdate(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1076,13 +1124,14 @@ pub async fn person_id_credential_update_get( pub async fn person_id_credential_update_intent_ttl_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, Query(ttl): Query, ) -> Result, WebError> { state .qe_w_ref .handle_idmcredentialupdateintent( - kopid.uat, + client_auth_info, id, Some(Duration::from_secs(ttl)), kopid.eventid, @@ -1106,11 +1155,12 @@ pub async fn person_id_credential_update_intent_ttl_get( pub async fn person_id_credential_update_intent_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result, WebError> { state .qe_w_ref - .handle_idmcredentialupdateintent(kopid.uat, id, None, kopid.eventid) + .handle_idmcredentialupdateintent(client_auth_info, id, None, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1130,10 +1180,11 @@ pub async fn account_id_user_auth_token_get( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { state .qe_r_ref - .handle_account_user_auth_token_get(kopid.uat, id, kopid.eventid) + .handle_account_user_auth_token_get(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1152,10 +1203,11 @@ pub async fn account_user_auth_token_delete( State(state): State, Path((id, token_id)): Path<(String, Uuid)>, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { state .qe_w_ref - .handle_account_user_auth_token_destroy(kopid.uat, id, token_id, kopid.eventid) + .handle_account_user_auth_token_destroy(client_auth_info, id, token_id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1309,11 +1361,12 @@ pub async fn credential_update_cancel( pub async fn service_account_id_credential_status_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result, WebError> { match state .qe_r_ref - .handle_idmcredentialstatus(kopid.uat, id.clone(), kopid.eventid) + .handle_idmcredentialstatus(client_auth_info, id.clone(), kopid.eventid) .await .map(Json::from) { @@ -1342,11 +1395,12 @@ pub async fn service_account_id_credential_status_get( pub async fn person_get_id_credential_status( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result, WebError> { match state .qe_r_ref - .handle_idmcredentialstatus(kopid.uat, id.clone(), kopid.eventid) + .handle_idmcredentialstatus(client_auth_info, id.clone(), kopid.eventid) .await .map(Json::from) { @@ -1375,11 +1429,12 @@ pub async fn person_get_id_credential_status( pub async fn person_id_ssh_pubkeys_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result>, WebError> { state .qe_r_ref - .handle_internalsshkeyread(kopid.uat, id, kopid.eventid) + .handle_internalsshkeyread(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1399,11 +1454,12 @@ pub async fn person_id_ssh_pubkeys_get( pub async fn account_id_ssh_pubkeys_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result>, WebError> { state .qe_r_ref - .handle_internalsshkeyread(kopid.uat, id, kopid.eventid) + .handle_internalsshkeyread(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1422,11 +1478,12 @@ pub async fn account_id_ssh_pubkeys_get( pub async fn service_account_id_ssh_pubkeys_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result>, WebError> { state .qe_r_ref - .handle_internalsshkeyread(kopid.uat, id, kopid.eventid) + .handle_internalsshkeyread(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1444,6 +1501,7 @@ pub async fn service_account_id_ssh_pubkeys_get( pub async fn person_id_ssh_pubkeys_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, Json((tag, key)): Json<(String, String)>, ) -> Result, WebError> { @@ -1451,7 +1509,7 @@ pub async fn person_id_ssh_pubkeys_post( // Add a msg here state .qe_w_ref - .handle_sshkeycreate(kopid.uat, id, &tag, &key, filter, kopid.eventid) + .handle_sshkeycreate(client_auth_info, id, &tag, &key, filter, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1470,6 +1528,7 @@ pub async fn person_id_ssh_pubkeys_post( pub async fn service_account_id_ssh_pubkeys_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, Json((tag, key)): Json<(String, String)>, ) -> Result, WebError> { @@ -1477,7 +1536,7 @@ pub async fn service_account_id_ssh_pubkeys_post( // Add a msg here state .qe_w_ref - .handle_sshkeycreate(kopid.uat, id, &tag, &key, filter, kopid.eventid) + .handle_sshkeycreate(client_auth_info, id, &tag, &key, filter, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1496,11 +1555,12 @@ pub async fn service_account_id_ssh_pubkeys_post( pub async fn person_id_ssh_pubkeys_tag_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path((id, tag)): Path<(String, String)>, ) -> Result>, WebError> { state .qe_r_ref - .handle_internalsshkeytagread(kopid.uat, id, tag, kopid.eventid) + .handle_internalsshkeytagread(client_auth_info, id, tag, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1518,11 +1578,12 @@ pub async fn person_id_ssh_pubkeys_tag_get( pub async fn account_id_ssh_pubkeys_tag_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path((id, tag)): Path<(String, String)>, ) -> Result>, WebError> { state .qe_r_ref - .handle_internalsshkeytagread(kopid.uat, id, tag, kopid.eventid) + .handle_internalsshkeytagread(client_auth_info, id, tag, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1541,11 +1602,12 @@ pub async fn account_id_ssh_pubkeys_tag_get( pub async fn service_account_id_ssh_pubkeys_tag_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path((id, tag)): Path<(String, String)>, ) -> Result>, WebError> { state .qe_r_ref - .handle_internalsshkeytagread(kopid.uat, id, tag, kopid.eventid) + .handle_internalsshkeytagread(client_auth_info, id, tag, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1566,6 +1628,7 @@ pub async fn service_account_id_ssh_pubkeys_tag_get( pub async fn person_id_ssh_pubkeys_tag_delete( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path((id, tag)): Path<(String, String)>, ) -> Result, WebError> { let values = vec![tag]; @@ -1573,7 +1636,7 @@ pub async fn person_id_ssh_pubkeys_tag_delete( state .qe_w_ref .handle_removeattributevalues( - kopid.uat, + client_auth_info, id, Attribute::SshPublicKey.to_string(), values, @@ -1600,6 +1663,7 @@ pub async fn person_id_ssh_pubkeys_tag_delete( pub async fn service_account_id_ssh_pubkeys_tag_delete( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path((id, tag)): Path<(String, String)>, ) -> Result, WebError> { let values = vec![tag]; @@ -1607,7 +1671,7 @@ pub async fn service_account_id_ssh_pubkeys_tag_delete( state .qe_w_ref .handle_removeattributevalues( - kopid.uat, + client_auth_info, id, Attribute::SshPublicKey.to_string(), values, @@ -1633,12 +1697,13 @@ pub async fn service_account_id_ssh_pubkeys_tag_delete( pub async fn person_id_radius_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result>, WebError> { // TODO: string state .qe_r_ref - .handle_internalradiusread(kopid.uat, id, kopid.eventid) + .handle_internalradiusread(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1658,12 +1723,13 @@ pub async fn person_id_radius_get( pub async fn person_id_radius_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result, WebError> { // Need to to send the regen msg state .qe_w_ref - .handle_regenerateradius(kopid.uat, id, kopid.eventid) + .handle_regenerateradius(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1682,10 +1748,11 @@ pub async fn person_id_radius_delete( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { let attr = "radius_secret".to_string(); let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Account.into())); - json_rest_event_delete_id_attr(state, id, attr, filter, None, kopid).await + json_rest_event_delete_id_attr(state, id, attr, filter, None, kopid, client_auth_info).await } // /v1/person/:id/_radius/_token @@ -1703,8 +1770,9 @@ pub async fn person_id_radius_token_get( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { - person_id_radius_handler(state, id, kopid).await + person_id_radius_handler(state, id, kopid, client_auth_info).await } // /v1/account/:id/_radius/_token @@ -1722,8 +1790,9 @@ pub async fn account_id_radius_token_get( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { - person_id_radius_handler(state, id, kopid).await + person_id_radius_handler(state, id, kopid, client_auth_info).await } #[utoipa::path( @@ -1740,18 +1809,20 @@ pub async fn account_id_radius_token_post( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { - person_id_radius_handler(state, id, kopid).await + person_id_radius_handler(state, id, kopid, client_auth_info).await } async fn person_id_radius_handler( state: ServerState, id: String, kopid: KOpId, + client_auth_info: ClientAuthInfo, ) -> Result, WebError> { state .qe_r_ref - .handle_internalradiustokenread(kopid.uat, id, kopid.eventid) + .handle_internalradiustokenread(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1772,11 +1843,12 @@ pub async fn person_id_unix_post( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(obj): Json, ) -> Result, WebError> { state .qe_w_ref - .handle_idmaccountunixextend(kopid.uat, id, obj, kopid.eventid) + .handle_idmaccountunixextend(client_auth_info, id, obj, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1798,11 +1870,12 @@ pub async fn service_account_id_unix_post( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(obj): Json, ) -> Result, WebError> { state .qe_w_ref - .handle_idmaccountunixextend(kopid.uat, id, obj, kopid.eventid) + .handle_idmaccountunixextend(client_auth_info, id, obj, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1823,11 +1896,12 @@ pub async fn account_id_unix_post( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(obj): Json, ) -> Result, WebError> { state .qe_w_ref - .handle_idmaccountunixextend(kopid.uat, id, obj, kopid.eventid) + .handle_idmaccountunixextend(client_auth_info, id, obj, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1847,6 +1921,7 @@ pub async fn account_id_unix_post( pub async fn account_id_unix_token( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result, WebError> { // no point asking for an empty id @@ -1856,7 +1931,7 @@ pub async fn account_id_unix_token( let res = state .qe_r_ref - .handle_internalunixusertokenread(kopid.uat, id, kopid.eventid) + .handle_internalunixusertokenread(client_auth_info, id, kopid.eventid) .await .map(Json::from); @@ -1886,12 +1961,13 @@ pub async fn account_id_unix_token( pub async fn account_id_unix_auth_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, Json(obj): Json, ) -> Result>, WebError> { state .qe_r_ref - .handle_idmaccountunixauth(kopid.uat, id, obj.value, kopid.eventid) + .handle_idmaccountunixauth(client_auth_info, id, obj.value, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1910,12 +1986,13 @@ pub async fn account_id_unix_auth_post( pub async fn person_id_unix_credential_put( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, Json(obj): Json, ) -> Result, WebError> { state .qe_w_ref - .handle_idmaccountunixsetcred(kopid.uat, id, obj.value, kopid.eventid) + .handle_idmaccountunixsetcred(client_auth_info, id, obj.value, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -1933,13 +2010,14 @@ pub async fn person_id_unix_credential_put( pub async fn person_id_unix_credential_delete( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::PosixAccount.into())); state .qe_w_ref .handle_purgeattribute( - kopid.uat, + client_auth_info, id, "unix_password".to_string(), filter, @@ -1963,12 +2041,13 @@ pub async fn person_id_unix_credential_delete( pub async fn person_identify_user_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, Json(user_request): Json, ) -> Result, WebError> { state .qe_r_ref - .handle_user_identity_verification(kopid.uat, kopid.eventid, user_request, id) + .handle_user_identity_verification(client_auth_info, kopid.eventid, user_request, id) .await .map(Json::from) .map_err(WebError::from) @@ -1988,9 +2067,10 @@ pub async fn person_identify_user_post( pub async fn group_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Group.into())); - json_rest_event_get(state, None, filter, kopid).await + json_rest_event_get(state, None, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -2008,10 +2088,11 @@ pub async fn group_get( pub async fn group_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(obj): Json, ) -> Result, WebError> { let classes = vec!["group".to_string(), "object".to_string()]; - json_rest_event_post(state, classes, obj, kopid).await + json_rest_event_post(state, classes, obj, kopid, client_auth_info).await } #[utoipa::path( @@ -2027,10 +2108,11 @@ pub async fn group_post( pub async fn group_id_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Group.into())); - json_rest_event_get_id(state, id, filter, None, kopid).await + json_rest_event_get_id(state, id, filter, None, kopid, client_auth_info).await } #[utoipa::path( @@ -2045,10 +2127,11 @@ pub async fn group_id_get( pub async fn group_id_delete( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Group.into())); - json_rest_event_delete_id(state, id, filter, kopid).await + json_rest_event_delete_id(state, id, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -2065,9 +2148,10 @@ pub async fn group_id_attr_get( State(state): State, Path((id, attr)): Path<(String, String)>, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Group.into())); - json_rest_event_get_id_attr(state, id, attr, filter, kopid).await + json_rest_event_get_id_attr(state, id, attr, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -2084,10 +2168,11 @@ pub async fn group_id_attr_post( Path((id, attr)): Path<(String, String)>, State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(values): Json>, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Group.into())); - json_rest_event_post_id_attr(state, id, attr, filter, values, kopid).await + json_rest_event_post_id_attr(state, id, attr, filter, values, kopid, client_auth_info).await } #[utoipa::path( @@ -2104,11 +2189,12 @@ pub async fn group_id_attr_delete( Path((id, attr)): Path<(String, String)>, State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, values: Option>>, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Group.into())); let values = values.map(|v| v.0); - json_rest_event_delete_id_attr(state, id, attr, filter, values, kopid).await + json_rest_event_delete_id_attr(state, id, attr, filter, values, kopid, client_auth_info).await } #[utoipa::path( @@ -2125,10 +2211,11 @@ pub async fn group_id_attr_put( Path((id, attr)): Path<(String, String)>, State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(values): Json>, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Group.into())); - json_rest_event_put_id_attr(state, id, attr, filter, values, kopid).await + json_rest_event_put_id_attr(state, id, attr, filter, values, kopid, client_auth_info).await } #[utoipa::path( @@ -2145,11 +2232,12 @@ pub async fn group_id_unix_post( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(obj): Json, ) -> Result, WebError> { state .qe_w_ref - .handle_idmgroupunixextend(kopid.uat, id, obj, kopid.eventid) + .handle_idmgroupunixextend(client_auth_info, id, obj, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -2168,11 +2256,12 @@ pub async fn group_id_unix_post( pub async fn group_id_unix_token_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(id): Path, ) -> Result, WebError> { state .qe_r_ref - .handle_internalunixgrouptokenread(kopid.uat, id, kopid.eventid) + .handle_internalunixgrouptokenread(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -2191,9 +2280,10 @@ pub async fn group_id_unix_token_get( pub async fn domain_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))); - json_rest_event_get(state, None, filter, kopid).await + json_rest_event_get(state, None, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -2209,10 +2299,19 @@ pub async fn domain_get( pub async fn domain_attr_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(attr): Path, ) -> Result>>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::DomainInfo.into())); - json_rest_event_get_attr(state, STR_UUID_DOMAIN_INFO, attr, filter, kopid).await + json_rest_event_get_attr( + state, + STR_UUID_DOMAIN_INFO, + attr, + filter, + kopid, + client_auth_info, + ) + .await } #[utoipa::path( @@ -2228,6 +2327,7 @@ pub async fn domain_attr_get( pub async fn domain_attr_put( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(attr): Path, Json(values): Json>, ) -> Result, WebError> { @@ -2239,6 +2339,7 @@ pub async fn domain_attr_put( filter, values, kopid, + client_auth_info, ) .await } @@ -2257,6 +2358,7 @@ pub async fn domain_attr_delete( State(state): State, Path(attr): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(values): Json>>, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::DomainInfo.into())); @@ -2267,6 +2369,7 @@ pub async fn domain_attr_delete( filter, values, kopid, + client_auth_info, ) .await } @@ -2284,12 +2387,13 @@ pub async fn domain_attr_delete( pub async fn system_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = filter_all!(f_eq( Attribute::Uuid, PartialValue::Uuid(UUID_SYSTEM_CONFIG) )); - json_rest_event_get(state, None, filter, kopid).await + json_rest_event_get(state, None, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -2306,9 +2410,18 @@ pub async fn system_attr_get( State(state): State, Path(attr): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SystemConfig.into())); - json_rest_event_get_attr(state, STR_UUID_SYSTEM_CONFIG, attr, filter, kopid).await + json_rest_event_get_attr( + state, + STR_UUID_SYSTEM_CONFIG, + attr, + filter, + kopid, + client_auth_info, + ) + .await } #[utoipa::path( @@ -2325,6 +2438,7 @@ pub async fn system_attr_post( State(state): State, Path(attr): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(values): Json>, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SystemConfig.into())); @@ -2335,6 +2449,7 @@ pub async fn system_attr_post( filter, values, kopid, + client_auth_info, ) .await } @@ -2353,6 +2468,7 @@ pub async fn system_attr_delete( State(state): State, Path(attr): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(values): Json>>, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SystemConfig.into())); @@ -2363,6 +2479,7 @@ pub async fn system_attr_delete( filter, values, kopid, + client_auth_info, ) .await } @@ -2381,6 +2498,7 @@ pub async fn system_attr_put( State(state): State, Path(attr): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(values): Json>, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SystemConfig.into())); @@ -2391,6 +2509,7 @@ pub async fn system_attr_put( filter, values, kopid, + client_auth_info, ) .await } @@ -2408,12 +2527,13 @@ pub async fn system_attr_put( pub async fn recycle_bin_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = filter_all!(f_pres(Attribute::Class)); let attrs = None; state .qe_r_ref - .handle_internalsearchrecycled(kopid.uat, filter, attrs, kopid.eventid) + .handle_internalsearchrecycled(client_auth_info, filter, attrs, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -2433,13 +2553,14 @@ pub async fn recycle_bin_id_get( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = filter_all!(f_id(id.as_str())); let attrs = None; state .qe_r_ref - .handle_internalsearchrecycled(kopid.uat, filter, attrs, kopid.eventid) + .handle_internalsearchrecycled(client_auth_info, filter, attrs, kopid.eventid) .await .map(|mut r| r.pop()) .map(Json::from) @@ -2459,11 +2580,12 @@ pub async fn recycle_bin_revive_id_post( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { let filter = filter_all!(f_id(id.as_str())); state .qe_w_ref - .handle_reviverecycled(kopid.uat, filter, kopid.eventid) + .handle_reviverecycled(client_auth_info, filter, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -2483,10 +2605,11 @@ pub async fn recycle_bin_revive_id_post( pub async fn applinks_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { state .qe_r_ref - .handle_list_applinks(kopid.uat, kopid.eventid) + .handle_list_applinks(client_auth_info, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -2505,14 +2628,14 @@ pub async fn applinks_get( )] // TODO: post body stuff pub async fn reauth( State(state): State, - TrustedClientIp(ip_addr): TrustedClientIp, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Extension(kopid): Extension, Json(obj): Json, ) -> Result { // This may change in the future ... let inter = state .qe_r_ref - .handle_reauth(kopid.uat, obj, kopid.eventid, ip_addr) + .handle_reauth(client_auth_info, obj, kopid.eventid) .await; debug!("ReAuth result: {:?}", inter); auth_session_state_management(state, inter) @@ -2531,7 +2654,7 @@ pub async fn reauth( )] pub async fn auth( State(state): State, - TrustedClientIp(ip_addr): TrustedClientIp, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, headers: HeaderMap, Extension(kopid): Extension, Json(obj): Json, @@ -2547,7 +2670,7 @@ pub async fn auth( // invalid. let inter = state // This may change in the future ... .qe_r_ref - .handle_auth(maybe_sessionid, obj, kopid.eventid, ip_addr) + .handle_auth(maybe_sessionid, obj, kopid.eventid, client_auth_info) .await; debug!("Auth result: {:?}", inter); auth_session_state_management(state, inter) @@ -2655,10 +2778,11 @@ fn auth_session_state_management( pub async fn auth_valid( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { state .qe_r_ref - .handle_auth_valid(kopid.uat, kopid.eventid) + .handle_auth_valid(client_auth_info, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) diff --git a/server/core/src/https/v1_oauth2.rs b/server/core/src/https/v1_oauth2.rs index 396688d41..423b83c80 100644 --- a/server/core/src/https/v1_oauth2.rs +++ b/server/core/src/https/v1_oauth2.rs @@ -5,6 +5,7 @@ use super::oauth2::oauth2_id; use super::v1::{json_rest_event_get, json_rest_event_post}; use super::ServerState; +use crate::https::extractors::VerifiedClientInformation; use axum::extract::{Path, State}; use axum::{Extension, Json}; use kanidm_proto::internal::{ImageType, ImageValue}; @@ -27,12 +28,13 @@ use sketching::admin_error; pub(crate) async fn oauth2_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = filter_all!(f_eq( Attribute::Class, EntryClass::OAuth2ResourceServer.into() )); - json_rest_event_get(state, None, filter, kopid).await + json_rest_event_get(state, None, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -49,6 +51,7 @@ pub(crate) async fn oauth2_get( pub(crate) async fn oauth2_basic_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(obj): Json, ) -> Result, WebError> { let classes = vec![ @@ -56,7 +59,7 @@ pub(crate) async fn oauth2_basic_post( EntryClass::OAuth2ResourceServerBasic.to_string(), EntryClass::Object.to_string(), ]; - json_rest_event_post(state, classes, obj, kopid).await + json_rest_event_post(state, classes, obj, kopid, client_auth_info).await } #[utoipa::path( @@ -73,6 +76,7 @@ pub(crate) async fn oauth2_basic_post( pub(crate) async fn oauth2_public_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(obj): Json, ) -> Result, WebError> { let classes = vec![ @@ -80,7 +84,7 @@ pub(crate) async fn oauth2_public_post( EntryClass::OAuth2ResourceServerPublic.to_string(), EntryClass::Object.to_string(), ]; - json_rest_event_post(state, classes, obj, kopid).await + json_rest_event_post(state, classes, obj, kopid, client_auth_info).await } #[utoipa::path( @@ -98,11 +102,12 @@ pub(crate) async fn oauth2_id_get( State(state): State, Path(rs_name): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = oauth2_id(&rs_name); state .qe_r_ref - .handle_internalsearch(kopid.uat, filter, None, kopid.eventid) + .handle_internalsearch(client_auth_info, filter, None, kopid.eventid) .await .map(|mut r| r.pop()) .map(Json::from) @@ -124,12 +129,13 @@ pub(crate) async fn oauth2_id_get( pub(crate) async fn oauth2_id_get_basic_secret( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(rs_name): Path, ) -> Result>, WebError> { let filter = oauth2_id(&rs_name); state .qe_r_ref - .handle_oauth2_basic_secret_read(kopid.uat, filter, kopid.eventid) + .handle_oauth2_basic_secret_read(client_auth_info, filter, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -150,13 +156,14 @@ pub(crate) async fn oauth2_id_patch( State(state): State, Path(rs_name): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(obj): Json, ) -> Result, WebError> { let filter = oauth2_id(&rs_name); state .qe_w_ref - .handle_internalpatch(kopid.uat, filter, obj, kopid.eventid) + .handle_internalpatch(client_auth_info, filter, obj, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -176,13 +183,14 @@ pub(crate) async fn oauth2_id_patch( pub(crate) async fn oauth2_id_scopemap_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path((rs_name, group)): Path<(String, String)>, Json(scopes): Json>, ) -> Result, WebError> { let filter = oauth2_id(&rs_name); state .qe_w_ref - .handle_oauth2_scopemap_update(kopid.uat, group, scopes, filter, kopid.eventid) + .handle_oauth2_scopemap_update(client_auth_info, group, scopes, filter, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -201,12 +209,13 @@ pub(crate) async fn oauth2_id_scopemap_post( pub(crate) async fn oauth2_id_scopemap_delete( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path((rs_name, group)): Path<(String, String)>, ) -> Result, WebError> { let filter = oauth2_id(&rs_name); state .qe_w_ref - .handle_oauth2_scopemap_delete(kopid.uat, group, filter, kopid.eventid) + .handle_oauth2_scopemap_delete(client_auth_info, group, filter, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -225,13 +234,14 @@ pub(crate) async fn oauth2_id_scopemap_delete( pub(crate) async fn oauth2_id_sup_scopemap_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path((rs_name, group)): Path<(String, String)>, Json(scopes): Json>, ) -> Result, WebError> { let filter = oauth2_id(&rs_name); state .qe_w_ref - .handle_oauth2_sup_scopemap_update(kopid.uat, group, scopes, filter, kopid.eventid) + .handle_oauth2_sup_scopemap_update(client_auth_info, group, scopes, filter, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -250,12 +260,13 @@ pub(crate) async fn oauth2_id_sup_scopemap_post( pub(crate) async fn oauth2_id_sup_scopemap_delete( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path((rs_name, group)): Path<(String, String)>, ) -> Result, WebError> { let filter = oauth2_id(&rs_name); state .qe_w_ref - .handle_oauth2_sup_scopemap_delete(kopid.uat, group, filter, kopid.eventid) + .handle_oauth2_sup_scopemap_delete(client_auth_info, group, filter, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -275,12 +286,13 @@ pub(crate) async fn oauth2_id_sup_scopemap_delete( pub(crate) async fn oauth2_id_delete( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(rs_name): Path, ) -> Result, WebError> { let filter = oauth2_id(&rs_name); state .qe_w_ref - .handle_internaldelete(kopid.uat, filter, kopid.eventid) + .handle_internaldelete(client_auth_info, filter, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -298,12 +310,12 @@ pub(crate) async fn oauth2_id_delete( // API endpoint for deleting the image associated with an OAuth2 Resource Server. pub(crate) async fn oauth2_id_image_delete( State(state): State, - Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(rs_name): Path, ) -> Result, WebError> { state .qe_w_ref - .handle_oauth2_rs_image_delete(kopid.uat, oauth2_id(&rs_name)) + .handle_oauth2_rs_image_delete(client_auth_info, oauth2_id(&rs_name)) .await .map(Json::from) .map_err(WebError::from) @@ -324,7 +336,7 @@ pub(crate) async fn oauth2_id_image_delete( /// [VALID_IMAGE_UPLOAD_CONTENT_TYPES]. pub(crate) async fn oauth2_id_image_post( State(state): State, - Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path(rs_name): Path, mut multipart: axum::extract::Multipart, ) -> Result, WebError> { @@ -380,7 +392,7 @@ pub(crate) async fn oauth2_id_image_post( let rs_name = oauth2_id(&rs_name); state .qe_w_ref - .handle_oauth2_rs_image_update(kopid.uat, rs_name, image) + .handle_oauth2_rs_image_update(client_auth_info, rs_name, image) .await .map(Json::from) .map_err(WebError::from) diff --git a/server/core/src/https/v1_scim.rs b/server/core/src/https/v1_scim.rs index 69d08fec6..c92e61e1e 100644 --- a/server/core/src/https/v1_scim.rs +++ b/server/core/src/https/v1_scim.rs @@ -6,6 +6,7 @@ use super::v1::{ json_rest_event_put_id_attr, }; use super::ServerState; +use crate::https::extractors::VerifiedClientInformation; use axum::extract::{Path, State}; use axum::response::Html; use axum::routing::{get, post}; @@ -29,9 +30,10 @@ use kanidmd_lib::prelude::*; pub async fn sync_account_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SyncAccount.into())); - json_rest_event_get(state, None, filter, kopid).await + json_rest_event_get(state, None, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -47,10 +49,11 @@ pub async fn sync_account_get( pub async fn sync_account_post( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(obj): Json, ) -> Result, WebError> { let classes: Vec = vec![EntryClass::SyncAccount.into(), EntryClass::Object.into()]; - json_rest_event_post(state, classes, obj, kopid).await + json_rest_event_post(state, classes, obj, kopid, client_auth_info).await } #[utoipa::path( @@ -68,9 +71,10 @@ pub async fn sync_account_id_get( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SyncAccount.into())); - json_rest_event_get_id(state, id, filter, None, kopid).await + json_rest_event_get_id(state, id, filter, None, kopid, client_auth_info).await } #[utoipa::path( @@ -88,6 +92,7 @@ pub async fn sync_account_id_patch( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(obj): Json, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SyncAccount.into())); @@ -95,7 +100,7 @@ pub async fn sync_account_id_patch( state .qe_w_ref - .handle_internalpatch(kopid.uat, filter, obj, kopid.eventid) + .handle_internalpatch(client_auth_info, filter, obj, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -115,10 +120,11 @@ pub async fn sync_account_id_finalise_get( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { state .qe_w_ref - .handle_sync_account_finalise(kopid.uat, id, kopid.eventid) + .handle_sync_account_finalise(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -138,10 +144,11 @@ pub async fn sync_account_id_terminate_get( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { state .qe_w_ref - .handle_sync_account_terminate(kopid.uat, id, kopid.eventid) + .handle_sync_account_terminate(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -161,11 +168,12 @@ pub async fn sync_account_token_post( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(label): Json, ) -> Result, WebError> { state .qe_w_ref - .handle_sync_account_token_generate(kopid.uat, id, label, kopid.eventid) + .handle_sync_account_token_generate(client_auth_info, id, label, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -184,10 +192,11 @@ pub async fn sync_account_token_delete( State(state): State, Path(id): Path, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, ) -> Result, WebError> { state .qe_w_ref - .handle_sync_account_token_destroy(kopid.uat, id, kopid.eventid) + .handle_sync_account_token_destroy(client_auth_info, id, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -206,12 +215,12 @@ pub async fn sync_account_token_delete( async fn scim_sync_post( State(state): State, Extension(kopid): Extension, - AuthBearer(bearer): AuthBearer, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Json(changes): Json, ) -> Result, WebError> { state .qe_w_ref - .handle_scim_sync_apply(Some(bearer), changes, kopid.eventid) + .handle_scim_sync_apply(client_auth_info, changes, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -230,13 +239,14 @@ async fn scim_sync_post( async fn scim_sync_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, AuthBearer(bearer): AuthBearer, ) -> Result, WebError> { // Given the token, what is it's connected sync state? trace!(?bearer); state .qe_r_ref - .handle_scim_sync_status(Some(bearer), kopid.eventid) + .handle_scim_sync_status(client_auth_info, kopid.eventid) .await .map(Json::from) .map_err(WebError::from) @@ -254,10 +264,11 @@ async fn scim_sync_get( pub async fn sync_account_id_attr_get( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path((id, attr)): Path<(String, String)>, ) -> Result>>, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SyncAccount.into())); - json_rest_event_get_id_attr(state, id, attr, filter, kopid).await + json_rest_event_get_id_attr(state, id, attr, filter, kopid, client_auth_info).await } #[utoipa::path( @@ -273,11 +284,12 @@ pub async fn sync_account_id_attr_get( pub async fn sync_account_id_attr_put( State(state): State, Extension(kopid): Extension, + VerifiedClientInformation(client_auth_info): VerifiedClientInformation, Path((id, attr)): Path<(String, String)>, Json(values): Json>, ) -> Result, WebError> { let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SyncAccount.into())); - json_rest_event_put_id_attr(state, id, attr, filter, values, kopid).await + json_rest_event_put_id_attr(state, id, attr, filter, values, kopid, client_auth_info).await } /// When you want the kitchen Sink diff --git a/server/core/src/ldaps.rs b/server/core/src/ldaps.rs index 52a73c4c1..0395fd96b 100644 --- a/server/core/src/ldaps.rs +++ b/server/core/src/ldaps.rs @@ -43,7 +43,9 @@ async fn client_process_msg( client_port = %client_address.port(), "LDAP client" ); - qe_r_ref.handle_ldaprequest(eventid, protomsg, uat).await + qe_r_ref + .handle_ldaprequest(eventid, protomsg, uat, client_address.ip()) + .await } async fn client_process( diff --git a/server/daemon/run_insecure_dev_server.sh b/server/daemon/run_insecure_dev_server.sh index ed04ae2ee..38b6c57ad 100755 --- a/server/daemon/run_insecure_dev_server.sh +++ b/server/daemon/run_insecure_dev_server.sh @@ -18,6 +18,8 @@ if [ ! -d "${KANI_TMP}" ]; then mkdir -p "${KANI_TMP}" fi +mkdir -p "${KANI_TMP}"/client_ca + CONFIG_FILE=${CONFIG_FILE:="../../examples/insecure_server.toml"} if [ ! -f "${CONFIG_FILE}" ]; then diff --git a/server/lib/src/idm/audit.rs b/server/lib/src/idm/audit.rs index 09c5df5c3..1e6ff6a78 100644 --- a/server/lib/src/idm/audit.rs +++ b/server/lib/src/idm/audit.rs @@ -7,6 +7,7 @@ use time::OffsetDateTime; pub enum AuditSource { Internal, Https(IpAddr), + Ldaps(IpAddr), } impl From for AuditSource { @@ -14,6 +15,7 @@ impl From for AuditSource { match value { Source::Internal => AuditSource::Internal, Source::Https(ip) => AuditSource::Https(ip), + Source::Ldaps(ip) => AuditSource::Ldaps(ip), } } } diff --git a/server/lib/src/idm/authsession.rs b/server/lib/src/idm/authsession.rs index 69604e5f8..43f2dd438 100644 --- a/server/lib/src/idm/authsession.rs +++ b/server/lib/src/idm/authsession.rs @@ -889,7 +889,7 @@ pub(crate) struct AuthSessionData<'a> { pub(crate) issue: AuthIssueSession, pub(crate) webauthn: &'a Webauthn, pub(crate) ct: Duration, - pub(crate) source: Source, + pub(crate) client_auth_info: ClientAuthInfo, } #[derive(Clone)] @@ -1008,7 +1008,7 @@ impl AuthSession { state, issue: asd.issue, intent: AuthIntent::InitialAuth { privileged }, - source: asd.source, + source: asd.client_auth_info.source, }; // Get the set of mechanisms that can proceed. This is tied // to the session so that it can mutate state and have progression @@ -1149,7 +1149,7 @@ impl AuthSession { session_id, session_expiry, }, - source: asd.source, + source: asd.client_auth_info.source, }; let as_state = AuthState::Continue(allow); @@ -1548,7 +1548,7 @@ mod tests { issue: AuthIssueSession::Token, webauthn: &webauthn, ct: duration_from_epoch_now(), - source: Source::Internal, + client_auth_info: Source::Internal.into(), }; let (session, state) = AuthSession::new(asd, false); if let AuthState::Choose(auth_mechs) = state { @@ -1584,7 +1584,7 @@ mod tests { issue: AuthIssueSession::Token, webauthn: $webauthn, ct: duration_from_epoch_now(), - source: Source::Internal, + client_auth_info: Source::Internal.into(), }; let (session, state) = AuthSession::new(asd, $privileged); let mut session = session.unwrap(); @@ -1771,7 +1771,7 @@ mod tests { issue: AuthIssueSession::Token, webauthn: $webauthn, ct: duration_from_epoch_now(), - source: Source::Internal, + client_auth_info: Source::Internal.into(), }; let (session, state) = AuthSession::new(asd, false); let mut session = session.expect("Session was unable to be created."); @@ -2099,7 +2099,7 @@ mod tests { issue: AuthIssueSession::Token, webauthn: $webauthn, ct: duration_from_epoch_now(), - source: Source::Internal, + client_auth_info: Source::Internal.into(), }; let (session, state) = AuthSession::new(asd, false); let mut session = session.unwrap(); diff --git a/server/lib/src/idm/credupdatesession.rs b/server/lib/src/idm/credupdatesession.rs index 8242a0717..69baeb57e 100644 --- a/server/lib/src/idm/credupdatesession.rs +++ b/server/lib/src/idm/credupdatesession.rs @@ -2534,7 +2534,9 @@ mod tests { let auth_init = AuthEvent::named_init("testperson"); - let r1 = idms_auth.auth(&auth_init, ct, Source::Internal).await; + let r1 = idms_auth + .auth(&auth_init, ct, Source::Internal.into()) + .await; let ar = r1.unwrap(); let AuthResult { sessionid, state } = ar; @@ -2545,7 +2547,9 @@ mod tests { let auth_begin = AuthEvent::begin_mech(sessionid, AuthMech::Password); - let r2 = idms_auth.auth(&auth_begin, ct, Source::Internal).await; + let r2 = idms_auth + .auth(&auth_begin, ct, Source::Internal.into()) + .await; let ar = r2.unwrap(); let AuthResult { sessionid, state } = ar; @@ -2554,7 +2558,7 @@ mod tests { let pw_step = AuthEvent::cred_step_password(sessionid, pw); // Expect success - let r2 = idms_auth.auth(&pw_step, ct, Source::Internal).await; + let r2 = idms_auth.auth(&pw_step, ct, Source::Internal.into()).await; debug!("r2 ==> {:?}", r2); idms_auth.commit().expect("Must not fail"); @@ -2584,7 +2588,9 @@ mod tests { let auth_init = AuthEvent::named_init("testperson"); - let r1 = idms_auth.auth(&auth_init, ct, Source::Internal).await; + let r1 = idms_auth + .auth(&auth_init, ct, Source::Internal.into()) + .await; let ar = r1.unwrap(); let AuthResult { sessionid, state } = ar; @@ -2595,7 +2601,9 @@ mod tests { let auth_begin = AuthEvent::begin_mech(sessionid, AuthMech::PasswordMfa); - let r2 = idms_auth.auth(&auth_begin, ct, Source::Internal).await; + let r2 = idms_auth + .auth(&auth_begin, ct, Source::Internal.into()) + .await; let ar = r2.unwrap(); let AuthResult { sessionid, state } = ar; @@ -2606,7 +2614,9 @@ mod tests { .expect("Failed to perform totp step"); let totp_step = AuthEvent::cred_step_totp(sessionid, totp); - let r2 = idms_auth.auth(&totp_step, ct, Source::Internal).await; + let r2 = idms_auth + .auth(&totp_step, ct, Source::Internal.into()) + .await; let ar = r2.unwrap(); let AuthResult { sessionid, state } = ar; @@ -2615,7 +2625,7 @@ mod tests { let pw_step = AuthEvent::cred_step_password(sessionid, pw); // Expect success - let r3 = idms_auth.auth(&pw_step, ct, Source::Internal).await; + let r3 = idms_auth.auth(&pw_step, ct, Source::Internal.into()).await; debug!("r3 ==> {:?}", r3); idms_auth.commit().expect("Must not fail"); @@ -2644,7 +2654,9 @@ mod tests { let auth_init = AuthEvent::named_init("testperson"); - let r1 = idms_auth.auth(&auth_init, ct, Source::Internal).await; + let r1 = idms_auth + .auth(&auth_init, ct, Source::Internal.into()) + .await; let ar = r1.unwrap(); let AuthResult { sessionid, state } = ar; @@ -2655,14 +2667,18 @@ mod tests { let auth_begin = AuthEvent::begin_mech(sessionid, AuthMech::PasswordMfa); - let r2 = idms_auth.auth(&auth_begin, ct, Source::Internal).await; + let r2 = idms_auth + .auth(&auth_begin, ct, Source::Internal.into()) + .await; let ar = r2.unwrap(); let AuthResult { sessionid, state } = ar; assert!(matches!(state, AuthState::Continue(_))); let code_step = AuthEvent::cred_step_backup_code(sessionid, code); - let r2 = idms_auth.auth(&code_step, ct, Source::Internal).await; + let r2 = idms_auth + .auth(&code_step, ct, Source::Internal.into()) + .await; let ar = r2.unwrap(); let AuthResult { sessionid, state } = ar; @@ -2671,7 +2687,7 @@ mod tests { let pw_step = AuthEvent::cred_step_password(sessionid, pw); // Expect success - let r3 = idms_auth.auth(&pw_step, ct, Source::Internal).await; + let r3 = idms_auth.auth(&pw_step, ct, Source::Internal.into()).await; debug!("r3 ==> {:?}", r3); idms_auth.commit().expect("Must not fail"); @@ -2706,7 +2722,9 @@ mod tests { let auth_init = AuthEvent::named_init("testperson"); - let r1 = idms_auth.auth(&auth_init, ct, Source::Internal).await; + let r1 = idms_auth + .auth(&auth_init, ct, Source::Internal.into()) + .await; let ar = r1.unwrap(); let AuthResult { sessionid, state } = ar; @@ -2717,7 +2735,9 @@ mod tests { let auth_begin = AuthEvent::begin_mech(sessionid, AuthMech::Passkey); - let r2 = idms_auth.auth(&auth_begin, ct, Source::Internal).await; + let r2 = idms_auth + .auth(&auth_begin, ct, Source::Internal.into()) + .await; let ar = r2.unwrap(); let AuthResult { sessionid, state } = ar; @@ -2739,7 +2759,9 @@ mod tests { let passkey_step = AuthEvent::cred_step_passkey(sessionid, resp); - let r3 = idms_auth.auth(&passkey_step, ct, Source::Internal).await; + let r3 = idms_auth + .auth(&passkey_step, ct, Source::Internal.into()) + .await; debug!("r3 ==> {:?}", r3); idms_auth.commit().expect("Must not fail"); diff --git a/server/lib/src/idm/ldap.rs b/server/lib/src/idm/ldap.rs index 056da2a18..b5c5ddf62 100644 --- a/server/lib/src/idm/ldap.rs +++ b/server/lib/src/idm/ldap.rs @@ -8,6 +8,7 @@ use kanidm_proto::constants::*; use kanidm_proto::v1::{ApiToken, OperationError, UserAuthToken}; use ldap3_proto::simple::*; use regex::Regex; +use std::net::IpAddr; use tracing::trace; use uuid::Uuid; @@ -138,6 +139,7 @@ impl LdapServer { idms: &IdmServer, sr: &SearchRequest, uat: &LdapBoundToken, + source: Source, // eventid: &Uuid, ) -> Result, OperationError> { admin_info!("Attempt LDAP Search for {}", uat.spn); @@ -320,7 +322,7 @@ impl LdapServer { // // ! Remember, searchEvent wraps to ignore hidden for us. let ident = idm_read - .validate_ldap_session(&uat.effective_session, ct) + .validate_ldap_session(&uat.effective_session, source, ct) .map_err(|e| { admin_error!("Invalid identity: {:?}", e); e @@ -456,8 +458,11 @@ impl LdapServer { idms: &IdmServer, server_op: ServerOps, uat: Option, + ip_addr: IpAddr, eventid: Uuid, ) -> Result { + let source = Source::Ldaps(ip_addr); + match server_op { ServerOps::SimpleBind(sbr) => self .do_bind(idms, sbr.dn.as_str(), sbr.pw.as_str()) @@ -472,7 +477,7 @@ impl LdapServer { }), ServerOps::Search(sr) => match uat { Some(u) => self - .do_search(idms, &sr, &u) + .do_search(idms, &sr, &u, source) .await .map(LdapResponseState::MultiPartResponse) .or_else(|e| { @@ -494,7 +499,7 @@ impl LdapServer { } }; // If okay, do the search. - self.do_search(idms, &sr, &lbt) + self.do_search(idms, &sr, &lbt, Source::Internal) .await .map(|r| LdapResponseState::BindMultiPartResponse(lbt, r)) .or_else(|e| { @@ -895,7 +900,10 @@ mod tests { filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()), attrs: vec!["*".to_string()], }; - let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap(); + let r1 = ldaps + .do_search(idms, &sr, &anon_t, Source::Internal) + .await + .unwrap(); // The result, and the ldap proto success msg. assert!(r1.len() == 2); @@ -927,7 +935,10 @@ mod tests { filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()), attrs: vec!["+".to_string()], }; - let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap(); + let r1 = ldaps + .do_search(idms, &sr, &anon_t, Source::Internal) + .await + .unwrap(); // The result, and the ldap proto success msg. assert!(r1.len() == 2); @@ -971,7 +982,10 @@ mod tests { Attribute::UidNumber.to_string(), ], }; - let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap(); + let r1 = ldaps + .do_search(idms, &sr, &anon_t, Source::Internal) + .await + .unwrap(); // The result, and the ldap proto success msg. assert!(r1.len() == 2); @@ -1102,7 +1116,10 @@ mod tests { let anon_lbt = ldaps.do_bind(idms, "", "").await.unwrap().unwrap(); assert!(anon_lbt.effective_session == LdapSession::UnixBind(UUID_ANONYMOUS)); - let r1 = ldaps.do_search(idms, &sr, &anon_lbt).await.unwrap(); + let r1 = ldaps + .do_search(idms, &sr, &anon_lbt, Source::Internal) + .await + .unwrap(); assert!(r1.len() == 2); match &r1[0].op { LdapOp::SearchResultEntry(lsre) => { @@ -1141,7 +1158,10 @@ mod tests { assert!(sa_lbt.effective_session == LdapSession::ApiToken(apitoken_inner)); // Search and retrieve mail that's now accessible. - let r1 = ldaps.do_search(idms, &sr, &sa_lbt).await.unwrap(); + let r1 = ldaps + .do_search(idms, &sr, &sa_lbt, Source::Internal) + .await + .unwrap(); assert!(r1.len() == 2); match &r1[0].op { LdapOp::SearchResultEntry(lsre) => { @@ -1216,7 +1236,10 @@ mod tests { Attribute::EntryUuid.to_string(), ], }; - let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap(); + let r1 = ldaps + .do_search(idms, &sr, &anon_t, Source::Internal) + .await + .unwrap(); // The result, and the ldap proto success msg. assert!(r1.len() == 2); @@ -1252,7 +1275,10 @@ mod tests { filter: LdapFilter::Present(Attribute::ObjectClass.to_string()), attrs: vec!["*".to_string()], }; - let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap(); + let r1 = ldaps + .do_search(idms, &sr, &anon_t, Source::Internal) + .await + .unwrap(); trace!(?r1); @@ -1302,7 +1328,10 @@ mod tests { filter: LdapFilter::Present(Attribute::ObjectClass.to_string()), attrs: vec!["*".to_string()], }; - let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap(); + let r1 = ldaps + .do_search(idms, &sr, &anon_t, Source::Internal) + .await + .unwrap(); trace!(?r1); @@ -1378,7 +1407,10 @@ mod tests { Attribute::EntryUuid.to_string(), ], }; - let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap(); + let r1 = ldaps + .do_search(idms, &sr, &anon_t, Source::Internal) + .await + .unwrap(); // Empty results and ldap proto success msg. assert!(r1.len() == 1); @@ -1399,7 +1431,10 @@ mod tests { "entryuuid".to_string(), ], }; - let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap(); + let r1 = ldaps + .do_search(idms, &sr, &anon_t, Source::Internal) + .await + .unwrap(); trace!(?r1); diff --git a/server/lib/src/idm/mod.rs b/server/lib/src/idm/mod.rs index 058e1effe..f16734e5f 100644 --- a/server/lib/src/idm/mod.rs +++ b/server/lib/src/idm/mod.rs @@ -22,9 +22,9 @@ pub mod server; pub mod serviceaccount; pub(crate) mod unix; -use std::fmt; - +use crate::server::identity::Source; use kanidm_proto::v1::{AuthAllowed, AuthIssueSession, AuthMech}; +use std::fmt; pub enum AuthState { Choose(Vec), @@ -43,3 +43,49 @@ impl fmt::Debug for AuthState { } } } + +#[derive(Debug, Clone)] +pub struct ClientAuthInfo { + pub source: Source, + pub client_cert: Option, + pub bearer_token: Option, +} + +#[derive(Debug, Clone)] +pub struct ClientCertInfo { + pub subject_key_id: Option>, + pub cn: Option, +} + +#[cfg(test)] +impl From for ClientAuthInfo { + fn from(value: Source) -> ClientAuthInfo { + ClientAuthInfo { + source: value, + client_cert: None, + bearer_token: None, + } + } +} + +#[cfg(test)] +impl From<&str> for ClientAuthInfo { + fn from(value: &str) -> ClientAuthInfo { + ClientAuthInfo { + source: Source::Internal, + client_cert: None, + bearer_token: Some(value.to_string()), + } + } +} + +#[cfg(test)] +impl From for ClientAuthInfo { + fn from(value: ClientCertInfo) -> ClientAuthInfo { + ClientAuthInfo { + source: Source::Internal, + client_cert: Some(value), + bearer_token: None, + } + } +} diff --git a/server/lib/src/idm/oauth2.rs b/server/lib/src/idm/oauth2.rs index 7483f86ae..19a1d423a 100644 --- a/server/lib/src/idm/oauth2.rs +++ b/server/lib/src/idm/oauth2.rs @@ -29,7 +29,6 @@ use kanidm_proto::oauth2::{ ClaimType, DisplayValue, GrantType, IdTokenSignAlg, ResponseMode, ResponseType, SubjectType, TokenEndpointAuthMethod, }; -use kanidm_proto::v1::UserAuthToken; use openssl::sha; use serde::{Deserialize, Serialize}; use time::OffsetDateTime; @@ -112,7 +111,10 @@ struct ConsentToken { struct TokenExchangeCode { // We don't need the client_id here, because it's signed with an RS specific // key which gives us the assurance that it's the correct combination. - pub uat: UserAuthToken, + // pub uat: UserAuthToken, + pub account_uuid: Uuid, + pub session_id: Uuid, + // The S256 code challenge. pub code_challenge: Option, // The original redirect uri @@ -683,10 +685,14 @@ impl<'a> IdmServerProxyWriteTransaction<'a> { pub fn check_oauth2_authorise_permit( &mut self, ident: &Identity, - uat: &UserAuthToken, consent_token: &str, ct: Duration, ) -> Result { + let Some(account_uuid) = ident.get_uuid() else { + error!("consent request ident does not have a valid uuid, unable to proceed"); + return Err(OperationError::InvalidSessionState); + }; + // Decode the consent req with our system fernet key. Use a ttl of 5 minutes. let consent_req: ConsentToken = self .oauth2rs @@ -711,7 +717,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> { } // Validate that the session id matches our uat. - if consent_req.session_id != uat.session_id { + if consent_req.session_id != ident.get_session_id() { security_info!("consent request session id does not match the session id of our UAT."); return Err(OperationError::InvalidSessionState); } @@ -729,7 +735,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> { // Extract the state, code challenge, redirect_uri let xchg_code = TokenExchangeCode { - uat: uat.clone(), + // uat: uat.clone(), + account_uuid, + session_id: ident.get_session_id(), code_challenge: consent_req.code_challenge, redirect_uri: consent_req.redirect_uri.clone(), scopes: consent_req.scopes.clone(), @@ -760,7 +768,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> { ]); self.qs_write.internal_modify( - &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(uat.uuid))), + &filter_all!(f_eq(Attribute::Uuid, PartialValue::Uuid(account_uuid))), &modlist, )?; @@ -840,7 +848,11 @@ impl<'a> IdmServerProxyWriteTransaction<'a> { return Err(Oauth2Error::InvalidOrigin); } + /* // Check that the UAT we are issuing for still is valid. + // + // Not sure this is actually needed. To create the token exchange code you need to have + // a valid, non-expired session, so why do we double check this here? let odt_ct = OffsetDateTime::UNIX_EPOCH + ct; if let Some(expiry) = code_xchg.uat.expiry { if expiry <= odt_ct { @@ -850,14 +862,15 @@ impl<'a> IdmServerProxyWriteTransaction<'a> { return Err(Oauth2Error::AccessDenied); } } + */ // ==== We are now GOOD TO GO! ==== // Grant the access token response. - let parent_session_id = code_xchg.uat.session_id; + let parent_session_id = code_xchg.session_id; let session_id = Uuid::new_v4(); let scopes = code_xchg.scopes; - let account_uuid = code_xchg.uat.uuid; + let account_uuid = code_xchg.account_uuid; let nonce = code_xchg.nonce; self.generate_access_token_response( @@ -1239,7 +1252,6 @@ impl<'a> IdmServerProxyReadTransaction<'a> { pub fn check_oauth2_authorisation( &self, ident: &Identity, - uat: &UserAuthToken, auth_req: &AuthorisationRequest, ct: Duration, ) -> Result { @@ -1340,8 +1352,13 @@ impl<'a> IdmServerProxyReadTransaction<'a> { // NOTE: login_hint is handled in the UI code, not here. + let Some(account_uuid) = ident.get_uuid() else { + error!("consent request ident does not have a valid uuid, unable to proceed"); + return Err(Oauth2Error::InvalidRequest); + }; + // Deny anonymous access to oauth2 - if uat.uuid == UUID_ANONYMOUS { + if account_uuid == UUID_ANONYMOUS { admin_error!( "Invalid OAuth2 request - refusing to allow user that authenticated with anonymous" ); @@ -1451,6 +1468,8 @@ impl<'a> IdmServerProxyReadTransaction<'a> { false }; + let session_id = ident.get_session_id(); + if consent_previously_granted { let pretty_scopes: Vec = granted_scopes.iter().map(|s| s.to_owned()).collect(); admin_info!( @@ -1460,7 +1479,8 @@ impl<'a> IdmServerProxyReadTransaction<'a> { // Setup for the permit success let xchg_code = TokenExchangeCode { - uat: uat.clone(), + account_uuid, + session_id, code_challenge, redirect_uri: auth_req.redirect_uri.clone(), scopes: granted_scopes.into_iter().collect(), @@ -1511,7 +1531,7 @@ impl<'a> IdmServerProxyReadTransaction<'a> { let consent_req = ConsentToken { client_id: auth_req.client_id.clone(), ident_id: ident.get_event_origin_id(), - session_id: uat.session_id, + session_id, state: auth_req.state.clone(), code_challenge, redirect_uri: auth_req.redirect_uri.clone(), @@ -1543,7 +1563,6 @@ impl<'a> IdmServerProxyReadTransaction<'a> { pub fn check_oauth2_authorise_reject( &self, ident: &Identity, - uat: &UserAuthToken, consent_token: &str, ct: Duration, ) -> Result { @@ -1570,8 +1589,8 @@ impl<'a> IdmServerProxyReadTransaction<'a> { return Err(OperationError::InvalidSessionState); } - // Validate that the session id matches our uat. - if consent_req.session_id != uat.session_id { + // Validate that the session id matches our session + if consent_req.session_id != ident.get_session_id() { security_info!("consent request sessien id does not match the session id of our UAT."); return Err(OperationError::InvalidSessionState); } @@ -2047,7 +2066,6 @@ mod tests { ( $idms_prox_read:expr, $ident:expr, - $uat:expr, $ct:expr, $code_challenge:expr, $scope:expr @@ -2068,7 +2086,7 @@ mod tests { }; $idms_prox_read - .check_oauth2_authorisation($ident, $uat, &auth_req, $ct) + .check_oauth2_authorisation($ident, &auth_req, $ct) .expect("OAuth2 authorisation failed") }}; } @@ -2215,7 +2233,7 @@ mod tests { .expect("Failed to modify user"); let ident = idms_prox_write - .process_uat_to_identity(&uat, ct) + .process_uat_to_identity(&uat, ct, Source::Internal) .expect("Unable to process uat"); idms_prox_write.commit().expect("failed to commit"); @@ -2337,7 +2355,7 @@ mod tests { .expect("Failed to modify user"); let ident = idms_prox_write - .process_uat_to_identity(&uat, ct) + .process_uat_to_identity(&uat, ct, Source::Internal) .expect("Unable to process uat"); idms_prox_write.commit().expect("failed to commit"); @@ -2360,7 +2378,7 @@ mod tests { ) .expect("Unable to create uat"); let ident = idms_prox_write - .process_uat_to_identity(&uat, ct) + .process_uat_to_identity(&uat, ct, Source::Internal) .expect("Unable to process uat"); idms_prox_write.commit().expect("failed to commit"); @@ -2374,7 +2392,7 @@ mod tests { _idms_delayed: &mut IdmServerDelayed, ) { let ct = Duration::from_secs(TEST_CURRENT_TIME); - let (secret, uat, ident, _) = + let (secret, _uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, true, false, false).await; let idms_prox_read = idms.proxy_read().await; @@ -2387,7 +2405,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -2406,7 +2423,7 @@ mod tests { let mut idms_prox_write = idms.proxy_write(ct).await; let permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); // Check we are reflecting the CSRF properly. @@ -2441,7 +2458,7 @@ mod tests { _idms_delayed: &mut IdmServerDelayed, ) { let ct = Duration::from_secs(TEST_CURRENT_TIME); - let (uat, ident, _) = setup_oauth2_resource_server_public(idms, ct).await; + let (_uat, ident, _) = setup_oauth2_resource_server_public(idms, ct).await; let idms_prox_read = idms.proxy_read().await; @@ -2453,7 +2470,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -2472,7 +2488,7 @@ mod tests { let mut idms_prox_write = idms.proxy_write(ct).await; let permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); // Check we are reflecting the CSRF properly. @@ -2508,11 +2524,11 @@ mod tests { ) { // Test invalid OAuth2 authorisation states/requests. let ct = Duration::from_secs(TEST_CURRENT_TIME); - let (_secret, uat, ident, _) = + let (_secret, _uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, true, false, false).await; - let (anon_uat, anon_ident) = setup_idm_admin(idms, ct).await; - let (idm_admin_uat, idm_admin_ident) = setup_idm_admin(idms, ct).await; + let (_anon_uat, anon_ident) = setup_idm_admin(idms, ct).await; + let (_idm_admin_uat, idm_admin_ident) = setup_idm_admin(idms, ct).await; // Need a uat from a user not in the group. Probs anonymous. let idms_prox_read = idms.proxy_read().await; @@ -2539,7 +2555,7 @@ mod tests { assert!( idms_prox_read - .check_oauth2_authorisation(&ident, &uat, &auth_req, ct) + .check_oauth2_authorisation(&ident, &auth_req, ct) .unwrap_err() == Oauth2Error::UnsupportedResponseType ); @@ -2559,7 +2575,7 @@ mod tests { assert!( idms_prox_read - .check_oauth2_authorisation(&ident, &uat, &auth_req, ct) + .check_oauth2_authorisation(&ident, &auth_req, ct) .unwrap_err() == Oauth2Error::InvalidRequest ); @@ -2579,7 +2595,7 @@ mod tests { assert!( idms_prox_read - .check_oauth2_authorisation(&ident, &uat, &auth_req, ct) + .check_oauth2_authorisation(&ident, &auth_req, ct) .unwrap_err() == Oauth2Error::InvalidClientId ); @@ -2599,7 +2615,7 @@ mod tests { assert!( idms_prox_read - .check_oauth2_authorisation(&ident, &uat, &auth_req, ct) + .check_oauth2_authorisation(&ident, &auth_req, ct) .unwrap_err() == Oauth2Error::InvalidOrigin ); @@ -2619,7 +2635,7 @@ mod tests { assert!( idms_prox_read - .check_oauth2_authorisation(&ident, &uat, &auth_req, ct) + .check_oauth2_authorisation(&ident, &auth_req, ct) .unwrap_err() == Oauth2Error::AccessDenied ); @@ -2639,7 +2655,7 @@ mod tests { assert!( idms_prox_read - .check_oauth2_authorisation(&idm_admin_ident, &idm_admin_uat, &auth_req, ct) + .check_oauth2_authorisation(&idm_admin_ident, &auth_req, ct) .unwrap_err() == Oauth2Error::AccessDenied ); @@ -2659,7 +2675,7 @@ mod tests { assert!( idms_prox_read - .check_oauth2_authorisation(&anon_ident, &anon_uat, &auth_req, ct) + .check_oauth2_authorisation(&anon_ident, &auth_req, ct) .unwrap_err() == Oauth2Error::AccessDenied ); @@ -2675,25 +2691,33 @@ mod tests { let (_secret, uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, true, false, false).await; - let (uat2, ident2) = { - let mut idms_prox_write = idms.proxy_write(ct).await; - let account = idms_prox_write - .target_to_account(UUID_IDM_ADMIN) - .expect("account must exist"); - let session_id = uuid::Uuid::new_v4(); - let uat2 = account - .to_userauthtoken( - session_id, - SessionScope::ReadWrite, - ct, - DEFAULT_AUTH_SESSION_EXPIRY, - ) - .expect("Unable to create uat"); - let ident2 = idms_prox_write - .process_uat_to_identity(&uat2, ct) - .expect("Unable to process uat"); - (uat2, ident2) - }; + let mut idms_prox_write = idms.proxy_write(ct).await; + + let mut uat_wrong_session_id = uat.clone(); + uat_wrong_session_id.session_id = uuid::Uuid::new_v4(); + let ident_wrong_session_id = idms_prox_write + .process_uat_to_identity(&uat_wrong_session_id, ct, Source::Internal) + .expect("Unable to process uat"); + + let account = idms_prox_write + .target_to_account(UUID_IDM_ADMIN) + .expect("account must exist"); + let session_id = uuid::Uuid::new_v4(); + let uat2 = account + .to_userauthtoken( + session_id, + SessionScope::ReadWrite, + ct, + DEFAULT_AUTH_SESSION_EXPIRY, + ) + .expect("Unable to create uat"); + let ident2 = idms_prox_write + .process_uat_to_identity(&uat2, ct, Source::Internal) + .expect("Unable to process uat"); + + assert!(idms_prox_write.commit().is_ok()); + + // Now start the test let idms_prox_read = idms.proxy_read().await; @@ -2702,7 +2726,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -2724,7 +2747,6 @@ mod tests { idms_prox_write .check_oauth2_authorise_permit( &ident, - &uat, &consent_token, ct + Duration::from_secs(TOKEN_EXPIRE), ) @@ -2738,7 +2760,7 @@ mod tests { assert!( idms_prox_write - .check_oauth2_authorise_permit(&ident2, &uat, &consent_token, ct,) + .check_oauth2_authorise_permit(&ident2, &consent_token, ct,) .unwrap_err() == OperationError::InvalidSessionState ); @@ -2746,7 +2768,7 @@ mod tests { // * incorrect session id assert!( idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat2, &consent_token, ct,) + .check_oauth2_authorise_permit(&ident_wrong_session_id, &consent_token, ct,) .unwrap_err() == OperationError::InvalidSessionState ); @@ -2784,7 +2806,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -2802,7 +2823,7 @@ mod tests { // == Manually submit the consent token to the permit for the permit_success let permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); // == Submit the token exchange code. @@ -2867,19 +2888,6 @@ mod tests { == Oauth2Error::InvalidRequest ); - // * Uat has expired! - // NOTE: This is setup EARLY in the test, by manipulation of the UAT expiry. - assert!( - idms_prox_write - .check_oauth2_token_exchange( - client_authz.as_deref(), - &token_req, - ct + Duration::from_secs(UAT_EXPIRE) - ) - .unwrap_err() - == Oauth2Error::AccessDenied - ); - /* // * incorrect grant_type // No longer possible due to changes in json api @@ -2936,7 +2944,7 @@ mod tests { _idms_delayed: &mut IdmServerDelayed, ) { let ct = Duration::from_secs(TEST_CURRENT_TIME); - let (secret, uat, ident, _) = + let (secret, _uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, true, false, false).await; let client_authz = Some(general_purpose::STANDARD.encode(format!("test_resource_server:{secret}"))); @@ -2948,7 +2956,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -2966,7 +2973,7 @@ mod tests { let mut idms_prox_write = idms.proxy_write(ct).await; let permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); let token_req: AccessTokenRequest = GrantTypeReq::AuthorizationCode { @@ -3032,7 +3039,7 @@ mod tests { async fn test_idm_oauth2_token_revoke(idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed) { // First, setup to get a token. let ct = Duration::from_secs(TEST_CURRENT_TIME); - let (secret, uat, ident, _) = + let (secret, _uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, true, false, false).await; let client_authz = Some(general_purpose::STANDARD.encode(format!("test_resource_server:{secret}"))); @@ -3044,7 +3051,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -3062,7 +3068,7 @@ mod tests { let mut idms_prox_write = idms.proxy_write(ct).await; let permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); // Assert that the consent was submitted @@ -3149,7 +3155,10 @@ mod tests { // Force trim the session and wait for the grace window to pass. The token will be invalidated let mut idms_prox_write = idms.proxy_write(ct).await; - let filt = filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(uat.uuid))); + let filt = filter!(f_eq( + Attribute::Uuid, + PartialValue::Uuid(ident.get_uuid().unwrap()) + )); let mut work_set = idms_prox_write .qs_write .internal_search_writeable(&filt) @@ -3199,7 +3208,7 @@ mod tests { ) { // First, setup to get a token. let ct = Duration::from_secs(TEST_CURRENT_TIME); - let (secret, uat, ident, _) = + let (secret, _uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, true, false, false).await; let client_authz = Some(general_purpose::STANDARD.encode(format!("test_resource_server:{secret}"))); @@ -3211,7 +3220,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -3229,7 +3237,7 @@ mod tests { let mut idms_prox_write = idms.proxy_write(ct).await; let permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); let token_req: AccessTokenRequest = GrantTypeReq::AuthorizationCode { @@ -3300,10 +3308,10 @@ mod tests { _idms_delayed: &mut IdmServerDelayed, ) { let ct = Duration::from_secs(TEST_CURRENT_TIME); - let (_secret, uat, ident, _) = + let (_secret, _uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, true, false, false).await; - let (uat2, ident2) = { + let ident2 = { let mut idms_prox_write = idms.proxy_write(ct).await; let account = idms_prox_write .target_to_account(UUID_IDM_ADMIN) @@ -3318,9 +3326,9 @@ mod tests { ) .expect("Unable to create uat"); let ident2 = idms_prox_write - .process_uat_to_identity(&uat2, ct) + .process_uat_to_identity(&uat2, ct, Source::Internal) .expect("Unable to process uat"); - (uat2, ident2) + ident2 }; let idms_prox_read = idms.proxy_read().await; @@ -3331,7 +3339,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -3345,7 +3352,7 @@ mod tests { }; let reject_success = idms_prox_read - .check_oauth2_authorise_reject(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_reject(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 reject"); assert!(reject_success == redirect_uri); @@ -3354,7 +3361,7 @@ mod tests { let past_ct = Duration::from_secs(TEST_CURRENT_TIME + 301); assert!( idms_prox_read - .check_oauth2_authorise_reject(&ident, &uat, &consent_token, past_ct) + .check_oauth2_authorise_reject(&ident, &consent_token, past_ct) .unwrap_err() == OperationError::CryptographyError ); @@ -3362,22 +3369,15 @@ mod tests { // Invalid consent token assert!( idms_prox_read - .check_oauth2_authorise_reject(&ident, &uat, "not a token", ct) + .check_oauth2_authorise_reject(&ident, "not a token", ct) .unwrap_err() == OperationError::CryptographyError ); - // Wrong UAT - assert!( - idms_prox_read - .check_oauth2_authorise_reject(&ident, &uat2, &consent_token, ct) - .unwrap_err() - == OperationError::InvalidSessionState - ); // Wrong ident assert!( idms_prox_read - .check_oauth2_authorise_reject(&ident2, &uat, &consent_token, ct) + .check_oauth2_authorise_reject(&ident2, &consent_token, ct) .unwrap_err() == OperationError::InvalidSessionState ); @@ -3529,7 +3529,7 @@ mod tests { _idms_delayed: &mut IdmServerDelayed, ) { let ct = Duration::from_secs(TEST_CURRENT_TIME); - let (secret, uat, ident, _) = + let (secret, _uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, true, false, false).await; let client_authz = Some(general_purpose::STANDARD.encode(format!("test_resource_server:{secret}"))); @@ -3541,7 +3541,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -3559,7 +3558,7 @@ mod tests { let mut idms_prox_write = idms.proxy_write(ct).await; let permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); // == Submit the token exchange code. @@ -3712,7 +3711,7 @@ mod tests { // we run the same test as test_idm_oauth2_openid_extensions() // but change the preferred_username setting on the RS let ct = Duration::from_secs(TEST_CURRENT_TIME); - let (secret, uat, ident, _) = + let (secret, _uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, true, false, true).await; let client_authz = Some(general_purpose::STANDARD.encode(format!("test_resource_server:{secret}"))); @@ -3724,7 +3723,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -3742,7 +3740,7 @@ mod tests { let mut idms_prox_write = idms.proxy_write(ct).await; let permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); // == Submit the token exchange code. @@ -3801,7 +3799,7 @@ mod tests { // we run the same test as test_idm_oauth2_openid_extensions() // but change the preferred_username setting on the RS let ct = Duration::from_secs(TEST_CURRENT_TIME); - let (secret, uat, ident, _) = + let (secret, _uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, true, false, true).await; let client_authz = Some(general_purpose::STANDARD.encode(format!("test_resource_server:{secret}"))); @@ -3813,7 +3811,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, "openid groups".to_string() @@ -3831,7 +3828,7 @@ mod tests { let mut idms_prox_write = idms.proxy_write(ct).await; let permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); // == Submit the token exchange code. @@ -3898,7 +3895,7 @@ mod tests { #[idm_test] async fn test_idm_oauth2_insecure_pkce(idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed) { let ct = Duration::from_secs(TEST_CURRENT_TIME); - let (_secret, uat, ident, _) = + let (_secret, _uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, false, false, false).await; let idms_prox_read = idms.proxy_read().await; @@ -3910,7 +3907,6 @@ mod tests { let _consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -3930,7 +3926,7 @@ mod tests { }; idms_prox_read - .check_oauth2_authorisation(&ident, &uat, &auth_req, ct) + .check_oauth2_authorisation(&ident, &auth_req, ct) .expect("Oauth2 authorisation failed"); } @@ -3940,7 +3936,7 @@ mod tests { _idms_delayed: &mut IdmServerDelayed, ) { let ct = Duration::from_secs(TEST_CURRENT_TIME); - let (secret, uat, ident, _) = + let (secret, _uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, false, true, false).await; let idms_prox_read = idms.proxy_read().await; // The public key url should offer an rs key @@ -3977,7 +3973,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -3995,7 +3990,7 @@ mod tests { let mut idms_prox_write = idms.proxy_write(ct).await; let permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); // == Submit the token exchange code. @@ -4052,7 +4047,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -4071,7 +4065,7 @@ mod tests { let mut idms_prox_write = idms.proxy_write(ct).await; let _permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); assert!(idms_prox_write.commit().is_ok()); @@ -4081,14 +4075,13 @@ mod tests { // We need to reload our identity let ident = idms_prox_read - .process_uat_to_identity(&uat, ct) + .process_uat_to_identity(&uat, ct, Source::Internal) .expect("Unable to process uat"); let (_code_verifier, code_challenge) = create_code_verifier!("Whar Garble"); let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -4134,7 +4127,7 @@ mod tests { // We need to reload our identity let ident = idms_prox_read - .process_uat_to_identity(&uat, ct) + .process_uat_to_identity(&uat, ct, Source::Internal) .expect("Unable to process uat"); let (_code_verifier, code_challenge) = create_code_verifier!("Whar Garble"); @@ -4155,7 +4148,7 @@ mod tests { }; let consent_request = idms_prox_read - .check_oauth2_authorisation(&ident, &uat, &auth_req, ct) + .check_oauth2_authorisation(&ident, &auth_req, ct) .expect("Oauth2 authorisation failed"); // Should be in the consent phase; @@ -4194,7 +4187,7 @@ mod tests { // We need to reload our identity let ident = idms_prox_read - .process_uat_to_identity(&uat, ct) + .process_uat_to_identity(&uat, ct, Source::Internal) .expect("Unable to process uat"); let (_code_verifier, code_challenge) = create_code_verifier!("Whar Garble"); @@ -4216,7 +4209,7 @@ mod tests { }; let consent_request = idms_prox_read - .check_oauth2_authorisation(&ident, &uat, &auth_req, ct) + .check_oauth2_authorisation(&ident, &auth_req, ct) .expect("Oauth2 authorisation failed"); // Should be present in the consent phase however! @@ -4251,7 +4244,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -4270,11 +4262,11 @@ mod tests { let mut idms_prox_write = idms.proxy_write(ct).await; let _permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); let ident = idms_prox_write - .process_uat_to_identity(&uat, ct) + .process_uat_to_identity(&uat, ct, Source::Internal) .expect("Unable to process uat"); // Assert that the ident now has the consents. @@ -4295,7 +4287,7 @@ mod tests { assert!(idms_prox_write.qs_write.delete(&de).is_ok()); // Assert the consent maps are gone. let ident = idms_prox_write - .process_uat_to_identity(&uat, ct) + .process_uat_to_identity(&uat, ct, Source::Internal) .expect("Unable to process uat"); dbg!(&o2rs_uuid); dbg!(&ident); @@ -4332,7 +4324,7 @@ mod tests { ) { let ct = Duration::from_secs(TEST_CURRENT_TIME); // Enable pkce is set to FALSE - let (secret, uat, ident, _) = + let (secret, _uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, false, false, false).await; let idms_prox_read = idms.proxy_read().await; @@ -4357,7 +4349,7 @@ mod tests { }; let consent_request = idms_prox_read - .check_oauth2_authorisation(&ident, &uat, &auth_req, ct) + .check_oauth2_authorisation(&ident, &auth_req, ct) .expect("Failed to perform OAuth2 authorisation request."); // Should be in the consent phase; @@ -4373,7 +4365,7 @@ mod tests { let mut idms_prox_write = idms.proxy_write(ct).await; let permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); // == Submit the token exchange code. @@ -4409,7 +4401,7 @@ mod tests { ) { let ct = Duration::from_secs(TEST_CURRENT_TIME); // Enable pkce is set to FALSE - let (secret, uat, ident, _) = + let (secret, _uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, false, false, false).await; let idms_prox_read = idms.proxy_read().await; @@ -4438,7 +4430,7 @@ mod tests { assert!( idms_prox_read - .check_oauth2_authorisation(&ident, &uat, &auth_req, ct) + .check_oauth2_authorisation(&ident, &auth_req, ct) .unwrap_err() == Oauth2Error::InvalidOrigin ); @@ -4447,7 +4439,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -4466,7 +4457,7 @@ mod tests { let mut idms_prox_write = idms.proxy_write(ct).await; let permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); // == Submit the token exchange code. @@ -4497,7 +4488,7 @@ mod tests { ct: Duration, ) -> (AccessTokenResponse, Option) { // First, setup to get a token. - let (secret, uat, ident, _) = + let (secret, _uat, ident, _) = setup_oauth2_resource_server_basic(idms, ct, true, false, false).await; let client_authz = Some(general_purpose::STANDARD.encode(format!("test_resource_server:{secret}"))); @@ -4509,7 +4500,6 @@ mod tests { let consent_request = good_authorisation_request!( idms_prox_read, &ident, - &uat, ct, code_challenge, OAUTH2_SCOPE_OPENID.to_string() @@ -4527,7 +4517,7 @@ mod tests { let mut idms_prox_write = idms.proxy_write(ct).await; let permit_success = idms_prox_write - .check_oauth2_authorise_permit(&ident, &uat, &consent_token, ct) + .check_oauth2_authorise_permit(&ident, &consent_token, ct) .expect("Failed to perform OAuth2 permit"); let token_req: AccessTokenRequest = GrantTypeReq::AuthorizationCode { diff --git a/server/lib/src/idm/reauth.rs b/server/lib/src/idm/reauth.rs index c730abb35..e85db06f4 100644 --- a/server/lib/src/idm/reauth.rs +++ b/server/lib/src/idm/reauth.rs @@ -23,7 +23,7 @@ impl<'a> IdmServerAuthTransaction<'a> { ident: Identity, issue: AuthIssueSession, ct: Duration, - source: Source, + client_auth_info: ClientAuthInfo, ) -> Result { // re-auth only works on users, so lets get the user account. // hint - it's in the ident! @@ -135,7 +135,7 @@ impl<'a> IdmServerAuthTransaction<'a> { issue, webauthn: self.webauthn, ct, - source, + client_auth_info, }; let (auth_session, state) = AuthSession::new_reauth(asd, ident.session_id, session, session_cred_id); @@ -330,7 +330,9 @@ mod tests { let auth_init = AuthEvent::named_init("testperson"); - let r1 = idms_auth.auth(&auth_init, ct, Source::Internal).await; + let r1 = idms_auth + .auth(&auth_init, ct, Source::Internal.into()) + .await; let ar = r1.unwrap(); let AuthResult { sessionid, state } = ar; @@ -341,7 +343,9 @@ mod tests { let auth_begin = AuthEvent::begin_mech(sessionid, AuthMech::Passkey); - let r2 = idms_auth.auth(&auth_begin, ct, Source::Internal).await; + let r2 = idms_auth + .auth(&auth_begin, ct, Source::Internal.into()) + .await; let ar = r2.unwrap(); let AuthResult { sessionid, state } = ar; @@ -363,7 +367,9 @@ mod tests { let passkey_step = AuthEvent::cred_step_passkey(sessionid, resp); - let r3 = idms_auth.auth(&passkey_step, ct, Source::Internal).await; + let r3 = idms_auth + .auth(&passkey_step, ct, Source::Internal.into()) + .await; debug!("r3 ==> {:?}", r3); idms_auth.commit().expect("Must not fail"); @@ -403,7 +409,9 @@ mod tests { let auth_init = AuthEvent::named_init("testperson"); - let r1 = idms_auth.auth(&auth_init, ct, Source::Internal).await; + let r1 = idms_auth + .auth(&auth_init, ct, Source::Internal.into()) + .await; let ar = r1.unwrap(); let AuthResult { sessionid, state } = ar; @@ -414,7 +422,9 @@ mod tests { let auth_begin = AuthEvent::begin_mech(sessionid, AuthMech::PasswordMfa); - let r2 = idms_auth.auth(&auth_begin, ct, Source::Internal).await; + let r2 = idms_auth + .auth(&auth_begin, ct, Source::Internal.into()) + .await; let ar = r2.unwrap(); let AuthResult { sessionid, state } = ar; @@ -425,7 +435,9 @@ mod tests { .expect("Failed to perform totp step"); let totp_step = AuthEvent::cred_step_totp(sessionid, totp); - let r2 = idms_auth.auth(&totp_step, ct, Source::Internal).await; + let r2 = idms_auth + .auth(&totp_step, ct, Source::Internal.into()) + .await; let ar = r2.unwrap(); let AuthResult { sessionid, state } = ar; @@ -434,7 +446,7 @@ mod tests { let pw_step = AuthEvent::cred_step_password(sessionid, pw); // Expect success - let r3 = idms_auth.auth(&pw_step, ct, Source::Internal).await; + let r3 = idms_auth.auth(&pw_step, ct, Source::Internal.into()).await; debug!("r3 ==> {:?}", r3); idms_auth.commit().expect("Must not fail"); @@ -457,11 +469,15 @@ mod tests { } } - async fn token_to_ident(idms: &IdmServer, ct: Duration, token: Option<&str>) -> Identity { + async fn token_to_ident( + idms: &IdmServer, + ct: Duration, + client_auth_info: ClientAuthInfo, + ) -> Identity { let mut idms_prox_read = idms.proxy_read().await; idms_prox_read - .validate_and_parse_token_to_ident(token, ct) + .validate_client_auth_info_to_ident(client_auth_info, ct) .expect("Invalid UAT") } @@ -476,7 +492,12 @@ mod tests { let origin = idms_auth.get_origin().clone(); let auth_allowed = idms_auth - .reauth_init(ident.clone(), AuthIssueSession::Token, ct, Source::Internal) + .reauth_init( + ident.clone(), + AuthIssueSession::Token, + ct, + Source::Internal.into(), + ) .await .expect("Failed to start reauth."); @@ -500,7 +521,9 @@ mod tests { let passkey_step = AuthEvent::cred_step_passkey(sessionid, resp); - let r3 = idms_auth.auth(&passkey_step, ct, Source::Internal).await; + let r3 = idms_auth + .auth(&passkey_step, ct, Source::Internal.into()) + .await; debug!("r3 ==> {:?}", r3); idms_auth.commit().expect("Must not fail"); @@ -535,7 +558,12 @@ mod tests { let mut idms_auth = idms.auth().await; let auth_allowed = idms_auth - .reauth_init(ident.clone(), AuthIssueSession::Token, ct, Source::Internal) + .reauth_init( + ident.clone(), + AuthIssueSession::Token, + ct, + Source::Internal.into(), + ) .await .expect("Failed to start reauth."); @@ -560,7 +588,9 @@ mod tests { .expect("Failed to perform totp step"); let totp_step = AuthEvent::cred_step_totp(sessionid, totp); - let r2 = idms_auth.auth(&totp_step, ct, Source::Internal).await; + let r2 = idms_auth + .auth(&totp_step, ct, Source::Internal.into()) + .await; let ar = r2.unwrap(); let AuthResult { sessionid, state } = ar; @@ -569,7 +599,7 @@ mod tests { let pw_step = AuthEvent::cred_step_password(sessionid, pw); // Expect success - let r3 = idms_auth.auth(&pw_step, ct, Source::Internal).await; + let r3 = idms_auth.auth(&pw_step, ct, Source::Internal.into()).await; debug!("r3 ==> {:?}", r3); idms_auth.commit().expect("Must not fail"); @@ -601,7 +631,7 @@ mod tests { .expect("failed to authenticate with passkey"); // Token_str to uat - let ident = token_to_ident(idms, ct, Some(token.as_str())).await; + let ident = token_to_ident(idms, ct, token.as_str().into()).await; // Check that the rw entitlement is not present debug!(?ident); @@ -620,7 +650,7 @@ mod tests { .expect("Failed to get new session token"); // Token_str to uat - let ident = token_to_ident(idms, ct, Some(token.as_str())).await; + let ident = token_to_ident(idms, ct, token.as_str().into()).await; // They now have the entitlement. debug!(?ident); @@ -647,7 +677,7 @@ mod tests { .expect("failed to authenticate with passkey"); // Token_str to uat - let ident = token_to_ident(idms, ct, Some(token.as_str())).await; + let ident = token_to_ident(idms, ct, token.as_str().into()).await; // Check that the rw entitlement is not present debug!(?ident); diff --git a/server/lib/src/idm/scim.rs b/server/lib/src/idm/scim.rs index 086c5f605..e378fd02f 100644 --- a/server/lib/src/idm/scim.rs +++ b/server/lib/src/idm/scim.rs @@ -1594,7 +1594,7 @@ mod tests { let mut idms_prox_read = idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_sync_token_to_ident(Some(sync_token.as_str()), ct) + .validate_sync_client_auth_info_to_ident(sync_token.as_str().into(), ct) .expect("Failed to validate sync token"); assert!(Some(sync_uuid) == ident.get_uuid()); @@ -1650,7 +1650,7 @@ mod tests { // -- Check the happy path. let mut idms_prox_read = idms.proxy_read().await; let ident = idms_prox_read - .validate_and_parse_sync_token_to_ident(Some(sync_token.as_str()), ct) + .validate_sync_client_auth_info_to_ident(sync_token.as_str().into(), ct) .expect("Failed to validate sync token"); assert!(Some(sync_uuid) == ident.get_uuid()); drop(idms_prox_read); @@ -1671,7 +1671,7 @@ mod tests { // Must fail let mut idms_prox_read = idms.proxy_read().await; let fail = - idms_prox_read.validate_and_parse_sync_token_to_ident(Some(sync_token.as_str()), ct); + idms_prox_read.validate_sync_client_auth_info_to_ident(sync_token.as_str().into(), ct); assert!(matches!(fail, Err(OperationError::NotAuthenticated))); drop(idms_prox_read); @@ -1695,7 +1695,7 @@ mod tests { let mut idms_prox_read = idms.proxy_read().await; let fail = - idms_prox_read.validate_and_parse_sync_token_to_ident(Some(sync_token.as_str()), ct); + idms_prox_read.validate_sync_client_auth_info_to_ident(sync_token.as_str().into(), ct); assert!(matches!(fail, Err(OperationError::NotAuthenticated))); // -- Forge a session, use wrong types @@ -1737,8 +1737,8 @@ mod tests { .map(|jws_signed| jws_signed.to_string()) .expect("Unable to sign forged token"); - let fail = - idms_prox_read.validate_and_parse_sync_token_to_ident(Some(forged_token.as_str()), ct); + let fail = idms_prox_read + .validate_sync_client_auth_info_to_ident(forged_token.as_str().into(), ct); assert!(matches!(fail, Err(OperationError::NotAuthenticated))); } @@ -1770,7 +1770,7 @@ mod tests { .expect("failed to generate new scim sync token"); let ident = idms_prox_write - .validate_and_parse_sync_token_to_ident(Some(sync_token.as_str()), ct) + .validate_sync_client_auth_info_to_ident(sync_token.as_str().into(), ct) .expect("Failed to process sync token to ident"); (sync_uuid, ident) diff --git a/server/lib/src/idm/server.rs b/server/lib/src/idm/server.rs index 2f793e342..90443cc80 100644 --- a/server/lib/src/idm/server.rs +++ b/server/lib/src/idm/server.rs @@ -407,27 +407,63 @@ pub trait IdmServerTransaction<'a> { /// that we internally sign with. We can use this to select the appropriate token type /// and validation method. #[instrument(level = "info", skip_all)] - fn validate_and_parse_token_to_ident( + fn validate_client_auth_info_to_ident( &mut self, - token: Option<&str>, + client_auth_info: ClientAuthInfo, ct: Duration, ) -> Result { - match self.validate_and_parse_token_to_token(token, ct)? { - Token::UserAuthToken(uat) => self.process_uat_to_identity(&uat, ct), - Token::ApiToken(apit, entry) => self.process_apit_to_identity(&apit, entry, ct), + let ClientAuthInfo { + client_cert, + bearer_token, + source, + } = client_auth_info; + + match (client_cert, bearer_token) { + (Some(_client_cert_info), _) => { + // TODO: Cert validation here. + warn!("Unable to process client certificate identity"); + Err(OperationError::NotAuthenticated) + } + (None, Some(token)) => match self.validate_and_parse_token_to_token(&token, ct)? { + Token::UserAuthToken(uat) => self.process_uat_to_identity(&uat, ct, source), + Token::ApiToken(apit, entry) => { + self.process_apit_to_identity(&apit, source, entry, ct) + } + }, + (None, None) => { + warn!("No client certificate or bearer tokens were supplied"); + Err(OperationError::NotAuthenticated) + } } } #[instrument(level = "info", skip_all)] - fn validate_and_parse_token_to_uat( + fn validate_client_auth_info_to_uat( &mut self, - token: Option<&str>, + client_auth_info: ClientAuthInfo, ct: Duration, ) -> Result { - match self.validate_and_parse_token_to_token(token, ct)? { - Token::UserAuthToken(uat) => Ok(uat), - Token::ApiToken(_apit, _entry) => { - warn!("Unable to process non user auth token"); + let ClientAuthInfo { + client_cert, + bearer_token, + source: _, + } = client_auth_info; + + match (client_cert, bearer_token) { + (Some(_client_cert_info), _) => { + // TODO: Cert validation here. + warn!("Unable to process client certificate identity"); + Err(OperationError::NotAuthenticated) + } + (None, Some(token)) => match self.validate_and_parse_token_to_token(&token, ct)? { + Token::UserAuthToken(uat) => Ok(uat), + Token::ApiToken(_apit, _entry) => { + warn!("Unable to process non user auth token"); + Err(OperationError::NotAuthenticated) + } + }, + (None, None) => { + warn!("No client certificate or bearer tokens were supplied"); Err(OperationError::NotAuthenticated) } } @@ -435,20 +471,13 @@ pub trait IdmServerTransaction<'a> { fn validate_and_parse_token_to_token( &mut self, - token: Option<&str>, + token: &str, ct: Duration, ) -> Result { - let jwsu = token - .ok_or_else(|| { - security_info!("No token provided"); - OperationError::NotAuthenticated - }) - .and_then(|s| { - JwsCompact::from_str(s).map_err(|e| { - security_info!(?e, "Unable to decode token"); - OperationError::NotAuthenticated - }) - })?; + let jwsu = JwsCompact::from_str(token).map_err(|e| { + security_info!(?e, "Unable to decode token"); + OperationError::NotAuthenticated + })?; // Frow the unverified token we can now get the kid, and use that to locate the correct // key to id the token. @@ -692,6 +721,7 @@ pub trait IdmServerTransaction<'a> { &mut self, uat: &UserAuthToken, ct: Duration, + source: Source, ) -> Result { // From a UAT, get the current identity and associated information. let entry = self @@ -744,6 +774,7 @@ pub trait IdmServerTransaction<'a> { Ok(Identity { origin: IdentType::User(IdentUser { entry }), + source, session_id: uat.session_id, scope, limits, @@ -754,6 +785,7 @@ pub trait IdmServerTransaction<'a> { fn process_apit_to_identity( &mut self, apit: &ApiToken, + source: Source, entry: Arc, ct: Duration, ) -> Result { @@ -769,6 +801,7 @@ pub trait IdmServerTransaction<'a> { let limits = Limits::default(); Ok(Identity { origin: IdentType::User(IdentUser { entry }), + source, session_id: apit.token_id, scope, limits, @@ -779,6 +812,7 @@ pub trait IdmServerTransaction<'a> { fn validate_ldap_session( &mut self, session: &LdapSession, + source: Source, ct: Duration, ) -> Result { match session { @@ -815,6 +849,7 @@ pub trait IdmServerTransaction<'a> { Ok(Identity { origin: IdentType::User(IdentUser { entry: anon_entry }), + source, session_id, scope: AccessScope::ReadOnly, limits, @@ -824,7 +859,7 @@ pub trait IdmServerTransaction<'a> { Err(OperationError::SessionExpired) } } - LdapSession::UserAuthToken(uat) => self.process_uat_to_identity(uat, ct), + LdapSession::UserAuthToken(uat) => self.process_uat_to_identity(uat, ct, source), LdapSession::ApiToken(apit) => { let entry = self .get_qs_txn() @@ -834,24 +869,27 @@ pub trait IdmServerTransaction<'a> { e })?; - self.process_apit_to_identity(apit, entry, ct) + self.process_apit_to_identity(apit, source, entry, ct) } } } #[instrument(level = "info", skip_all)] - fn validate_and_parse_sync_token_to_ident( + fn validate_sync_client_auth_info_to_ident( &mut self, - token: Option<&str>, + client_auth_info: ClientAuthInfo, ct: Duration, ) -> Result { - let jwsu = token + // FUTURE: Could allow mTLS here instead? + + let jwsu = client_auth_info + .bearer_token .ok_or_else(|| { security_info!("No token provided"); OperationError::NotAuthenticated }) .and_then(|s| { - JwsCompact::from_str(s).map_err(|e| { + JwsCompact::from_str(s.as_str()).map_err(|e| { security_info!(?e, "Unable to decode token"); OperationError::NotAuthenticated }) @@ -921,6 +959,7 @@ pub trait IdmServerTransaction<'a> { let limits = Limits::unlimited(); Ok(Identity { origin: IdentType::Synch(entry.get_uuid()), + source: client_auth_info.source, session_id: sync_token.token_id, scope, limits, @@ -969,7 +1008,7 @@ impl<'a> IdmServerAuthTransaction<'a> { &mut self, ae: &AuthEvent, ct: Duration, - source: Source, + client_auth_info: ClientAuthInfo, ) -> Result { // Match on the auth event, to see what we need to do. match &ae.step { @@ -1051,7 +1090,7 @@ impl<'a> IdmServerAuthTransaction<'a> { issue: init.issue, webauthn: self.webauthn, ct, - source, + client_auth_info, }; let (auth_session, state) = AuthSession::new(asd, init.privileged); @@ -1289,7 +1328,7 @@ impl<'a> IdmServerAuthTransaction<'a> { lae: &LdapTokenAuthEvent, ct: Duration, ) -> Result, OperationError> { - match self.validate_and_parse_token_to_token(Some(&lae.token), ct)? { + match self.validate_and_parse_token_to_token(&lae.token, ct)? { Token::UserAuthToken(uat) => { let spn = uat.spn.clone(); Ok(Some(LdapBoundToken { @@ -2143,7 +2182,7 @@ mod tests { .auth( &anon_init, Duration::from_secs(TEST_CURRENT_TIME), - Source::Internal, + Source::Internal.into(), ) .await; /* Some weird lifetime things happen here ... */ @@ -2185,7 +2224,7 @@ mod tests { .auth( &anon_begin, Duration::from_secs(TEST_CURRENT_TIME), - Source::Internal, + Source::Internal.into(), ) .await; debug!("r2 ==> {:?}", r2); @@ -2227,7 +2266,7 @@ mod tests { .auth( &anon_step, Duration::from_secs(TEST_CURRENT_TIME), - Source::Internal, + Source::Internal.into(), ) .await; debug!("r2 ==> {:?}", r2); @@ -2275,7 +2314,7 @@ mod tests { .auth( &anon_step, Duration::from_secs(TEST_CURRENT_TIME), - Source::Internal, + Source::Internal.into(), ) .await; debug!("r2 ==> {:?}", r2); @@ -2318,7 +2357,9 @@ mod tests { let mut idms_auth = idms.auth().await; let admin_init = AuthEvent::named_init(name); - let r1 = idms_auth.auth(&admin_init, ct, Source::Internal).await; + let r1 = idms_auth + .auth(&admin_init, ct, Source::Internal.into()) + .await; let ar = r1.unwrap(); let AuthResult { sessionid, state } = ar; @@ -2327,7 +2368,9 @@ mod tests { // Now push that we want the Password Mech. let admin_begin = AuthEvent::begin_mech(sessionid, AuthMech::Password); - let r2 = idms_auth.auth(&admin_begin, ct, Source::Internal).await; + let r2 = idms_auth + .auth(&admin_begin, ct, Source::Internal.into()) + .await; let ar = r2.unwrap(); let AuthResult { sessionid, state } = ar; @@ -2356,7 +2399,7 @@ mod tests { .auth( &anon_step, Duration::from_secs(TEST_CURRENT_TIME), - Source::Internal, + Source::Internal.into(), ) .await; debug!("r2 ==> {:?}", r2); @@ -2428,7 +2471,7 @@ mod tests { .auth( &anon_step, Duration::from_secs(TEST_CURRENT_TIME), - Source::Internal, + Source::Internal.into(), ) .await; debug!("r2 ==> {:?}", r2); @@ -2483,7 +2526,7 @@ mod tests { .auth( &anon_step, Duration::from_secs(TEST_CURRENT_TIME), - Source::Internal, + Source::Internal.into(), ) .await; debug!("r2 ==> {:?}", r2); @@ -2906,7 +2949,7 @@ mod tests { let mut idms_auth = idms.auth().await; let admin_init = AuthEvent::named_init("admin"); let r1 = idms_auth - .auth(&admin_init, time_low, Source::Internal) + .auth(&admin_init, time_low, Source::Internal.into()) .await; let ar = r1.unwrap(); @@ -2928,7 +2971,7 @@ mod tests { let mut idms_auth = idms.auth().await; let admin_init = AuthEvent::named_init("admin"); let r1 = idms_auth - .auth(&admin_init, time_high, Source::Internal) + .auth(&admin_init, time_high, Source::Internal.into()) .await; let ar = r1.unwrap(); @@ -3081,7 +3124,7 @@ mod tests { .auth( &anon_step, Duration::from_secs(TEST_CURRENT_TIME), - Source::Internal, + Source::Internal.into(), ) .await; debug!("r2 ==> {:?}", r2); @@ -3126,7 +3169,7 @@ mod tests { .auth( &admin_init, Duration::from_secs(TEST_CURRENT_TIME), - Source::Internal, + Source::Internal.into(), ) .await; let ar = r1.unwrap(); @@ -3140,7 +3183,7 @@ mod tests { .auth( &admin_begin, Duration::from_secs(TEST_CURRENT_TIME), - Source::Internal, + Source::Internal.into(), ) .await; let ar = r2.unwrap(); @@ -3177,7 +3220,7 @@ mod tests { .auth( &anon_step, Duration::from_secs(TEST_CURRENT_TIME + 2), - Source::Internal, + Source::Internal.into(), ) .await; debug!("r2 ==> {:?}", r2); @@ -3245,7 +3288,7 @@ mod tests { .auth( &anon_step, Duration::from_secs(TEST_CURRENT_TIME), - Source::Internal, + Source::Internal.into(), ) .await; debug!("r2 ==> {:?}", r2); @@ -3288,7 +3331,7 @@ mod tests { .auth( &anon_step, Duration::from_secs(TEST_CURRENT_TIME), - Source::Internal, + Source::Internal.into(), ) .await; debug!("r2 ==> {:?}", r2); @@ -3394,11 +3437,11 @@ mod tests { // Check it's valid - This is within the time window so will pass. idms_prox_read - .validate_and_parse_token_to_ident(Some(token.as_str()), ct) + .validate_client_auth_info_to_ident(token.as_str().into(), ct) .expect("Failed to validate"); // In X time it should be INVALID - match idms_prox_read.validate_and_parse_token_to_ident(Some(token.as_str()), expiry) { + match idms_prox_read.validate_client_auth_info_to_ident(token.as_str().into(), expiry) { Err(OperationError::SessionExpired) => {} _ => assert!(false), } @@ -3539,12 +3582,12 @@ mod tests { // Check it's valid. idms_prox_read - .validate_and_parse_token_to_ident(Some(token.as_str()), ct) + .validate_client_auth_info_to_ident(token.as_str().into(), ct) .expect("Failed to validate"); // If the auth session record wasn't processed, this will fail. idms_prox_read - .validate_and_parse_token_to_ident(Some(token.as_str()), post_grace) + .validate_client_auth_info_to_ident(token.as_str().into(), post_grace) .expect("Failed to validate"); drop(idms_prox_read); @@ -3560,7 +3603,7 @@ mod tests { // Now, within gracewindow, it's NOT valid because the session entry exists and is in // the revoked state! - match idms_prox_read.validate_and_parse_token_to_ident(Some(token.as_str()), post_grace) { + match idms_prox_read.validate_client_auth_info_to_ident(token.as_str().into(), post_grace) { Err(OperationError::SessionExpired) => {} _ => assert!(false), } @@ -3585,11 +3628,11 @@ mod tests { let mut idms_prox_read = idms.proxy_read().await; idms_prox_read - .validate_and_parse_token_to_ident(Some(token.as_str()), ct) + .validate_client_auth_info_to_ident(token.as_str().into(), ct) .expect("Failed to validate"); // post grace, it's not valid. - match idms_prox_read.validate_and_parse_token_to_ident(Some(token.as_str()), post_grace) { + match idms_prox_read.validate_client_auth_info_to_ident(token.as_str().into(), post_grace) { Err(OperationError::SessionExpired) => {} _ => assert!(false), } @@ -3623,7 +3666,9 @@ mod tests { // Send the initial auth event for initialising the session let anon_init = AuthEvent::anonymous_init(); // Expect success - let r1 = idms_auth.auth(&anon_init, ct, Source::Internal).await; + let r1 = idms_auth + .auth(&anon_init, ct, Source::Internal.into()) + .await; /* Some weird lifetime things happen here ... */ let sid = match r1 { @@ -3657,7 +3702,9 @@ mod tests { let mut idms_auth = idms.auth().await; let anon_begin = AuthEvent::begin_mech(sid, AuthMech::Anonymous); - let r2 = idms_auth.auth(&anon_begin, ct, Source::Internal).await; + let r2 = idms_auth + .auth(&anon_begin, ct, Source::Internal.into()) + .await; match r2 { Ok(ar) => { @@ -3692,7 +3739,9 @@ mod tests { let anon_step = AuthEvent::cred_step_anonymous(sid); // Expect success - let r2 = idms_auth.auth(&anon_step, ct, Source::Internal).await; + let r2 = idms_auth + .auth(&anon_step, ct, Source::Internal.into()) + .await; let token = match r2 { Ok(ar) => { @@ -3723,7 +3772,7 @@ mod tests { let Token::UserAuthToken(uat) = idms .proxy_read() .await - .validate_and_parse_token_to_token(Some(&token), ct) + .validate_and_parse_token_to_token(&token, ct) .expect("Must not fail") else { panic!("Unexpected auth token type for anonymous auth"); @@ -3761,7 +3810,7 @@ mod tests { ) .expect("Unable to create uat"); let ident = idms_prox_write - .process_uat_to_identity(&uat, ct) + .process_uat_to_identity(&uat, ct, Source::Internal) .expect("Unable to process uat"); assert!(!ident.has_claim("authtype_anonymous")); @@ -3780,7 +3829,7 @@ mod tests { ) .expect("Unable to create uat"); let ident = idms_prox_write - .process_uat_to_identity(&uat, ct) + .process_uat_to_identity(&uat, ct, Source::Internal) .expect("Unable to process uat"); assert!(!ident.has_claim("authtype_unixpassword")); @@ -3799,7 +3848,7 @@ mod tests { ) .expect("Unable to create uat"); let ident = idms_prox_write - .process_uat_to_identity(&uat, ct) + .process_uat_to_identity(&uat, ct, Source::Internal) .expect("Unable to process uat"); assert!(!ident.has_claim("authtype_password")); @@ -3818,7 +3867,7 @@ mod tests { ) .expect("Unable to create uat"); let ident = idms_prox_write - .process_uat_to_identity(&uat, ct) + .process_uat_to_identity(&uat, ct, Source::Internal) .expect("Unable to process uat"); assert!(!ident.has_claim("authtype_generatedpassword")); @@ -3837,7 +3886,7 @@ mod tests { ) .expect("Unable to create uat"); let ident = idms_prox_write - .process_uat_to_identity(&uat, ct) + .process_uat_to_identity(&uat, ct, Source::Internal) .expect("Unable to process uat"); assert!(!ident.has_claim("authtype_webauthn")); @@ -3856,7 +3905,7 @@ mod tests { ) .expect("Unable to create uat"); let ident = idms_prox_write - .process_uat_to_identity(&uat, ct) + .process_uat_to_identity(&uat, ct, Source::Internal) .expect("Unable to process uat"); assert!(!ident.has_claim("authtype_passwordmfa")); @@ -3887,7 +3936,7 @@ mod tests { // Check it's valid. idms_prox_read - .validate_and_parse_token_to_ident(Some(token.as_str()), ct) + .validate_client_auth_info_to_ident(token.as_str().into(), ct) .expect("Failed to validate"); drop(idms_prox_read); @@ -3918,11 +3967,11 @@ mod tests { let mut idms_prox_read = idms.proxy_read().await; assert!(idms_prox_read - .validate_and_parse_token_to_ident(Some(token.as_str()), ct) + .validate_client_auth_info_to_ident(token.as_str().into(), ct) .is_err()); // A new token will work due to the matching key. idms_prox_read - .validate_and_parse_token_to_ident(Some(new_token.as_str()), ct) + .validate_client_auth_info_to_ident(new_token.as_str().into(), ct) .expect("Failed to validate"); } diff --git a/server/lib/src/idm/serviceaccount.rs b/server/lib/src/idm/serviceaccount.rs index b2e16d9de..05822a5ed 100644 --- a/server/lib/src/idm/serviceaccount.rs +++ b/server/lib/src/idm/serviceaccount.rs @@ -490,7 +490,7 @@ mod tests { .unwrap(); let ident = idms_prox_write - .validate_and_parse_token_to_ident(Some(&api_token), ct) + .validate_client_auth_info_to_ident(api_token.as_str().into(), ct) .expect("Unable to verify api token."); assert!(ident.get_uuid() == Some(testaccount_uuid)); @@ -500,7 +500,7 @@ mod tests { // Check the expiry assert!( idms_prox_write - .validate_and_parse_token_to_ident(Some(&api_token), post_exp) + .validate_client_auth_info_to_ident(api_token.as_str().into(), post_exp) .expect_err("Should not succeed") == OperationError::SessionExpired ); @@ -515,14 +515,14 @@ mod tests { // Within gracewindow? // This is okay, because we are within the gracewindow. let ident = idms_prox_write - .validate_and_parse_token_to_ident(Some(&api_token), ct) + .validate_client_auth_info_to_ident(api_token.as_str().into(), ct) .expect("Unable to verify api token."); assert!(ident.get_uuid() == Some(testaccount_uuid)); // Past gracewindow? assert!( idms_prox_write - .validate_and_parse_token_to_ident(Some(&api_token), past_grc) + .validate_client_auth_info_to_ident(api_token.as_str().into(), past_grc) .expect_err("Should not succeed") == OperationError::SessionExpired ); diff --git a/server/lib/src/lib.rs b/server/lib/src/lib.rs index c135866bb..b439fd98a 100644 --- a/server/lib/src/lib.rs +++ b/server/lib/src/lib.rs @@ -89,6 +89,7 @@ pub mod prelude { FilterInvalid, FilterValid, FC, }; pub use crate::idm::server::{IdmServer, IdmServerAudit, IdmServerDelayed}; + pub use crate::idm::{ClientAuthInfo, ClientCertInfo}; pub use crate::modify::{ m_assert, m_pres, m_purge, m_remove, Modify, ModifyInvalid, ModifyList, ModifyValid, }; diff --git a/server/lib/src/server/identity.rs b/server/lib/src/server/identity.rs index a923c4ac2..2a8c26865 100644 --- a/server/lib/src/server/identity.rs +++ b/server/lib/src/server/identity.rs @@ -21,7 +21,7 @@ use crate::value::Session; pub enum Source { Internal, Https(IpAddr), - // Ldaps, + Ldaps(IpAddr), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -102,7 +102,7 @@ impl From<&IdentType> for IdentityId { /// and other info that can assist with server decision making. pub struct Identity { pub origin: IdentType, - // pub(crate) source: + pub(crate) source: Source, // pub(crate) impersonate: bool, // In a way I guess these are session claims? pub(crate) session_id: Uuid, @@ -131,9 +131,14 @@ impl std::fmt::Display for Identity { } impl Identity { + pub fn source(&self) -> &Source { + &self.source + } + pub fn from_internal() -> Self { Identity { origin: IdentType::Internal, + source: Source::Internal, session_id: uuid!("00000000-0000-0000-0000-000000000000"), scope: AccessScope::ReadWrite, limits: Limits::unlimited(), @@ -144,6 +149,7 @@ impl Identity { pub fn from_impersonate_entry_readonly(entry: Arc>) -> Self { Identity { origin: IdentType::User(IdentUser { entry }), + source: Source::Internal, session_id: uuid!("00000000-0000-0000-0000-000000000000"), scope: AccessScope::ReadOnly, limits: Limits::unlimited(), @@ -156,6 +162,7 @@ impl Identity { ) -> Self { Identity { origin: IdentType::User(IdentUser { entry }), + source: Source::Internal, session_id: uuid!("00000000-0000-0000-0000-000000000000"), scope: AccessScope::ReadWrite, limits: Limits::unlimited(), @@ -213,6 +220,17 @@ impl Identity { } } + /// Indicate if the session associated with this identity has a session + /// that can logout. Examples of sessions that *can not* logout are anonymous, + /// tokens, or PIV sessions. + pub fn can_logout(&self) -> bool { + match &self.origin { + IdentType::Internal => false, + IdentType::User(u) => u.entry.get_uuid() != UUID_ANONYMOUS, + IdentType::Synch(_) => false, + } + } + pub fn get_event_origin_id(&self) -> IdentityId { IdentityId::from(&self.origin) }