diff --git a/kanidm_client/src/lib.rs b/kanidm_client/src/lib.rs index 464e9b2e4..b1f0dbb2a 100644 --- a/kanidm_client/src/lib.rs +++ b/kanidm_client/src/lib.rs @@ -1755,6 +1755,29 @@ impl KanidmClient { .await } + pub async fn idm_oauth2_rs_prefer_short_username(&self, id: &str) -> Result<(), ClientError> { + let mut update_oauth2_rs = Entry { + attrs: BTreeMap::new(), + }; + update_oauth2_rs.attrs.insert( + "oauth2_prefer_short_username".to_string(), + vec!["true".to_string()], + ); + self.perform_patch_request(format!("/v1/oauth2/{}", id).as_str(), update_oauth2_rs) + .await + } + pub async fn idm_oauth2_rs_prefer_spn_username(&self, id: &str) -> Result<(), ClientError> { + let mut update_oauth2_rs = Entry { + attrs: BTreeMap::new(), + }; + update_oauth2_rs.attrs.insert( + "oauth2_prefer_short_username".to_string(), + vec!["false".to_string()], + ); + self.perform_patch_request(format!("/v1/oauth2/{}", id).as_str(), update_oauth2_rs) + .await + } + // ==== recycle bin pub async fn recycle_bin_list(&self) -> Result, ClientError> { self.perform_get_request("/v1/recycle_bin").await diff --git a/kanidm_proto/src/v1.rs b/kanidm_proto/src/v1.rs index 550b55a19..f8d62f511 100644 --- a/kanidm_proto/src/v1.rs +++ b/kanidm_proto/src/v1.rs @@ -333,7 +333,7 @@ pub struct UserAuthToken { // may depend on the client application. pub expiry: time::OffsetDateTime, pub uuid: Uuid, - // pub name: String, + pub name: String, pub displayname: String, pub spn: String, pub mail_primary: Option, diff --git a/kanidm_tools/src/cli/oauth2.rs b/kanidm_tools/src/cli/oauth2.rs index 500d13f3e..cfea4acc8 100644 --- a/kanidm_tools/src/cli/oauth2.rs +++ b/kanidm_tools/src/cli/oauth2.rs @@ -16,6 +16,8 @@ impl Oauth2Opt { Oauth2Opt::DisablePkce(nopt) => nopt.copt.debug, Oauth2Opt::EnableLegacyCrypto(nopt) => nopt.copt.debug, Oauth2Opt::DisableLegacyCrypto(nopt) => nopt.copt.debug, + Oauth2Opt::PreferShortUsername(nopt) => nopt.copt.debug, + Oauth2Opt::PreferSPNUsername(nopt) => nopt.copt.debug, } } @@ -172,6 +174,26 @@ impl Oauth2Opt { Err(e) => error!("Error -> {:?}", e), } } + Oauth2Opt::PreferShortUsername(nopt) => { + let client = nopt.copt.to_client().await; + match client + .idm_oauth2_rs_prefer_short_username(nopt.name.as_str()) + .await + { + Ok(_) => println!("Success"), + Err(e) => error!("Error -> {:?}", e), + } + } + Oauth2Opt::PreferSPNUsername(nopt) => { + let client = nopt.copt.to_client().await; + match client + .idm_oauth2_rs_prefer_spn_username(nopt.name.as_str()) + .await + { + Ok(_) => println!("Success"), + Err(e) => error!("Error -> {:?}", e), + } + } } } } diff --git a/kanidm_tools/src/opt/kanidm.rs b/kanidm_tools/src/opt/kanidm.rs index 198096f51..7cbb81f55 100644 --- a/kanidm_tools/src/opt/kanidm.rs +++ b/kanidm_tools/src/opt/kanidm.rs @@ -239,13 +239,17 @@ pub enum ServiceAccountPosix { pub struct PersonUpdateOpt { #[clap(flatten)] aopts: AccountCommonOpt, - #[clap(long, short, help="Set the legal name for the person.")] + #[clap(long, short, help = "Set the legal name for the person.")] legalname: Option, - #[clap(long, short, help="Set the account name for the person.")] + #[clap(long, short, help = "Set the account name for the person.")] newname: Option, - #[clap(long, short='i', help="Set the display name for the person.")] + #[clap(long, short = 'i', help = "Set the display name for the person.")] displayname: Option, - #[clap(long, short, help="Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'")] + #[clap( + long, + short, + help = "Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'" + )] mail: Option>, #[clap(flatten)] copt: CommonOpt, @@ -339,11 +343,19 @@ pub enum ServiceAccountCredential { pub struct ServiceAccountUpdateOpt { #[clap(flatten)] aopts: AccountCommonOpt, - #[clap(long, short, help="Set the account name for the service account.")] + #[clap(long, short, help = "Set the account name for the service account.")] newname: Option, - #[clap(long, short='i', help="Set the display name for the service account.")] + #[clap( + long, + short = 'i', + help = "Set the display name for the service account." + )] displayname: Option, - #[clap(long, short, help="Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'")] + #[clap( + long, + short, + help = "Set the mail address, can be set multiple times for multiple addresses. The first listed mail address is the 'primary'" + )] mail: Option>, #[clap(flatten)] copt: CommonOpt, @@ -575,10 +587,16 @@ pub enum Oauth2Opt { /// Disable legacy signing crypto on this oauth2 resource server. This is the default. #[clap(name = "disable_legacy_crypto")] DisableLegacyCrypto(Named), + #[clap(name = "prefer_short_username")] + /// Use the 'name' attribute instead of 'spn' for the preferred_username + PreferShortUsername(Named), + #[clap(name = "prefer_spn_username")] + /// Use the 'spn' attribute instead of 'name' for the preferred_username + PreferSPNUsername(Named), } #[derive(Args, Debug)] -pub struct OptSetDomainDisplayName{ +pub struct OptSetDomainDisplayName { #[clap(flatten)] copt: CommonOpt, #[clap(name = "new_display_Name")] @@ -636,7 +654,7 @@ pub enum KanidmClientOpt { /// Actions to manage and view person (user) accounts Person { #[clap(subcommand)] - commands: PersonOpt + commands: PersonOpt, }, /// Actions to manage groups Group { @@ -666,9 +684,7 @@ pub enum KanidmClientOpt { commands: RawOpt, }, /// Print the program version and exit - Version { - - } + Version {}, } #[derive(Debug, clap::Parser)] diff --git a/kanidmd/idm/src/constants/acp.rs b/kanidmd/idm/src/constants/acp.rs index 734297ee7..1215bded4 100644 --- a/kanidmd/idm/src/constants/acp.rs +++ b/kanidmd/idm/src/constants/acp.rs @@ -1183,7 +1183,8 @@ pub const JSON_IDM_HP_ACP_OAUTH2_MANAGE_PRIV_V1: &str = r#"{ "es256_private_key_der", "oauth2_allow_insecure_client_disable_pkce", "rs256_private_key_der", - "oauth2_jwt_legacy_crypto_enable" + "oauth2_jwt_legacy_crypto_enable", + "oauth2_prefer_short_username" ], "acp_modify_removedattr": [ "description", @@ -1197,7 +1198,8 @@ pub const JSON_IDM_HP_ACP_OAUTH2_MANAGE_PRIV_V1: &str = r#"{ "es256_private_key_der", "oauth2_allow_insecure_client_disable_pkce", "rs256_private_key_der", - "oauth2_jwt_legacy_crypto_enable" + "oauth2_jwt_legacy_crypto_enable", + "oauth2_prefer_short_username" ], "acp_modify_presentattr": [ "description", @@ -1207,7 +1209,8 @@ pub const JSON_IDM_HP_ACP_OAUTH2_MANAGE_PRIV_V1: &str = r#"{ "oauth2_rs_scope_map", "oauth2_rs_implicit_scopes", "oauth2_allow_insecure_client_disable_pkce", - "oauth2_jwt_legacy_crypto_enable" + "oauth2_jwt_legacy_crypto_enable", + "oauth2_prefer_short_username" ], "acp_modify_class": [], "acp_create_attr": [ @@ -1219,7 +1222,8 @@ pub const JSON_IDM_HP_ACP_OAUTH2_MANAGE_PRIV_V1: &str = r#"{ "oauth2_rs_scope_map", "oauth2_rs_implicit_scopes", "oauth2_allow_insecure_client_disable_pkce", - "oauth2_jwt_legacy_crypto_enable" + "oauth2_jwt_legacy_crypto_enable", + "oauth2_prefer_short_username" ], "acp_create_class": ["oauth2_resource_server", "oauth2_resource_server_basic", "object"] } diff --git a/kanidmd/idm/src/constants/schema.rs b/kanidmd/idm/src/constants/schema.rs index 5e1b26cea..3526d411f 100644 --- a/kanidmd/idm/src/constants/schema.rs +++ b/kanidmd/idm/src/constants/schema.rs @@ -1018,6 +1018,35 @@ pub const JSON_SCHEMA_ATTR_DYNGROUP_FILTER: &str = r#"{ } }"#; +pub const JSON_SCHEMA_ATTR_OAUTH2_PREFER_SHORT_USERNAME: &str = r#"{ + "attrs": { + "class": [ + "object", + "system", + "attributetype" + ], + "description": [ + "Use 'name' instead of 'spn' in the preferred_username claim" + ], + "index": [], + "unique": [ + "false" + ], + "multivalue": [ + "false" + ], + "attributename": [ + "oauth2_prefer_short_username" + ], + "syntax": [ + "BOOLEAN" + ], + "uuid": [ + "00000000-0000-0000-0000-ffff00000109" + ] + } +}"#; + // === classes === pub const JSON_SCHEMA_CLASS_PERSON: &str = r#" @@ -1344,7 +1373,8 @@ pub const JSON_SCHEMA_CLASS_OAUTH2_RS: &str = r#" "oauth2_rs_implicit_scopes", "oauth2_allow_insecure_client_disable_pkce", "rs256_private_key_der", - "oauth2_jwt_legacy_crypto_enable" + "oauth2_jwt_legacy_crypto_enable", + "oauth2_prefer_short_username" ], "systemmust": [ "oauth2_rs_name", diff --git a/kanidmd/idm/src/constants/uuids.rs b/kanidmd/idm/src/constants/uuids.rs index 48c6204ba..c6430e873 100644 --- a/kanidmd/idm/src/constants/uuids.rs +++ b/kanidmd/idm/src/constants/uuids.rs @@ -184,6 +184,8 @@ pub const UUID_SCHEMA_ATTR_SCOPE: Uuid = uuid!("00000000-0000-0000-0000-ffff0000 pub const UUID_SCHEMA_CLASS_SERVICE_ACCOUNT: Uuid = uuid!("00000000-0000-0000-0000-ffff00000106"); pub const _UUID_SCHEMA_CLASS_DYNGROUP: Uuid = uuid!("00000000-0000-0000-0000-ffff00000107"); pub const _UUID_SCHEMA_ATTR_DYNGROUP_FILTER: Uuid = uuid!("00000000-0000-0000-0000-ffff00000108"); +pub const _UUID_SCHEMA_ATTR_OAUTH2_PREFERR_SHORT_USERNAME: Uuid = + uuid!("00000000-0000-0000-0000-ffff00000109"); // System and domain infos // I'd like to strongly criticise william of the past for making poor choices about these allocations. diff --git a/kanidmd/idm/src/idm/account.rs b/kanidmd/idm/src/idm/account.rs index dbe49c71e..b4f51b6ea 100644 --- a/kanidmd/idm/src/idm/account.rs +++ b/kanidmd/idm/src/idm/account.rs @@ -202,7 +202,7 @@ impl Account { auth_type, expiry, uuid: self.uuid, - // name: self.name.clone(), + name: self.name.clone(), displayname: self.displayname.clone(), spn: self.spn.clone(), mail_primary: self.mail_primary.clone(), diff --git a/kanidmd/idm/src/idm/oauth2.rs b/kanidmd/idm/src/idm/oauth2.rs index 7dcbbb39b..5f944118c 100644 --- a/kanidmd/idm/src/idm/oauth2.rs +++ b/kanidmd/idm/src/idm/oauth2.rs @@ -205,6 +205,7 @@ pub struct Oauth2RS { userinfo_endpoint: Url, jwks_uri: Url, scopes_supported: Vec, + prefer_short_username: bool, } impl std::fmt::Debug for Oauth2RS { @@ -367,6 +368,10 @@ impl<'a> Oauth2ResourceServersWriteTransaction<'a> { .map(|e| !e) .unwrap_or(true); + let prefer_short_username = ent + .get_ava_single_bool("oauth2_prefer_short_username") + .unwrap_or(false); + let mut authorization_endpoint = self.inner.origin.clone(); authorization_endpoint.set_path("/ui/oauth2"); @@ -408,6 +413,7 @@ impl<'a> Oauth2ResourceServersWriteTransaction<'a> { userinfo_endpoint, jwks_uri, scopes_supported, + prefer_short_username, }; Ok((client_id, rscfg)) @@ -935,6 +941,13 @@ impl Oauth2ResourceServersReadTransaction { (None, None) }; + admin_warn!("prefer_short_username: {:?}", o2rs.prefer_short_username); + let preferred_username = if o2rs.prefer_short_username { + Some(code_xchg.uat.name.clone()) + } else { + Some(code_xchg.uat.spn.clone()) + }; + // TODO: If max_age was requested in the request, we MUST provide auth_time. // amr == auth method @@ -963,10 +976,10 @@ impl Oauth2ResourceServersReadTransaction { // Map from displayname name: Some(code_xchg.uat.displayname.clone()), // Map from spn - preferred_username: Some(code_xchg.uat.spn.clone()), scopes: code_xchg.scopes.clone(), email, email_verified, + preferred_username, ..Default::default() }, claims: Default::default(), diff --git a/kanidmd/idm/src/server.rs b/kanidmd/idm/src/server.rs index 44746c11f..d8016e60b 100644 --- a/kanidmd/idm/src/server.rs +++ b/kanidmd/idm/src/server.rs @@ -2646,6 +2646,7 @@ impl<'a> QueryServerWriteTransaction<'a> { JSON_SCHEMA_CLASS_OAUTH2_RS, JSON_SCHEMA_CLASS_OAUTH2_RS_BASIC, JSON_SCHEMA_ATTR_NSUNIQUEID, + JSON_SCHEMA_ATTR_OAUTH2_PREFER_SHORT_USERNAME, ]; let r = idm_schema