diff --git a/.gitignore b/.gitignore index 1b0d95468..4f632ca32 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ .DS_Store .backup_test.db /target +/insecure **/*.rs.bk test.db diff --git a/designs/idm_rest_layout.rst b/designs/idm_rest_layout.rst new file mode 100644 index 000000000..91ff57c38 --- /dev/null +++ b/designs/idm_rest_layout.rst @@ -0,0 +1,316 @@ +IDM API Design and Layout +------------------------- + +To think about this, we need to look at what the eventual structure of the CLI will look like as +it roughly maps to the same operations. + +The cli layout will be (roughly, not actual final product): + +:: + + - raw + | - search + | - create + | - modify + | - delete + - recycle_bin + | - list + | - display (view, get) + | - search + | - revive (is restore better) + - self + | - display + | - set_credential (--appid/primary, --password, --totp, --webauthn, or combinations?) + | - reset_radius_password + | - add_credential_claim + | - remove_credential_claim + | - set_name + | - set_displayname + | - set_legalname + | - add_sshpublickey + | - remove_sshpublickey + | - modify (with --arg, could we get this from schema) + | - get_radius_android + | - get_radius_ios_macos + | - get_radius_config + - account + | - list + | - display + | - create + | - delete + | - modify + | - reset_credential + | - add_credential_claim + | - remove_credential_claim + | - set_name + | - set_displayname + | - set_legalname + | - enroll_sshpublickey + | - remove_sshpublickey + | - lock + | - unlock + | - expire_at + - group + | - list + | - display + | - create + | - delete + | - modify + | - add_members + | - remove_members + - claims + | - list + | - display + | - create + | - delete + | - modify + | - set_credential_policy + - access_profiles + | - list + | - display + | - create + | - delete + | - modify + | - enable + | - disable + - schema + | - class + | - list + | - get + | - create + | - add_may_attribute + | - add_must_attribute + | - attribute + | - list + | - get + | - create + | - query_class (--may, --must) + - radius + | - TBD + +To support this, I think we need to break the api resources down in a similar pattern. We'd need +to think about how this looks with rest ... + +raw +=== + +This is the server's internal CRUD (well, CSMD) protocol exposed at a low level +for batch operations if required. We could simply have /raw take a list of +the CUD/CMD ops for a real batching system ... + +:: + + /v1/raw/search + POST -> search request + /v1/raw/create + POST -> create request + /v1/raw/modify + POST -> modify request + /v1/raw/delete + POST -> modify request + +account +======= + +:: + + /v1/account/ + GET -> list all account ids + POST -> create new account + /v1/account/{id} + GET -> display account + PUT -> overwrite account attrs + PATCH -> update via diff + DELETE -> delete this account + /v1/account/{id}/_attr/{attr} + GET -> display this attr + PUT -> overwrite this attr value list + POST -> append this list to attr + DELETE -> purge this attr + /v1/account/{id}/_lock + POST -> lock this account until time (or null for permanent) + DELETE -> unlock this account + /v1/account/{id}/_credential + GET -> list the credentials + DELETE -> + /v1/account/{id}/_credential/{id}/_lock + POST -> lock this credential until time (or null for permament) + DELETE -> unlock this account + /v1/account/{id}/_radius + GET -> get the accounts radius credentials + (note: more methods to come to update/reset this credential + /v1/account/{id}/_radius/_token + GET -> let's the radius server get all required details for radius to work + + +self +==== + +Modify and perform actions on self - generally this is an extension of capability +from account and person, but combined to one. + +:: + + /v1/self/ + GET -> view self (aka whoami) + PUT -> overwrite self content + PATCH -> update self via diff + /v1/self/_attr/{attr} + GET -> view self attribute. + PUT -> overwrite attr + POST -> append list of attr + DELETE -> purge attr + /v1/self/_credential + (note: more to come re setting/updating credentials, see account) + /v1/self/_radius/ + GET -> list radius cred + (note: more to come re setting/updating this credential) + /v1/self/_radius/_config + POST -> create new config link w_ secret key? + /v1/self/_radius/_config/{secret_key}/ + GET -> get radius config json (no auth needed, secret_key is OTP) + /v1/self/_radius/_config/{secret_key}/apple + GET -> get radius config profile for apple (secret_key is OTP) + /v1/self/_radius/_config/{secret_key}/android + GET -> get radius config profile for android (secret_key is OTP) + +group +===== + +:: + + /v1/group/ + GET -> list all group ids + POST -> create new group + /v1/group/{id} + GET -> get this group id + PUT -> overwrite group content + PATCH -> update via diff + DELETE -> whole entry + /v1/group/{id}/_attr/{attr} + GET -> get this groups attr + PUT -> overwrite this group attr value list + POST -> append this list to group attr + DELETE -> purge this attr + +schema +====== + +Schema defines how we structure and store attributes, so we need a way to query +this and see what it contains. + +:: + + /v1/schema/ + GET -> list all class and attr types + +:: + + /v1/schema/classtype/ + GET -> list schema class names + POST -> create new class + /v1/schema/classtype/{id} + GET -> list schema class + PUT -> overwrite schema content + PATCH -> update via diff + /v1/schema/classtype/{id}/_attr/{attr} + GET -> list value of attr + PUT -> overwrite attr value + POST -> append list of values to attr + DELETE -> purge attr + +:: + + /v1/schema/attributetype/ + GET -> list schema class names + POST -> create new class + /v1/schema/attributetype/{id} + GET -> list schema class + PUT -> overwrite schema content + PATCH -> update via diff + /v1/schema/attributetype/{id}/_attr/{attr} + GET -> list value of attr + PUT -> overwrite attr value + POST -> append list of values to attr + DELETE -> purge attr + +claims +====== + +TBD + +recycle_bin +=========== + +List and restore from the recycle bin if possible. + +:: + + /v1/recycle_bin/ + GET -> list + /v1/recycle_bin/{id} + GET -> view recycled type + /v1/recycle_bin/{id}/_restore + POST -> restore this id. + +access_profile +============== + +:: + + /v1/access_profiles + GET -> list + POST -> create new acp + /v1/access_profiles/{id} + GET -> display acp + PUT -> overwrite acp + PATCH -> update via diff + DELETE -> delete this acp + /v1/access_profiles/{id}/_attr + GET -> list value of attr + PUT -> overwrite attr value + POST -> append list of values to attr + DELETE -> purge attr + + +References +========== + +Great resource on api design +https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design + +Has a great section on filtering strings that we should implement +https://github.com/Microsoft/api-guidelines/blob/master/Guidelines.md + + +Azure AD api as inspiration. +https://docs.microsoft.com/en-au/previous-versions/azure/ad/graph/api/functions-and-actions#changePassword + +https://docs.microsoft.com/en-au/previous-versions/azure/ad/graph/api/users-operations + +https://docs.microsoft.com/en-au/previous-versions/azure/ad/graph/api/groups-operations + +https://github.com/mozilla-services/fernet-rs/blob/master/src/lib.rs + +Other Notes +=========== + +What about a sudo/temporal claim assignment for pw change instead? +-- temporal claim that requires re-auth to add? +-- similar for self-write? + +claims: +- enforce cred policy +- may not always be granted +- need a reauth+claim request interface +- claims must be able to be scoped by time +- uat signed/tamper proof + - similar when bearer. + +- pw reset links must expire + - url should be a bearer signed containing expiry + + - similar for radius profile view, should have a limited time scope on url. + + + diff --git a/examples/wifi-blackhats.mobileconfig b/examples/wifi-blackhats.mobileconfig new file mode 100644 index 000000000..a67796a85 --- /dev/null +++ b/examples/wifi-blackhats.mobileconfig @@ -0,0 +1,150 @@ + + + + + ConsentText + + default + Consent Message + + PayloadContent + + + AutoJoin + + CaptiveBypass + + EAPClientConfiguration + + AcceptEAPTypes + + 25 + + OuterIdentity + outerident + PayloadCertificateAnchorUUID + + 3507F18D-291F-4C03-885A-F3A33CD3A811 + + TLSMaximumVersion + 1.2 + TLSMinimumVersion + 1.0 + TLSTrustedServerNames + + UserName + username + UserPassword + password + + EncryptionType + WPA2 + HIDDEN_NETWORK + + IsHotspot + + PayloadDescription + Configures Wi-Fi settings + PayloadDisplayName + Wi-Fi + PayloadIdentifier + com.apple.wifi.managed.74AC9A39-1A8F-48D8-A731-5C7D0491365A + PayloadType + com.apple.wifi.managed + PayloadUUID + 74AC9A39-1A8F-48D8-A731-5C7D0491365A + PayloadVersion + 1 + ProxyType + None + SSID_STR + Blackhats + + + PayloadCertificateFileName + radius-ca.crt + PayloadContent + + LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZjVENDQTFt + Z0F3SUJBZ0lGQUs2WXZhTXdEUVlKS29aSWh2Y05BUUVMQlFBd1hq + RUxNQWtHQTFVRUJoTUMKUVZVeEV6QVJCZ05WQkFnVENsRjFaV1Z1 + YzJ4aGJtUXhFVEFQQmdOVkJBY1RDRUp5YVhOaVlXNWxNUkl3RUFZ + RApWUVFLRXdsQ2JHRmphMmhoZEhNeEV6QVJCZ05WQkFNVENtSm9J + R3hrWVhBZ1kyRXdIaGNOTVRnd09UQXhNREl4Ck9ETTRXaGNOTWpB + d09UQXhNREl4T0RNNFdqQmVNUXN3Q1FZRFZRUUdFd0pCVlRFVE1C + RUdBMVVFQ0JNS1VYVmwKWlc1emJHRnVaREVSTUE4R0ExVUVCeE1J + UW5KcGMySmhibVV4RWpBUUJnTlZCQW9UQ1VKc1lXTnJhR0YwY3pF + VApNQkVHQTFVRUF4TUtZbWdnYkdSaGNDQmpZVENDQWlJd0RRWUpL + b1pJaHZjTkFRRUJCUUFEZ2dJUEFEQ0NBZ29DCmdnSUJBTzhYdm1S + YmtkNm5BYlVUZi9KYzd6MWd6ampBbW1pTTJteFdLckl2ZE5DMGY3 + WW94akdWMFhPK3hFRkQKWGMyaHZVY0JTaTd5cEVaaEVhS3NjSU9M + MGVlTEh6U2Nnanh0ZjBiOUZjaXhUbEhVd2FyM05sS0FIUHdKQnpH + UApmOXZNTGQ4dkdFaG8xd09JYkRSc0MzYitiOGg4SVJPNWVtSTlB + WTdqbzJlR3IvT2l3UFhPM2l3R29mcHdtZWFNCnhoa0o0NGNHMm9O + c2JiUjN0bkFMdzVDWm44RXlxbkNOSytERUQvUmhrWmJNSThXbU9N + dllxTFJJejJIOEM2bVoKaGdFSjNRWEpyRFJNYUxwVE41Vm1zRnFF + bnJiWnBadXAyQmFraDAwaXZBblJqNUNRdnBYZXIwQU9weHB5ZXlZ + UQpYNWJXalhVMjM3czE1SVhkeEs5SEhuMHRsVDFoamQ3cUNqWEN6 + eEtxZy9QR2VwU2hZd1F6dzlwUTNmQ20yMzN2CnFYek0zeGQvVW9V + aVdrbkRCK3k2VVpBU3hRdHZETkF1T2QyV0lqUDA1SzRaaHFxSmdD + eWFycDJmbFNMb3IzSGUKVVhVWnByWTh1ckN1L2F4TE5uL1ZEcHFr + d0Rsek95cno2OEl3TEpIeE5CdzVWakltY1V2dEtEQjNMdDJLN0Ft + MwpvdHVEbVByMEw1MTlqeFBsZTlqOHUyMCtsakwzUW01d1FnQTBU + ZWwyZlZDUlF6OVgvNVlOVHBiVE55MXZHK3A3CjZ3Vi9jRUk5b2J2 + cE5vUjZWck4ySjRSZE01dzN2NjlQQ0g5aURPZnRrenYrMWlNVE84 + YWd3OVJrY0F4ZmtzWXYKWHdwUHZhQkR1eEk0NlFNUGpvRk43MXgv + d3AveHN3bmE2UjVUKyt3NGZIOG9qQVEvQWdNQkFBR2pOakEwTUFz + RwpBMVVkRHdRRUF3SUNCREFTQmdOVkhSTUJBZjhFQ0RBR0FRSC9B + Z0VBTUJFR0NXQ0dTQUdHK0VJQkFRUUVBd0lDCkJEQU5CZ2txaGtp + Rzl3MEJBUXNGQUFPQ0FnRUEwUHBtMldWWkJiOXRXUm8yZk9udk52 + NStBNXN2NUltaUJMOEIKYjgxeUdFN2hXZHBQdnlkSGlFSXNqbjJz + U0t5L1FXTlBtZStGb2RqaEVsMVNyYlg1ZkwxMjdOQmpKVmMxYzZD + NgpPVmZOOEpXUVZlOHcvKzFJSTRMRmw2dmlxdEd1K0RBdmJHMWVH + bmVwVEZ4VjBUb25lazA3T3YreGM0TGJ3T29xCmJvYTMySG05alRM + OE95REkrUFUvNmZnZFc5b1EyK1ZCSDNJQkhYV1pudHhWV3R6TDJt + c3dJTzcvcDdDazdzRDQKem9kSnRzMkZOeXdHdjlXT3BDaEIrRGd0 + Zks2dXVySzR2Kzd6UDBpRU9jWSt3V0Q1bEx0Z0I2RGJRVGRHQU1o + MApVNG5DVVRITS9SN3ZxZE5lT093Zlc2YllBVkhJMzluUnN3RWg0 + S3FqWHBITFdDTWJ0Wmw2NU9idEZacCtKS25GCndGWm1FbWk2NjlX + bm1PWkNDMmlrUWg2TXNJd3RhSlFzKzZhTFA1UWptYythTmtYcDM2 + OVNRK0V1SFpUQk0vMjkKczl5cWVkaHN2QkhiTWFjSG45WnI2QzEy + LytDSm5FeWdiSDFZQlFMRGQzL1ZNMlZZYTZyL2VEUkxjZ0pBa2M5 + VApMeHZtYzJjWEhWUVVQMDNyT3N1UFNMUUt6a0hJUlZlQktHNlFZ + RFBlVmRxL1VsOHVDZXcxbFAwb2tLOGJ1M2VECk5iM0YrMUlRWW1T + UUhhaVdtVzYzYXZyWENNS01sMkhCOU9NWjNNdnlQemN5dGNGdHNR + cUVjcGRaQlNRaTZyMWcKVEdLTGVEQ1psRUVYa1NKaktmU2VMN3lz + Z21CT2xBbXp6VmlhS1VJbTQ3T3pZUUhGWFNxb2ZoVzlHdEtJc3hx + aQo5VHdVaE9jPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + + PayloadDescription + Adds a CA root certificate + PayloadDisplayName + bh ldap ca + PayloadIdentifier + com.apple.security.root.3507F18D-291F-4C03-885A-F3A33CD3A811 + PayloadType + com.apple.security.root + PayloadUUID + 3507F18D-291F-4C03-885A-F3A33CD3A811 + PayloadVersion + 1 + + + PayloadDescription + Autogenerated Wifi Profile for Radius + PayloadDisplayName + wifi-blackhats + PayloadIdentifier + kanidm.1CAAD07A-89C7-4A3B-9AAD-C7C1DE7F69AE + PayloadOrganization + blackhats.net.au + PayloadRemovalDisallowed + + PayloadType + Configuration + PayloadUUID + E3A2F3F5-88E9-40B9-985C-1B35BD5314B3 + PayloadVersion + 1 + + diff --git a/kanidmd/src/lib/core.rs b/kanidmd/src/lib/core.rs index af7364176..19b5c579f 100644 --- a/kanidmd/src/lib/core.rs +++ b/kanidmd/src/lib/core.rs @@ -4,6 +4,7 @@ use actix_web::middleware::session::{self, RequestSession}; use actix_web::{ error, http, middleware, App, Error, HttpMessage, HttpRequest, HttpResponse, Result, State, }; +// use actix_web::Path; use bytes::BytesMut; use futures::{future, Future, Stream}; @@ -274,6 +275,29 @@ fn idm_account_set_password( ) } +/* +fn test_resource( + (class, _req, _state): (Path, HttpRequest ,State), +) -> String { + format!("Hello {:?}!", class) +} + +// https://actix.rs/docs/extractors/ +#[derive(Deserialize)] +struct RestResource { + class: String, + id: String, +} +fn test_resource_id( + (r, _req, _state): (Path, HttpRequest ,State), +) -> String { + format!("Hello {:?}/{:?}!", r.class, r.id) +} +*/ + + +// === internal setup helpers + fn setup_backend(config: &Configuration) -> Result { let mut audit_be = AuditScope::new("backend_setup"); let pool_size: u32 = config.threads as u32; @@ -624,17 +648,9 @@ pub fn create_server_core(config: Configuration) { // This forces https only if true .secure(secure_cookies), )) - // .resource("/", |r| r.f(index)) .resource("/v1/whoami", |r| { r.method(http::Method::GET).with_async(whoami) }) - // .resource("/v1/login", ...) - // .resource("/v1/logout", ...) - // .resource("/v1/token", ...) generate a token for id servers to use - // on clients, IE linux machines. Workflow being login -> token - // containing group uuids and information needed, as well as a - // set of data for user stuff - // curl --header "Content-Type: application/json" --request POST --data '{ "entries": [ {"attrs": {"class": ["group"], "name": ["testgroup"], "description": ["testperson"]}}]}' http://127.0.0.1:8080/v1/create .resource("/v1/create", |r| { r.method(http::Method::POST).with_async(create) }) @@ -655,6 +671,15 @@ pub fn create_server_core(config: Configuration) { r.method(http::Method::POST) .with_async(idm_account_set_password) }) + // Test resources + /* + .resource("/v1/account", |r| r.f(|_| "Hello Account")) + .resource("/v1/{class}/{id}", + |r| r.method(http::Method::GET).with(test_resource_id)) + .resource("/v1/{class}", + |r| r.method(http::Method::GET).with(test_resource)) + */ + // Add an ldap compat search function type? /* diff --git a/kanidmd/src/server/main.rs b/kanidmd/src/server/main.rs index 4040d536d..4c1723a4f 100644 --- a/kanidmd/src/server/main.rs +++ b/kanidmd/src/server/main.rs @@ -105,7 +105,7 @@ fn main() { // Configure the server logger. This could be adjusted based on what config // says. if opt.debug() { - ::std::env::set_var("RUST_LOG", "actix_web=info,kanidm=debug"); + ::std::env::set_var("RUST_LOG", "actix_web=debug,kanidm=debug"); } else { ::std::env::set_var("RUST_LOG", "actix_web=info,kanidm=info"); }