Tidy up schema

This commit is contained in:
William Brown 2025-03-28 15:50:07 +10:00
parent b113262357
commit 40cc9932a5
8 changed files with 86 additions and 139 deletions
proto/src
server
lib/src
constants
idm
migration_data/dl10
testkit/tests/testkit

View file

@ -32,6 +32,7 @@ pub enum Attribute {
AcpTargetScope,
ApiTokenSession,
ApplicationPassword,
ApplicationUrl,
AttestedPasskeys,
#[default]
Attr,
@ -267,6 +268,7 @@ impl Attribute {
Attribute::AcpTargetScope => ATTR_ACP_TARGET_SCOPE,
Attribute::ApiTokenSession => ATTR_API_TOKEN_SESSION,
Attribute::ApplicationPassword => ATTR_APPLICATION_PASSWORD,
Attribute::ApplicationUrl => ATTR_APPLICATION_URL,
Attribute::AttestedPasskeys => ATTR_ATTESTED_PASSKEYS,
Attribute::Attr => ATTR_ATTR,
Attribute::AttributeName => ATTR_ATTRIBUTENAME,
@ -454,6 +456,7 @@ impl Attribute {
ATTR_ACP_TARGET_SCOPE => Attribute::AcpTargetScope,
ATTR_API_TOKEN_SESSION => Attribute::ApiTokenSession,
ATTR_APPLICATION_PASSWORD => Attribute::ApplicationPassword,
ATTR_APPLICATION_URL => Attribute::ApplicationUrl,
ATTR_ATTESTED_PASSKEYS => Attribute::AttestedPasskeys,
ATTR_ATTR => Attribute::Attr,
ATTR_ATTRIBUTENAME => Attribute::AttributeName,

View file

@ -72,6 +72,7 @@ pub const ATTR_ACP_SEARCH_ATTR: &str = "acp_search_attr";
pub const ATTR_ACP_TARGET_SCOPE: &str = "acp_targetscope";
pub const ATTR_API_TOKEN_SESSION: &str = "api_token_session";
pub const ATTR_APPLICATION_PASSWORD: &str = "application_password";
pub const ATTR_APPLICATION_URL: &str = "application_url";
pub const ATTR_ATTESTED_PASSKEYS: &str = "attested_passkeys";
pub const ATTR_ATTR: &str = "attr";
pub const ATTR_ATTRIBUTENAME: &str = "attributename";

View file

@ -334,6 +334,7 @@ pub const UUID_SCHEMA_ATTR_ACP_MODIFY_PRESENT_CLASS: Uuid =
uuid!("00000000-0000-0000-0000-ffff00000189");
pub const UUID_SCHEMA_ATTR_ACP_MODIFY_REMOVE_CLASS: Uuid =
uuid!("00000000-0000-0000-0000-ffff00000190");
pub const UUID_SCHEMA_ATTR_APPLICATION_URL: Uuid = uuid!("00000000-0000-0000-0000-ffff00000191");
// System and domain infos
// I'd like to strongly criticise william of the past for making poor choices about these allocations.

View file

@ -255,139 +255,6 @@ mod tests {
const TEST_CURRENT_TIME: u64 = 6000;
// Tests that only the correct combinations of [Account, Person, Application and
// ServiceAccount] classes are allowed.
#[idm_test]
async fn test_idm_application_excludes(idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed) {
let ct = Duration::from_secs(TEST_CURRENT_TIME);
let mut idms_prox_write = idms.proxy_write(ct).await.unwrap();
// ServiceAccount, Application and Person not allowed together
let test_grp_name = "testgroup1";
let test_grp_uuid = Uuid::new_v4();
let e1 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Group.to_value()),
(Attribute::Name, Value::new_iname(test_grp_name)),
(Attribute::Uuid, Value::Uuid(test_grp_uuid))
);
let test_entry_uuid = Uuid::new_v4();
let e2 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Class, EntryClass::ServiceAccount.to_value()),
(Attribute::Class, EntryClass::Application.to_value()),
(Attribute::Class, EntryClass::Person.to_value()),
(Attribute::Name, Value::new_iname("test_app_name")),
(Attribute::Uuid, Value::Uuid(test_entry_uuid)),
(Attribute::Description, Value::new_utf8s("test_app_desc")),
(
Attribute::DisplayName,
Value::new_utf8s("test_app_dispname")
),
(Attribute::LinkedGroup, Value::Refer(test_grp_uuid))
);
let ce = CreateEvent::new_internal(vec![e1, e2]);
let cr = idms_prox_write.qs_write.create(&ce);
assert!(cr.is_err());
// Application and Person not allowed together
let test_grp_name = "testgroup1";
let test_grp_uuid = Uuid::new_v4();
let e1 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Group.to_value()),
(Attribute::Name, Value::new_iname(test_grp_name)),
(Attribute::Uuid, Value::Uuid(test_grp_uuid))
);
let test_entry_uuid = Uuid::new_v4();
let e2 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Class, EntryClass::Application.to_value()),
(Attribute::Class, EntryClass::Person.to_value()),
(Attribute::Name, Value::new_iname("test_app_name")),
(Attribute::Uuid, Value::Uuid(test_entry_uuid)),
(Attribute::Description, Value::new_utf8s("test_app_desc")),
(
Attribute::DisplayName,
Value::new_utf8s("test_app_dispname")
),
(Attribute::LinkedGroup, Value::Refer(test_grp_uuid))
);
let ce = CreateEvent::new_internal(vec![e1, e2]);
let cr = idms_prox_write.qs_write.create(&ce);
assert!(cr.is_err());
// Supplements not satisfied, Application supplements ServiceAccount
let test_grp_name = "testgroup1";
let test_grp_uuid = Uuid::new_v4();
let e1 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Group.to_value()),
(Attribute::Name, Value::new_iname(test_grp_name)),
(Attribute::Uuid, Value::Uuid(test_grp_uuid))
);
let test_entry_uuid = Uuid::new_v4();
let e2 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Class, EntryClass::Application.to_value()),
(Attribute::Name, Value::new_iname("test_app_name")),
(Attribute::Uuid, Value::Uuid(test_entry_uuid)),
(Attribute::Description, Value::new_utf8s("test_app_desc")),
(Attribute::LinkedGroup, Value::Refer(test_grp_uuid))
);
let ce = CreateEvent::new_internal(vec![e1, e2]);
let cr = idms_prox_write.qs_write.create(&ce);
assert!(cr.is_err());
// Supplements not satisfied, Application supplements ServiceAccount
let test_grp_name = "testgroup1";
let test_grp_uuid = Uuid::new_v4();
let e1 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Group.to_value()),
(Attribute::Name, Value::new_iname(test_grp_name)),
(Attribute::Uuid, Value::Uuid(test_grp_uuid))
);
let test_entry_uuid = Uuid::new_v4();
let e2 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Application.to_value()),
(Attribute::Name, Value::new_iname("test_app_name")),
(Attribute::Uuid, Value::Uuid(test_entry_uuid)),
(Attribute::Description, Value::new_utf8s("test_app_desc")),
(Attribute::LinkedGroup, Value::Refer(test_grp_uuid))
);
let ce = CreateEvent::new_internal(vec![e1, e2]);
let cr = idms_prox_write.qs_write.create(&ce);
assert!(cr.is_err());
// Supplements satisfied, Application supplements ServiceAccount
let test_grp_name = "testgroup1";
let test_grp_uuid = Uuid::new_v4();
let e1 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Group.to_value()),
(Attribute::Name, Value::new_iname(test_grp_name)),
(Attribute::Uuid, Value::Uuid(test_grp_uuid))
);
let test_entry_uuid = Uuid::new_v4();
let e2 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Application.to_value()),
(Attribute::Class, EntryClass::ServiceAccount.to_value()),
(Attribute::Name, Value::new_iname("test_app_name")),
(Attribute::Uuid, Value::Uuid(test_entry_uuid)),
(Attribute::Description, Value::new_utf8s("test_app_desc")),
(Attribute::LinkedGroup, Value::Refer(test_grp_uuid))
);
let ce = CreateEvent::new_internal(vec![e1, e2]);
let cr = idms_prox_write.qs_write.create(&ce);
assert!(cr.is_ok());
}
// Tests it is not possible to create an application without the linked group attribute
#[idm_test]
async fn test_idm_application_no_linked_group(
@ -404,6 +271,7 @@ mod tests {
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Class, EntryClass::ServiceAccount.to_value()),
(Attribute::Class, EntryClass::Application.to_value()),
(Attribute::DisplayName, Value::new_utf8s("Application")),
(Attribute::Name, Value::new_iname("test_app_name")),
(Attribute::Uuid, Value::Uuid(test_entry_uuid)),
(Attribute::Description, Value::new_utf8s("test_app_desc")),
@ -547,8 +415,10 @@ mod tests {
let e3 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Class, EntryClass::ServiceAccount.to_value()),
(Attribute::Class, EntryClass::Application.to_value()),
(Attribute::DisplayName, Value::new_utf8s("Application")),
(Attribute::Name, Value::new_iname(test_app_name)),
(Attribute::Uuid, Value::Uuid(test_app_uuid)),
(Attribute::LinkedGroup, Value::Refer(test_grp_uuid))
@ -647,7 +517,9 @@ mod tests {
let e2 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::ServiceAccount.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Class, EntryClass::Application.to_value()),
(Attribute::DisplayName, Value::new_utf8s("Application")),
(Attribute::Name, Value::new_iname("test_app_name")),
(Attribute::Uuid, Value::Uuid(test_entry_uuid)),
(Attribute::Description, Value::new_utf8s("test_app_desc")),

View file

@ -1119,8 +1119,10 @@ mod tests {
let e3 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Class, EntryClass::ServiceAccount.to_value()),
(Attribute::Class, EntryClass::Application.to_value()),
(Attribute::DisplayName, Value::new_utf8s("Application")),
(Attribute::Name, Value::new_iname(app_name)),
(Attribute::Uuid, Value::Uuid(app_uuid)),
(Attribute::LinkedGroup, Value::Refer(grp_uuid))
@ -1283,8 +1285,10 @@ mod tests {
let e3 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Class, EntryClass::ServiceAccount.to_value()),
(Attribute::Class, EntryClass::Application.to_value()),
(Attribute::DisplayName, Value::new_utf8s("Application")),
(Attribute::Name, Value::new_iname("testapp1")),
(Attribute::Uuid, Value::Uuid(app_uuid)),
(Attribute::LinkedGroup, Value::Refer(grp_uuid))
@ -1456,8 +1460,10 @@ mod tests {
let e4 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Class, EntryClass::ServiceAccount.to_value()),
(Attribute::Class, EntryClass::Application.to_value()),
(Attribute::DisplayName, Value::new_utf8s("Application")),
(Attribute::Name, Value::new_iname(app1_name)),
(Attribute::Uuid, Value::Uuid(app1_uuid)),
(Attribute::LinkedGroup, Value::Refer(grp1_uuid))
@ -1465,8 +1471,10 @@ mod tests {
let e5 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Class, EntryClass::ServiceAccount.to_value()),
(Attribute::Class, EntryClass::Application.to_value()),
(Attribute::DisplayName, Value::new_utf8s("Application")),
(Attribute::Name, Value::new_iname(app2_name)),
(Attribute::Uuid, Value::Uuid(app2_uuid)),
(Attribute::LinkedGroup, Value::Refer(grp2_uuid))
@ -1651,8 +1659,10 @@ mod tests {
let e3 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Class, EntryClass::ServiceAccount.to_value()),
(Attribute::Class, EntryClass::Application.to_value()),
(Attribute::DisplayName, Value::new_utf8s("Application")),
(Attribute::Name, Value::new_iname(app1_name)),
(Attribute::Uuid, Value::Uuid(app1_uuid)),
(Attribute::LinkedGroup, Value::Refer(grp1_uuid))
@ -2693,8 +2703,10 @@ mod tests {
let e3 = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Class, EntryClass::ServiceAccount.to_value()),
(Attribute::Class, EntryClass::Application.to_value()),
(Attribute::DisplayName, Value::new_utf8s("Application")),
(Attribute::Name, Value::new_iname(app_name)),
(Attribute::Uuid, Value::Uuid(app_uuid)),
(Attribute::LinkedGroup, Value::Refer(grp_uuid))

View file

@ -105,6 +105,7 @@ pub fn phase_1_schema_attrs() -> Vec<EntryInitNew> {
// DL10
SCHEMA_ATTR_DENIED_NAME_DL10.clone().into(),
SCHEMA_ATTR_LDAP_MAXIMUM_QUERYABLE_ATTRIBUTES.clone().into(),
SCHEMA_ATTR_APPLICATION_URL.clone().into(),
]
}
@ -134,7 +135,7 @@ pub fn phase_2_schema_classes() -> Vec<EntryInitNew> {
SCHEMA_CLASS_CLIENT_CERTIFICATE_DL7.clone().into(),
// DL8
SCHEMA_CLASS_ACCOUNT_POLICY_DL8.clone().into(),
SCHEMA_CLASS_APPLICATION_DL8.clone().into(),
SCHEMA_CLASS_APPLICATION.clone().into(),
SCHEMA_CLASS_PERSON_DL8.clone().into(),
// DL9
SCHEMA_CLASS_OAUTH2_RS_DL9.clone().into(),

View file

@ -729,6 +729,14 @@ pub static ref SCHEMA_ATTR_APPLICATION_PASSWORD_DL8: SchemaAttribute = SchemaAtt
..Default::default()
};
pub static ref SCHEMA_ATTR_APPLICATION_URL: SchemaAttribute = SchemaAttribute {
uuid: UUID_SCHEMA_ATTR_APPLICATION_URL,
name: Attribute::ApplicationUrl,
description: "The URL of an external application".to_string(),
syntax: SyntaxType::Url,
..Default::default()
};
// === classes ===
pub static ref SCHEMA_CLASS_PERSON_DL8: SchemaClass = SchemaClass {
uuid: UUID_SCHEMA_CLASS_PERSON,
@ -838,9 +846,9 @@ pub static ref SCHEMA_CLASS_ACCOUNT_DL5: SchemaClass = SchemaClass {
Attribute::Spn
],
systemsupplements: vec![
EntryClass::OAuth2ResourceServer.into(),
EntryClass::Person.into(),
EntryClass::ServiceAccount.into(),
EntryClass::OAuth2ResourceServer.into(),
],
..Default::default()
};
@ -1082,13 +1090,20 @@ pub static ref SCHEMA_CLASS_CLIENT_CERTIFICATE_DL7: SchemaClass = SchemaClass {
..Default::default()
};
pub static ref SCHEMA_CLASS_APPLICATION_DL8: SchemaClass = SchemaClass {
pub static ref SCHEMA_CLASS_APPLICATION: SchemaClass = SchemaClass {
uuid: UUID_SCHEMA_CLASS_APPLICATION,
name: EntryClass::Application.into(),
description: "The class representing an application".to_string(),
systemmust: vec![Attribute::Name, Attribute::LinkedGroup],
systemmay: vec![Attribute::Description],
systemmust: vec![Attribute::LinkedGroup],
systemmay: vec![
Attribute::ApplicationUrl,
],
// I think this could change before release - I can see a world
// whe we may want an oauth2 application to have application passwords,
// or for this to be it's own thing. But service accounts also don't
// quite do enough, they have api tokens, but that's all we kind
// of want from them?
systemsupplements: vec![EntryClass::ServiceAccount.into()],
..Default::default()
};

View file

@ -1,6 +1,8 @@
use kanidmd_testkit::AsyncTestEnvironment;
use kanidmd_testkit::{AsyncTestEnvironment, IDM_ADMIN_TEST_PASSWORD, IDM_ADMIN_TEST_USER};
use ldap3_client::LdapClientBuilder;
const TEST_PERSON: &str = "user_mcuserton";
#[kanidmd_testkit::test(ldap = true)]
async fn test_ldap_basic_unix_bind(test_env: &AsyncTestEnvironment) {
let ldap_url = test_env.ldap_url.as_ref().unwrap();
@ -14,3 +16,43 @@ async fn test_ldap_basic_unix_bind(test_env: &AsyncTestEnvironment) {
assert_eq!(whoami, Some("u: anonymous@localhost".to_string()));
}
#[kanidmd_testkit::test(ldap = true)]
async fn test_ldap_application_password_basic(test_env: &AsyncTestEnvironment) {
// Remember, this isn't the exhaustive test for application password behaviours,
// those are in the main server. This is just a basic smoke test that the interfaces
// are exposed and work in a basic manner.
let rsclient = test_env.rsclient.new_session().unwrap();
// Create a person
rsclient
.auth_simple_password(IDM_ADMIN_TEST_USER, IDM_ADMIN_TEST_PASSWORD)
.await
.expect("Failed to login as admin");
#[allow(clippy::expect_used)]
rsclient
.idm_person_account_create(TEST_PERSON, TEST_PERSON)
.await
.expect("Failed to create the user");
// Create two applications
// List, get them.
// Login as the person
// Create application passwords
// Check the work.
// Check they can't cross talk.
// Done!
// let ldap_url = test_env.ldap_url.as_ref().unwrap();
// let mut ldap_client = LdapClientBuilder::new(ldap_url).build().await.unwrap();
}