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