20231222 piv authentication (#2398)

Foundations of PIV authentication
This commit is contained in:
Firstyear 2023-12-30 09:15:26 +10:00 committed by GitHub
parent 307a66ea29
commit cc79b2a205
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1247 additions and 663 deletions

View file

@ -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
#

View file

@ -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<String>,
client_auth_info: ClientAuthInfo,
req: SearchRequest,
eventid: Uuid,
) -> Result<SearchResponse, OperationError> {
@ -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<Uuid>,
req: AuthRequest,
eventid: Uuid,
ip_addr: IpAddr,
client_auth_info: ClientAuthInfo,
) -> Result<AuthResult, OperationError> {
// 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<String>,
client_auth_info: ClientAuthInfo,
issue: AuthIssueSession,
eventid: Uuid,
ip_addr: IpAddr,
) -> Result<AuthResult, OperationError> {
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<String>,
client_auth_info: ClientAuthInfo,
eventid: Uuid,
) -> Result<WhoamiResponse, OperationError> {
// Begin a read
@ -344,7 +340,7 @@ impl QueryServerReadV1 {
// then move this to core.rs, and don't allow Option<UAT> 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<String>,
client_auth_info: ClientAuthInfo,
eventid: Uuid,
) -> Result<UserAuthToken, OperationError> {
let ct = duration_from_epoch_now();
@ -389,7 +385,7 @@ impl QueryServerReadV1 {
// then move this to core.rs, and don't allow Option<UAT> 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<String>,
client_auth_info: ClientAuthInfo,
rs: Filter<FilterInvalid>,
) -> Result<ImageValue, OperationError> {
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<String>,
client_auth_info: ClientAuthInfo,
filter: Filter<FilterInvalid>,
attrs: Option<Vec<String>>,
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<String>,
client_auth_info: ClientAuthInfo,
filter: Filter<FilterInvalid>,
attrs: Option<Vec<String>>,
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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
eventid: Uuid,
) -> Result<Option<String>, 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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
eventid: Uuid,
) -> Result<RadiusAuthToken, OperationError> {
@ -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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
eventid: Uuid,
) -> Result<UnixUserToken, OperationError> {
@ -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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
eventid: Uuid,
) -> Result<UnixGroupToken, 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
@ -735,14 +731,14 @@ impl QueryServerReadV1 {
)]
pub async fn handle_internalsshkeyread(
&self,
uat: Option<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
eventid: Uuid,
) -> Result<Vec<String>, 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<String>,
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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
eventid: Uuid,
) -> Result<Vec<ApiToken>, 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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
eventid: Uuid,
) -> Result<Vec<UatStatus>, 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<String>,
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<String>,
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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
eventid: Uuid,
) -> Result<CredentialStatus, OperationError> {
@ -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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
eventid: Uuid,
) -> Result<BackupCodesView, OperationError> {
@ -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<String>,
client_auth_info: ClientAuthInfo,
filter: Filter<FilterInvalid>,
eventid: Uuid,
) -> Result<Option<String>, 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<String>,
client_auth_info: ClientAuthInfo,
auth_req: AuthorisationRequest,
eventid: Uuid,
) -> Result<AuthoriseResponse, Oauth2Error> {
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<String>,
client_auth_info: ClientAuthInfo,
consent_req: String,
eventid: Uuid,
) -> Result<Url, OperationError> {
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<String>,
client_auth_info: ClientAuthInfo,
eventid: Uuid,
) -> Result<Vec<AppLink>, 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<String>,
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<LdapBoundToken>,
ip_addr: IpAddr,
) -> Option<LdapResponseState> {
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);

View file

@ -16,7 +16,7 @@ impl QueryServerWriteV1 {
)]
pub async fn handle_sync_account_token_generate(
&self,
uat: Option<String>,
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<String>,
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<String>,
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<String>,
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<String>,
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<String>,
client_auth_info: ClientAuthInfo,
eventid: Uuid,
) -> Result<ScimSyncState, 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_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)
}

View file

@ -55,7 +55,7 @@ impl QueryServerWriteV1 {
#[instrument(level = "debug", skip_all)]
async fn modify_from_parts(
&self,
uat: Option<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: &str,
proto_ml: &ProtoModifyList,
filter: Filter<FilterInvalid>,
@ -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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: &str,
ml: &ModifyList<ModifyInvalid>,
filter: Filter<FilterInvalid>,
@ -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<String>,
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<String>,
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<String>,
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<String>,
client_auth_info: ClientAuthInfo,
filter: Filter<FilterInvalid>,
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<String>,
client_auth_info: ClientAuthInfo,
filter: Filter<FilterInvalid>,
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<String>,
client_auth_info: ClientAuthInfo,
filter: Filter<FilterInvalid>,
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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
eventid: Uuid,
) -> Result<String, 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
@ -425,7 +425,7 @@ impl QueryServerWriteV1 {
)]
pub async fn handle_service_account_api_token_generate(
&self,
uat: Option<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
label: String,
expiry: Option<OffsetDateTime>,
@ -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<String>,
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<String>,
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<String>,
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<String>,
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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
ttl: Option<Duration>,
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<String>,
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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
eventid: Uuid,
) -> Result<String, 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
@ -859,7 +858,7 @@ impl QueryServerWriteV1 {
)]
pub async fn handle_purgeattribute(
&self,
uat: Option<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
attr: String,
filter: Filter<FilterInvalid>,
@ -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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
attr: String,
values: Vec<String>,
@ -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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
attr: String,
values: Vec<String>,
@ -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<String>,
client_auth_info: ClientAuthInfo,
uuid_or_name: String,
attr: String,
values: Vec<String>,
@ -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<String>,
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<String>,
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<String>,
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<String>,
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<String>,
client_auth_info: ClientAuthInfo,
rs: Filter<FilterInvalid>,
) -> 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<String>,
client_auth_info: ClientAuthInfo,
rs: Filter<FilterInvalid>,
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<String>,
client_auth_info: ClientAuthInfo,
group: String,
scopes: Vec<String>,
filter: Filter<FilterInvalid>,
@ -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<String>,
client_auth_info: ClientAuthInfo,
group: String,
filter: Filter<FilterInvalid>,
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<String>,
client_auth_info: ClientAuthInfo,
group: String,
scopes: Vec<String>,
filter: Filter<FilterInvalid>,
@ -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<String>,
client_auth_info: ClientAuthInfo,
group: String,
filter: Filter<FilterInvalid>,
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<String>,
client_auth_info: ClientAuthInfo,
consent_req: String,
eventid: Uuid,
) -> Result<AuthorisePermitSuccess, OperationError> {
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))
}

View file

@ -77,6 +77,7 @@ fn default_online_backup_versions() -> usize {
pub struct TlsConfiguration {
pub chain: String,
pub key: String,
pub client_ca: Option<String>,
}
/// 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<String>,
/// The directory path of the client ca and crl dir.
pub tls_client_ca: Option<String>,
/// 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<String>, key: &Option<String>) {
pub fn update_tls(
&mut self,
chain: &Option<String>,
key: &Option<String>,
client_ca: &Option<String>,
) {
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!");

View file

@ -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<ServerState> for TrustedClientIp {
parts: &mut Parts,
state: &ServerState,
) -> Result<Self, Self::Rejection> {
if state.trust_x_forward_for {
let ConnectInfo(ClientConnInfo {
addr,
client_cert: _,
}) = parts
.extract::<ConnectInfo<ClientConnInfo>>()
.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<ServerState> for TrustedClientIp {
)
})?;
first.parse::<IpAddr>().map(TrustedClientIp).map_err(|_| {
first.parse::<IpAddr>().map_err(|_| {
(
StatusCode::BAD_REQUEST,
"X-Forwarded-For contains invalid ip addr",
)
})
})?
} else {
let ConnectInfo(addr) =
parts
.extract::<ConnectInfo<SocketAddr>>()
.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::<ConnectInfo<SocketAddr>>()
.await
addr.ip()
};
Ok(TrustedClientIp(ip_addr))
}
}
pub struct VerifiedClientInformation(pub ClientAuthInfo);
#[async_trait]
impl FromRequestParts<ServerState> for VerifiedClientInformation {
type Rejection = (StatusCode, &'static str);
#[instrument(level = "debug", skip(state))]
async fn from_request_parts(
parts: &mut Parts,
state: &ServerState,
) -> Result<Self, Self::Rejection> {
let ConnectInfo(ClientConnInfo { addr, client_cert }) = parts
.extract::<ConnectInfo<ClientConnInfo>>()
.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::<IpAddr>().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<ClientCertInfo>,
}
impl Connected<ClientConnInfo> 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,
}
}
}

View file

@ -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<B>(request: Request<B>, next: Next<B>) -> 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<String>,
}
#[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<B>(request: Request<B>, next: Next<B>) -> Response {
let uri = request.uri().path().to_string();
@ -55,21 +44,21 @@ pub async fn are_we_json_yet<B>(request: Request<B>, next: Next<B>) -> 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<B>(
auth: Option<TypedHeader<Authorization<Bearer>>>,
mut request: Request<B>,
next: Next<B>,
) -> Response {
#[instrument(level = "debug", name = "kopid_middleware", skip_all)]
pub async fn kopid_middleware<B>(mut request: Request<B>, next: Next<B>) -> 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

View file

@ -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::<SocketAddr>();
.into_make_service_with_connect_info::<ClientConnInfo>();
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<Router, SocketAddr>,
app: IntoMakeServiceWithConnectInfo<Router, ClientConnInfo>,
) -> 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<Router, SocketAddr>,
// svc: ResponseFuture<Router, ClientConnInfo>,
mut app: IntoMakeServiceWithConnectInfo<Router, ClientConnInfo>,
protocol: Arc<Http>,
) -> 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)

View file

@ -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<FilterInvalid> {
///
pub(crate) async fn oauth2_image_get(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Path(rs_name): Path<String>,
) -> 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Json(auth_req): Json<AuthorisationRequest>,
) -> 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Query(auth_req): Query<AuthorisationRequest>,
) -> 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<AuthoriseResponse, Oauth2Error> = 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Json(consent_req): Json<String>,
) -> 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<ServerState>,
Query(token): Query<ConsentRequestData>,
Extension(kopid): Extension<KOpId>,
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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Form(consent_req): Form<ConsentRequestData>,
) -> Response<Body> {
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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Query(consent_req): Query<ConsentRequestData>,
) -> Response<Body> {
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<Body> {
// 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<ServerState>,
Path(client_id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
) -> Result<Json<OidcToken>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
headers: HeaderMap,
Form(intr_req): Form<AccessTokenIntrospectRequest>,
) -> 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Form(intr_req): Form<TokenRevokeRequest>,
) -> 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 (

File diff suppressed because it is too large Load diff

View file

@ -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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
) -> Result<Json<Vec<ProtoEntry>>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Json(obj): Json<ProtoEntry>,
) -> Result<Json<()>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Json(obj): Json<ProtoEntry>,
) -> Result<Json<()>, 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<ServerState>,
Path(rs_name): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
) -> Result<Json<Option<ProtoEntry>>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Path(rs_name): Path<String>,
) -> Result<Json<Option<String>>, 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<ServerState>,
Path(rs_name): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Json(obj): Json<ProtoEntry>,
) -> Result<Json<()>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Path((rs_name, group)): Path<(String, String)>,
Json(scopes): Json<Vec<String>>,
) -> Result<Json<()>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Path((rs_name, group)): Path<(String, String)>,
) -> Result<Json<()>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Path((rs_name, group)): Path<(String, String)>,
Json(scopes): Json<Vec<String>>,
) -> Result<Json<()>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Path((rs_name, group)): Path<(String, String)>,
) -> Result<Json<()>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Path(rs_name): Path<String>,
) -> Result<Json<()>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Path(rs_name): Path<String>,
) -> Result<Json<()>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Path(rs_name): Path<String>,
mut multipart: axum::extract::Multipart,
) -> Result<Json<()>, 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)

View file

@ -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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
) -> Result<Json<Vec<ProtoEntry>>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Json(obj): Json<ProtoEntry>,
) -> Result<Json<()>, WebError> {
let classes: Vec<String> = 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<ServerState>,
Path(id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
) -> Result<Json<Option<ProtoEntry>>, 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<ServerState>,
Path(id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Json(obj): Json<ProtoEntry>,
) -> Result<Json<()>, 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<ServerState>,
Path(id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
) -> Result<Json<()>, 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<ServerState>,
Path(id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
) -> Result<Json<()>, 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<ServerState>,
Path(id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Json(label): Json<String>,
) -> Result<Json<String>, 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<ServerState>,
Path(id): Path<String>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
) -> Result<Json<()>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
AuthBearer(bearer): AuthBearer,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Json(changes): Json<ScimSyncRequest>,
) -> Result<Json<()>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
AuthBearer(bearer): AuthBearer,
) -> Result<Json<ScimSyncState>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Path((id, attr)): Path<(String, String)>,
) -> Result<Json<Option<Vec<String>>>, 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<ServerState>,
Extension(kopid): Extension<KOpId>,
VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
Path((id, attr)): Path<(String, String)>,
Json(values): Json<Vec<String>>,
) -> Result<Json<()>, 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

View file

@ -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(

View file

@ -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

View file

@ -7,6 +7,7 @@ use time::OffsetDateTime;
pub enum AuditSource {
Internal,
Https(IpAddr),
Ldaps(IpAddr),
}
impl From<Source> for AuditSource {
@ -14,6 +15,7 @@ impl From<Source> for AuditSource {
match value {
Source::Internal => AuditSource::Internal,
Source::Https(ip) => AuditSource::Https(ip),
Source::Ldaps(ip) => AuditSource::Ldaps(ip),
}
}
}

View file

@ -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();

View file

@ -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");

View file

@ -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<Vec<LdapMsg>, 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<LdapBoundToken>,
ip_addr: IpAddr,
eventid: Uuid,
) -> Result<LdapResponseState, OperationError> {
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);

View file

@ -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<AuthMech>),
@ -43,3 +43,49 @@ impl fmt::Debug for AuthState {
}
}
}
#[derive(Debug, Clone)]
pub struct ClientAuthInfo {
pub source: Source,
pub client_cert: Option<ClientCertInfo>,
pub bearer_token: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ClientCertInfo {
pub subject_key_id: Option<Vec<u8>>,
pub cn: Option<String>,
}
#[cfg(test)]
impl From<Source> 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<ClientCertInfo> for ClientAuthInfo {
fn from(value: ClientCertInfo) -> ClientAuthInfo {
ClientAuthInfo {
source: Source::Internal,
client_cert: Some(value),
bearer_token: None,
}
}
}

View file

@ -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<Base64UrlSafeData>,
// 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<AuthorisePermitSuccess, OperationError> {
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<AuthoriseResponse, Oauth2Error> {
@ -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<String> = 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<Url, OperationError> {
@ -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<String>) {
// 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 {

View file

@ -23,7 +23,7 @@ impl<'a> IdmServerAuthTransaction<'a> {
ident: Identity,
issue: AuthIssueSession,
ct: Duration,
source: Source,
client_auth_info: ClientAuthInfo,
) -> Result<AuthResult, OperationError> {
// 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);

View file

@ -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)

View file

@ -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<Identity, OperationError> {
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<UserAuthToken, OperationError> {
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<Token, OperationError> {
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<Identity, OperationError> {
// 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<EntrySealedCommitted>,
ct: Duration,
) -> Result<Identity, OperationError> {
@ -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<Identity, OperationError> {
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<Identity, OperationError> {
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<AuthResult, OperationError> {
// 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<Option<LdapBoundToken>, 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");
}

View file

@ -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
);

View file

@ -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,
};

View file

@ -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<Entry<EntrySealed, EntryCommitted>>) -> 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)
}