diff --git a/libs/client/src/lib.rs b/libs/client/src/lib.rs index d9a393778..ee537f6f8 100644 --- a/libs/client/src/lib.rs +++ b/libs/client/src/lib.rs @@ -204,7 +204,7 @@ impl KanidmClientBuilder { let verify_ca = kcc.verify_ca.unwrap_or(verify_ca); let verify_hostnames = kcc.verify_hostnames.unwrap_or(verify_hostnames); let ca = match kcc.ca_path { - Some(ca_path) => Some(Self::parse_certificate(ca_path.as_str())?), + Some(ca_path) => Some(Self::parse_certificate(&ca_path)?), None => ca, }; @@ -271,7 +271,7 @@ impl KanidmClientBuilder { ClientError::ConfigParseIssue(format!("{:?}", e)) })?; - let config: KanidmClientConfig = toml::from_str(contents.as_str()).map_err(|e| { + let config: KanidmClientConfig = toml::from_str(&contents).map_err(|e| { error!("{:?}", e); ClientError::ConfigParseIssue(format!("{:?}", e)) })?; @@ -396,7 +396,7 @@ impl KanidmClientBuilder { } }; - self.display_warnings(address.as_str()); + self.display_warnings(&address); let client_builder = reqwest::Client::builder() .user_agent(KanidmClientBuilder::user_agent()) @@ -442,13 +442,49 @@ impl KanidmClientBuilder { } } +#[test] +fn test_make_url() { + let client: KanidmClient = KanidmClientBuilder::new() + .address("https://localhost:8080".to_string()) + .build() + .unwrap(); + assert_eq!( + client.get_url(), + Url::parse("https://localhost:8080").unwrap() + ); + assert_eq!( + client.make_url("/hello"), + Url::parse("https://localhost:8080/hello").unwrap() + ); + + let client: KanidmClient = KanidmClientBuilder::new() + .address("https://localhost:8080/cheese/".to_string()) + .build() + .unwrap(); + assert_eq!( + client.make_url("hello"), + Url::parse("https://localhost:8080/cheese/hello").unwrap() + ); +} + impl KanidmClient { pub fn get_origin(&self) -> &Url { &self.origin } - pub fn get_url(&self) -> &str { - self.addr.as_str() + /// Returns the base URL of the server + pub fn get_url(&self) -> Url { + #[allow(clippy::panic)] + match self.addr.parse::() { + Ok(val) => val, + Err(err) => panic!("Failed to parse {} into URL: {:?}", self.addr, err), + } + } + + /// Get a URL based on adding an endpoint to the base URL of the server + pub fn make_url(&self, endpoint: &str) -> Url { + #[allow(clippy::expect_used)] + self.get_url().join(endpoint).expect("Failed to join URL") } pub async fn set_token(&self, new_token: String) { @@ -474,6 +510,7 @@ impl KanidmClient { Ok(()) } + /// Check that we're getting the right version back from the server. async fn expect_version(&self, response: &reqwest::Response) { let mut guard = self.check_version.lock().await; @@ -508,13 +545,11 @@ impl KanidmClient { dest: &str, request: &R, ) -> Result { - let dest = format!("{}{}", self.get_url(), dest); - let req_string = serde_json::to_string(request).map_err(ClientError::JsonEncode)?; let response = self .client - .post(dest.as_str()) + .post(self.make_url(dest)) .body(req_string) .header(CONTENT_TYPE, APPLICATION_JSON); @@ -552,13 +587,12 @@ impl KanidmClient { dest: &str, request: R, ) -> Result { - let dest = format!("{}{}", self.get_url(), dest); trace!("perform_auth_post_request connecting to {}", dest); let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?; let response = self .client - .post(dest.as_str()) + .post(self.make_url(dest)) .body(req_string) .header(CONTENT_TYPE, APPLICATION_JSON); @@ -626,12 +660,10 @@ impl KanidmClient { dest: &str, request: R, ) -> Result { - let dest = format!("{}{}", self.get_url(), dest); - let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?; let response = self .client - .post(dest.as_str()) + .post(self.make_url(dest)) .body(req_string) .header(CONTENT_TYPE, APPLICATION_JSON); @@ -678,13 +710,11 @@ impl KanidmClient { dest: &str, request: R, ) -> Result { - let dest = format!("{}{}", self.get_url(), dest); - let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?; let response = self .client - .put(dest.as_str()) + .put(self.make_url(dest)) .header(CONTENT_TYPE, APPLICATION_JSON); let response = { let tguard = self.bearer_token.read().await; @@ -734,12 +764,10 @@ impl KanidmClient { dest: &str, request: R, ) -> Result { - let dest = format!("{}{}", self.get_url(), dest); - let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?; let response = self .client - .patch(dest.as_str()) + .patch(self.make_url(dest)) .body(req_string) .header(CONTENT_TYPE, APPLICATION_JSON); @@ -782,10 +810,11 @@ impl KanidmClient { } #[instrument(level = "debug", skip(self))] - async fn perform_get_request(&self, dest: &str) -> Result { - let dest = format!("{}{}", self.get_url(), dest); - let response = self.client.get(dest.as_str()); - + pub async fn perform_get_request( + &self, + dest: &str, + ) -> Result { + let response = self.client.get(self.make_url(dest)); let response = { let tguard = self.bearer_token.read().await; if let Some(token) = &(*tguard) { @@ -826,11 +855,9 @@ impl KanidmClient { } async fn perform_delete_request(&self, dest: &str) -> Result<(), ClientError> { - let dest = format!("{}{}", self.get_url(), dest); - let response = self .client - .delete(dest.as_str()) + .delete(self.make_url(dest)) .header(CONTENT_TYPE, APPLICATION_JSON); let response = { @@ -876,12 +903,10 @@ impl KanidmClient { dest: &str, request: R, ) -> Result<(), ClientError> { - let dest = format!("{}{}", self.get_url(), dest); - let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?; let response = self .client - .delete(dest.as_str()) + .delete(self.make_url(dest)) .body(req_string) .header(CONTENT_TYPE, APPLICATION_JSON); @@ -1352,10 +1377,7 @@ impl KanidmClient { } pub async fn whoami(&self) -> Result, ClientError> { - let whoami_dest = [self.addr.as_str(), "/v1/self"].concat(); - // format!("{}/v1/self", self.addr); - debug!("{:?}", whoami_dest); - let response = self.client.get(whoami_dest.as_str()); + let response = self.client.get(self.make_url("/v1/self")); let response = { let tguard = self.bearer_token.read().await; @@ -1429,15 +1451,14 @@ impl KanidmClient { } pub async fn idm_group_get(&self, id: &str) -> Result, ClientError> { - self.perform_get_request(format!("/v1/group/{}", id).as_str()) - .await + self.perform_get_request(&format!("/v1/group/{}", id)).await } pub async fn idm_group_get_members( &self, id: &str, ) -> Result>, ClientError> { - self.perform_get_request(format!("/v1/group/{}/_attr/member", id).as_str()) + self.perform_get_request(&format!("/v1/group/{}/_attr/member", id)) .await } @@ -1457,7 +1478,7 @@ impl KanidmClient { members: &[&str], ) -> Result<(), ClientError> { let m: Vec<_> = members.iter().map(|v| (*v).to_string()).collect(); - self.perform_put_request(format!("/v1/group/{}/_attr/member", id).as_str(), m) + self.perform_put_request(&format!("/v1/group/{}/_attr/member", id), m) .await } @@ -1467,7 +1488,7 @@ impl KanidmClient { members: &[&str], ) -> Result<(), ClientError> { let m: Vec<_> = members.iter().map(|v| (*v).to_string()).collect(); - self.perform_post_request(["/v1/group/", id, "/_attr/member"].concat().as_str(), m) + self.perform_post_request(&format!("/v1/group/{}/_attr/member", id), m) .await } @@ -1477,24 +1498,19 @@ impl KanidmClient { members: &[&str], ) -> Result<(), ClientError> { debug!( - "{}", - &[ - "Asked to remove members ", - &members.join(","), - " from ", - group - ] - .concat() + "Asked to remove members {} from {}", + &members.join(","), + group ); self.perform_delete_request_with_body( - ["/v1/group/", group, "/_attr/member"].concat().as_str(), + &format!("/v1/group/{}/_attr/member", group), &members, ) .await } pub async fn idm_group_purge_members(&self, id: &str) -> Result<(), ClientError> { - self.perform_delete_request(format!("/v1/group/{}/_attr/member", id).as_str()) + self.perform_delete_request(&format!("/v1/group/{}/_attr/member", id)) .await } @@ -1504,26 +1520,24 @@ impl KanidmClient { gidnumber: Option, ) -> Result<(), ClientError> { let gx = GroupUnixExtend { gidnumber }; - self.perform_post_request(format!("/v1/group/{}/_unix", id).as_str(), gx) + self.perform_post_request(&format!("/v1/group/{}/_unix", id), gx) .await } pub async fn idm_group_unix_token_get(&self, id: &str) -> Result { - self.perform_get_request(["/v1/group/", id, "/_unix/_token"].concat().as_str()) + self.perform_get_request(&format!("/v1/group/{}/_unix/_token", id)) .await } pub async fn idm_group_delete(&self, id: &str) -> Result<(), ClientError> { - self.perform_delete_request(["/v1/group/", id].concat().as_str()) + self.perform_delete_request(&format!("/v1/group/{}", id)) .await } // ==== ACCOUNTS pub async fn idm_account_unix_token_get(&self, id: &str) -> Result { - // Format doesn't work in async - // format!("/v1/account/{}/_unix/_token", id).as_str() - self.perform_get_request(["/v1/account/", id, "/_unix/_token"].concat().as_str()) + self.perform_get_request(&format!("/v1/account/{}/_unix/_token", id)) .await } @@ -1535,15 +1549,14 @@ impl KanidmClient { ttl: Option, ) -> Result { if let Some(ttl) = ttl { - self.perform_get_request( - format!("/v1/person/{}/_credential/_update_intent?ttl={}", id, ttl).as_str(), - ) + self.perform_get_request(&format!( + "/v1/person/{}/_credential/_update_intent?ttl={}", + id, ttl + )) .await } else { - self.perform_get_request( - format!("/v1/person/{}/_credential/_update_intent", id).as_str(), - ) - .await + self.perform_get_request(&format!("/v1/person/{}/_credential/_update_intent", id)) + .await } } @@ -1551,7 +1564,7 @@ impl KanidmClient { &self, id: &str, ) -> Result<(CUSessionToken, CUStatus), ClientError> { - self.perform_get_request(format!("/v1/person/{}/_credential/_update", id).as_str()) + self.perform_get_request(&format!("/v1/person/{}/_credential/_update", id)) .await } @@ -1692,7 +1705,7 @@ impl KanidmClient { &self, id: &str, ) -> Result { - self.perform_get_request(format!("/v1/account/{}/_radius/_token", id).as_str()) + self.perform_get_request(&format!("/v1/account/{}/_radius/_token", id)) .await } @@ -1704,7 +1717,7 @@ impl KanidmClient { let req = SingleStringRequest { value: cred.to_string(), }; - self.perform_post_request(["/v1/account/", id, "/_unix/_auth"].concat().as_str(), req) + self.perform_post_request(&format!("/v1/account/{}/_unix/_auth", id), req) .await } @@ -1714,12 +1727,12 @@ impl KanidmClient { id: &str, tag: &str, ) -> Result, ClientError> { - self.perform_get_request(format!("/v1/account/{}/_ssh_pubkeys/{}", id, tag).as_str()) + self.perform_get_request(&format!("/v1/account/{}/_ssh_pubkeys/{}", id, tag)) .await } pub async fn idm_account_get_ssh_pubkeys(&self, id: &str) -> Result, ClientError> { - self.perform_get_request(format!("/v1/account/{}/_ssh_pubkeys", id).as_str()) + self.perform_get_request(&format!("/v1/account/{}/_ssh_pubkeys", id)) .await } @@ -1783,7 +1796,7 @@ impl KanidmClient { &self, id: &str, ) -> Result, ClientError> { - self.perform_get_request(format!("/v1/schema/attributetype/{}", id).as_str()) + self.perform_get_request(&format!("/v1/schema/attributetype/{}", id)) .await } @@ -1792,7 +1805,7 @@ impl KanidmClient { } pub async fn idm_schema_classtype_get(&self, id: &str) -> Result, ClientError> { - self.perform_get_request(format!("/v1/schema/classtype/{}", id).as_str()) + self.perform_get_request(&format!("/v1/schema/classtype/{}", id)) .await } @@ -1802,12 +1815,12 @@ impl KanidmClient { } pub async fn recycle_bin_get(&self, id: &str) -> Result, ClientError> { - self.perform_get_request(format!("/v1/recycle_bin/{}", id).as_str()) + self.perform_get_request(&format!("/v1/recycle_bin/{}", id)) .await } pub async fn recycle_bin_revive(&self, id: &str) -> Result<(), ClientError> { - self.perform_post_request(format!("/v1/recycle_bin/{}/_revive", id).as_str(), ()) + self.perform_post_request(&format!("/v1/recycle_bin/{}/_revive", id), ()) .await } } diff --git a/libs/profiles/build.rs b/libs/profiles/build.rs index ca5ea2f6f..cf715c784 100644 --- a/libs/profiles/build.rs +++ b/libs/profiles/build.rs @@ -6,7 +6,7 @@ use base64::{engine::general_purpose, Engine as _}; // We do this here so it's only actually run and checked once. fn determine_git_rev() -> Option { let path = PathBuf::from("../../"); - let repo = git2::Repository::open(&path).ok()?; + let repo = git2::Repository::open(path).ok()?; let head = repo.head().ok()?; let commit = head.peel_to_commit().ok()?; let mut commit_id = commit.id().to_string(); diff --git a/proto/src/v1.rs b/proto/src/v1.rs index d771668d6..ba65af68f 100644 --- a/proto/src/v1.rs +++ b/proto/src/v1.rs @@ -1,3 +1,5 @@ +#![allow(non_upper_case_globals)] + use num_enum::TryFromPrimitive; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -294,14 +296,6 @@ pub struct Claim { // pub expiry: DateTime } -/* -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Application { - pub name: String, - pub uuid: String, -} -*/ - #[derive(Debug, Serialize, Deserialize, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] #[serde(rename_all = "lowercase")] #[derive(TryFromPrimitive)] diff --git a/scripts/setup_dev_environment.sh b/scripts/setup_dev_environment.sh index 3ed3c2206..1a2fee6eb 100755 --- a/scripts/setup_dev_environment.sh +++ b/scripts/setup_dev_environment.sh @@ -25,31 +25,34 @@ if [ -z "${REMOVE_TEST_DB}" ]; then fi fi - if [ ! -f run_insecure_dev_server.sh ]; then echo "Please run from the server/daemon dir!" exit 1 fi +# defaults +KANIDM_CONFIG_FILE="../../examples/insecure_server.toml" +KANIDM_URL="$(rg origin "${KANIDM_CONFIG_FILE}" | awk '{print $NF}' | tr -d '"')" +KANIDM_CA_PATH="/tmp/kanidm/ca.pem" + # wait for them to shut down the server if it's running... -while true -do - if [ "$(pgrep kanidmd | wc -l )" -eq 0 ]; then +while true; do + if [ "$(pgrep kanidmd | wc -l)" -eq 1 ]; then break fi - echo "Stop the kanidmd server first please!" - sleep 1 -done + echo "Start the kanidmd server first please!" -# defaults -KANIDM_CONFIG="../../examples/insecure_server.toml" -KANIDM_URL="$(rg origin "${KANIDM_CONFIG}" | awk '{print $NF}' | tr -d '"')" -KANIDM_CA_PATH="/tmp/kanidm/ca.pem" + while true; do + echo "Waiting for you to start the server... testing ${KANIDM_URL}" + curl --cacert "${KANIDM_CA_PATH}" -fs "${KANIDM_URL}" >/dev/null && break + sleep 2 + done +done # needed for the CLI tools to do their thing export KANIDM_URL export KANIDM_CA_PATH -export KANIDM_CONFIG +export KANIDM_CONFIG_FILE # string things TEST_USER_NAME="testuser" @@ -68,19 +71,20 @@ if [ "${REMOVE_TEST_DB}" -eq 1 ]; then fi echo "Reset the admin user" -ADMIN_PASS=$(${KANIDMD} recover-account admin -o json 2>&1 | rg recovery | rg result | jq -r .result ) +ADMIN_PASS=$(${KANIDMD} recover-account admin -o json 2>&1 | rg password | jq -r .password) +if [ -z "${ADMIN_PASS}" ] || [ "${ADMIN_PASS}" == "null " ]; then + echo "Failed to reset admin password!" + exit 1 +fi echo "admin pass: '${ADMIN_PASS}'" echo "Reset the idm_admin user" -IDM_ADMIN_PASS=$(${KANIDMD} recover-account idm_admin -o json 2>&1 | rg recovery | rg result | jq -r .result) +IDM_ADMIN_PASS=$(${KANIDMD} recover-account idm_admin -o json 2>&1 | rg password | jq -r .password) +if [ -z "${IDM_ADMIN_PASS}" ] || [ "${IDM_ADMIN_PASS}" == "null " ]; then + echo "Failed to reset admin password!" + exit 1 +fi echo "idm_admin pass: '${IDM_ADMIN_PASS}'" -while true -do - echo "Waiting for you to start the server... testing ${KANIDM_URL}" - curl --cacert "${KANIDM_CA_PATH}" -fs "${KANIDM_URL}" > /dev/null && break - sleep 2 -done - echo "login with admin" ${KANIDM} login -D admin --password "${ADMIN_PASS}" echo "login with idm_admin" @@ -96,7 +100,7 @@ echo "Adding ${TEST_USER_NAME} to ${TEST_GROUP}" ${KANIDM} group add-members "${TEST_GROUP}" "${TEST_USER_NAME}" -D idm_admin echo "Enable experimental UI for admin idm_admin ${TEST_USER_NAME}" -${KANIDM} group add-members idm_ui_enable_experimental_features admin idm_admin "${TEST_USER_NAME}" -D idm_admin +${KANIDM} group add-members idm_ui_enable_experimental_features admin idm_admin "${TEST_USER_NAME}" -D idm_admin # create oauth2 rp echo "Creating the OAuth2 RP" diff --git a/scripts/zypper_fixing.sh b/scripts/zypper_fixing.sh index 973437cd0..0e40bd705 100755 --- a/scripts/zypper_fixing.sh +++ b/scripts/zypper_fixing.sh @@ -2,12 +2,16 @@ # makes sure the repos are configured because the containers are derpy sometimes +set -e + #disable the openh264 repo if [ "$(zypper lr | grep -ci 'repo-openh264')" -eq 1 ]; then - zypper mr -d -f -n 'repo-openh264' + echo "Disabling openh264 repo" + zypper mr -d -f repo-openh264 fi # add the non-oss repo if it doesn't exist +echo "Adding the non-oss repo" if [ "$(zypper lr | grep -c 'repo-non-oss')" -eq 0 ]; then zypper ar -f -n 'Non-OSS' http://download.opensuse.org/tumbleweed/repo/non-oss/ repo-non-oss fi diff --git a/server/core/src/actors/v1_read.rs b/server/core/src/actors/v1_read.rs index 8f5cac5bc..049c8a0bb 100644 --- a/server/core/src/actors/v1_read.rs +++ b/server/core/src/actors/v1_read.rs @@ -618,10 +618,16 @@ impl QueryServerReadV1 { .qs_read .name_to_uuid(uuid_or_name.as_str()) .map_err(|e| { + // sometimes it comes back as empty which is bad, it's safe to start with ` "", + false => &uuid_or_name, + }; admin_info!( err = ?e, "Error resolving {} as gidnumber continuing ...", - uuid_or_name + uuid_or_name_val ); e })?; diff --git a/server/core/src/https/middleware/mod.rs b/server/core/src/https/middleware/mod.rs index f82d95baf..aa1677622 100644 --- a/server/core/src/https/middleware/mod.rs +++ b/server/core/src/https/middleware/mod.rs @@ -70,7 +70,7 @@ pub async fn are_we_json_yet(request: Request, next: Next) -> Response } /// This runs at the start of the request, adding an extension with `KOpId` which has useful things inside it. -#[instrument(name = "request", skip_all)] +#[instrument(name = "kopid_middleware", skip_all, level = "DEBUG")] pub async fn kopid_middleware( auth: Option>>, mut request: Request, @@ -86,10 +86,11 @@ pub async fn kopid_middleware( request.extensions_mut().insert(KOpId { eventid, uat }); let mut response = next.run(request).await; - #[allow(clippy::unwrap_used)] + #[allow(clippy::expect_used)] response.headers_mut().insert( "X-KANIDM-OPID", - HeaderValue::from_str(&eventid.as_hyphenated().to_string()).unwrap(), + HeaderValue::from_str(&eventid.as_hyphenated().to_string()) + .expect("Failed to set X-KANIDM-OPID header in response!"), ); response diff --git a/server/core/src/https/mod.rs b/server/core/src/https/mod.rs index 50219d142..953b9d27c 100644 --- a/server/core/src/https/mod.rs +++ b/server/core/src/https/mod.rs @@ -5,6 +5,7 @@ mod manifest; mod middleware; mod oauth2; mod tests; +mod trace; mod ui; mod v1; mod v1_scim; @@ -37,6 +38,7 @@ use tokio_openssl::SslStream; use futures_util::future::poll_fn; use serde::Serialize; use tokio::net::TcpListener; +use tracing::Level; use std::io::ErrorKind; use std::path::PathBuf; @@ -45,7 +47,7 @@ use std::sync::Arc; use std::{net::SocketAddr, str::FromStr}; use tokio::sync::broadcast; use tower_http::services::ServeDir; -use tower_http::trace::TraceLayer; +use tower_http::trace::{DefaultOnRequest, TraceLayer}; use uuid::Uuid; use crate::CoreAction; @@ -235,6 +237,12 @@ pub async fn create_https_server( } }; + // this sets up the default span which logs the URL etc. + let trace_layer = TraceLayer::new_for_http() + .make_span_with(trace::DefaultMakeSpanKanidmd::new()) + // setting these to trace because all they do is print "started processing request", and we are already doing that enough! + .on_request(DefaultOnRequest::new().level(Level::TRACE)); + let app = app .merge(static_routes) .layer(from_fn_with_state( @@ -244,19 +252,19 @@ pub async fn create_https_server( .layer(from_fn(middleware::version_middleware)) .layer(from_fn( middleware::hsts_header::strict_transport_security_layer, - )) - .layer(TraceLayer::new_for_http()) - // This must be the LAST middleware. - // This is because the last middleware here is the first to be entered and the last - // to be exited, and this middleware sets up ids' and other bits for for logging - // coherence to be maintained. - .layer(from_fn(middleware::kopid_middleware)); + )); // layer which checks the responses have a content-type of JSON when we're in debug mode #[cfg(debug_assertions)] let app = app.layer(from_fn(middleware::are_we_json_yet)); let app = app + .layer(trace_layer) + // This must be the LAST middleware. + // This is because the last middleware here is the first to be entered and the last + // to be exited, and this middleware sets up ids' and other bits for for logging + // coherence to be maintained. + .layer(from_fn(middleware::kopid_middleware)) .with_state(state) // the connect_info bit here lets us pick up the remote address of the client .into_make_service_with_connect_info::(); @@ -345,7 +353,6 @@ async fn server_loop( } } -// #[instrument(name = "handle-connection", level = "debug", skip_all)] /// This handles an individual connection. async fn handle_conn( acceptor: SslAcceptor, diff --git a/server/core/src/https/trace.rs b/server/core/src/https/trace.rs new file mode 100644 index 000000000..fbe49494c --- /dev/null +++ b/server/core/src/https/trace.rs @@ -0,0 +1,37 @@ +//! Reimplementation of [`tower-http`]'s [`DefaultMakeSpan`] that only runs at "INFO" level for our own needs. + +use http::Request; +use tracing::{Level, Span}; + +/// The default way [`Span`]s will be created for [`Trace`]. +/// +/// [`Span`]: tracing::Span +/// [`Trace`]: super::Trace +#[derive(Debug, Clone)] +pub struct DefaultMakeSpanKanidmd {} + +impl DefaultMakeSpanKanidmd { + /// Create a new `DefaultMakeSpanKanidmd`. + pub fn new() -> Self { + Self {} + } +} + +impl Default for DefaultMakeSpanKanidmd { + fn default() -> Self { + Self::new() + } +} + +impl tower_http::trace::MakeSpan for DefaultMakeSpanKanidmd { + #[instrument(name = "handle_request", skip_all)] + fn make_span(&mut self, request: &Request) -> Span { + tracing::span!( + Level::INFO, + "request", + method = %request.method(), + uri = %request.uri(), + version = ?request.version(), + ) + } +} diff --git a/server/core/src/https/v1.rs b/server/core/src/https/v1.rs index aafb489e7..c3c75a4a1 100644 --- a/server/core/src/https/v1.rs +++ b/server/core/src/https/v1.rs @@ -874,7 +874,7 @@ pub async fn account_delete_id_radius( json_rest_event_delete_id_attr(state, id, attr, filter, None, kopid).await } -pub async fn account_get_id_radius_token( +pub async fn account_id_radius_token( State(state): State, Path(id): Path, Extension(kopid): Extension, @@ -893,6 +893,7 @@ pub async fn account_get_id_radius_token( } /// Expects an `AccountUnixExtend` object +#[instrument(name = "account_post_id_unix", level = "INFO", skip(id, state, kopid))] pub async fn account_post_id_unix( State(state): State, Path(id): Path, @@ -906,15 +907,46 @@ pub async fn account_post_id_unix( to_axum_response(res) } -pub async fn account_get_id_unix_token( +#[instrument(name = "account_id_unix_token", level = "INFO", skip_all)] +pub async fn account_id_unix_token( State(state): State, Extension(kopid): Extension, Path(id): Path, ) -> impl IntoResponse { + // no point asking for an empty id + if id.is_empty() { + #[allow(clippy::unwrap_used)] + return Response::builder() + .status(StatusCode::BAD_REQUEST) + .body(Body::empty()) + .unwrap(); + } + let res = state .qe_r_ref .handle_internalunixusertokenread(kopid.uat, id, kopid.eventid) .await; + + if let Err(OperationError::InvalidAccountState(val)) = &res { + // if they're not a posix user we should just hide them + if *val == format!("Missing class: {}", "posixaccount") { + #[allow(clippy::unwrap_used)] + return Response::builder() + .status(StatusCode::NOT_FOUND) + .body(Body::empty()) + .unwrap(); + } + }; + + // the was returning a 500 error which wasn't right + if let Err(OperationError::InvalidValueState) = &res { + #[allow(clippy::unwrap_used)] + return Response::builder() + .status(StatusCode::NOT_FOUND) + .body(Body::empty()) + .unwrap(); + }; + to_axum_response(res) } @@ -1480,8 +1512,8 @@ pub fn router(state: ServerState) -> Router { ) .route( "/v1/person/:id/_radius/_token", - get(account_get_id_radius_token), - ) // TODO: make radius token cacheable + get(account_id_radius_token), + ) // TODO: make this cacheable .route("/v1/person/:id/_unix", post(account_post_id_unix)) .route( "/v1/person/:id/_unix/_credential", @@ -1551,11 +1583,11 @@ pub fn router(state: ServerState) -> Router { ) .route( "/v1/account/:id/_unix/_token", - post(account_get_id_unix_token).get(account_get_id_unix_token), // TODO: make this cacheable + post(account_id_unix_token).get(account_id_unix_token), // TODO: make this cacheable ) .route( "/v1/account/:id/_radius/_token", - post(account_get_id_radius_token).get(account_get_id_radius_token), // TODO: make this cacheable + post(account_id_radius_token).get(account_id_radius_token), // TODO: make this cacheable ) .route( "/v1/account/:id/_ssh_pubkeys", diff --git a/server/testkit/src/lib.rs b/server/testkit/src/lib.rs index fc67e8a59..c0fcca1d6 100644 --- a/server/testkit/src/lib.rs +++ b/server/testkit/src/lib.rs @@ -99,25 +99,24 @@ pub async fn create_user(rsclient: &KanidmClient, id: &str, group_name: &str) { .expect("Failed to create the user"); // Create group and add to user to test read attr: member_of - #[allow(clippy::expect_used)] + #[allow(clippy::panic)] if rsclient .idm_group_get(group_name) .await - .expect("Failed to get group") + .unwrap_or_else(|_| panic!("Failed to get group {}", group_name)) .is_none() { - #[allow(clippy::expect_used)] + #[allow(clippy::panic)] rsclient .idm_group_create(group_name) .await - .expect("Failed to create group"); + .unwrap_or_else(|_| panic!("Failed to create group {}", group_name)); } - - #[allow(clippy::expect_used)] + #[allow(clippy::panic)] rsclient .idm_group_add_members(group_name, &[id]) .await - .expect("Failed to set group membership for user"); + .unwrap_or_else(|_| panic!("Failed to add user {} to group {}", id, group_name)); } pub async fn create_user_with_all_attrs( diff --git a/server/testkit/tests/default_entries.rs b/server/testkit/tests/default_entries.rs index 92327327c..e3e0056c2 100644 --- a/server/testkit/tests/default_entries.rs +++ b/server/testkit/tests/default_entries.rs @@ -554,12 +554,15 @@ async fn test_self_write_mail_priv_people(rsclient: KanidmClient) { #[kanidmd_testkit::test] async fn test_https_robots_txt(rsclient: KanidmClient) { // We need to do manual reqwests here. - let addr = rsclient.get_url(); - let response = match reqwest::get(format!("{}/robots.txt", &addr)).await { + let response = match reqwest::get(rsclient.make_url("/robots.txt")).await { Ok(value) => value, Err(error) => { - panic!("Failed to query {:?} : {:#?}", addr, error); + panic!( + "Failed to query {:?} : {:#?}", + rsclient.make_url("/robots.txt"), + error + ); } }; eprintln!("response: {:#?}", response); @@ -577,9 +580,7 @@ async fn test_https_robots_txt(rsclient: KanidmClient) { // #[kanidmd_testkit::test] // async fn test_https_routemap(rsclient: KanidmClient) { // // We need to do manual reqwests here. -// let addr = rsclient.get_url(); - -// let response = match reqwest::get(format!("{}/v1/routemap", &addr)).await { +// let response = match reqwest::get(rsclient.make_url("/v1/routemap")).await { // Ok(value) => value, // Err(error) => { // panic!("Failed to query {:?} : {:#?}", addr, error); @@ -598,7 +599,7 @@ async fn test_https_robots_txt(rsclient: KanidmClient) { #[kanidmd_testkit::test] async fn test_v1_raw_delete(rsclient: KanidmClient) { // We need to do manual reqwests here. - let addr = rsclient.get_url(); + let client = reqwest::ClientBuilder::new() .danger_accept_invalid_certs(true) .build() @@ -607,7 +608,7 @@ async fn test_v1_raw_delete(rsclient: KanidmClient) { let post_body = serde_json::json!({"filter": "self"}).to_string(); let response = match client - .post(format!("{}/v1/raw/delete", &addr)) + .post(rsclient.make_url("/v1/raw/delete")) .header(CONTENT_TYPE, APPLICATION_JSON) .body(post_body) .send() @@ -615,7 +616,11 @@ async fn test_v1_raw_delete(rsclient: KanidmClient) { { Ok(value) => value, Err(error) => { - panic!("Failed to query {:?} : {:#?}", addr, error); + panic!( + "Failed to query {:?} : {:#?}", + rsclient.make_url("/v1/raw/delete"), + error + ); } }; eprintln!("response: {:#?}", response); @@ -629,16 +634,19 @@ async fn test_v1_raw_delete(rsclient: KanidmClient) { #[kanidmd_testkit::test] async fn test_v1_raw_logout(rsclient: KanidmClient) { // We need to do manual reqwests here. - let addr = rsclient.get_url(); let client = reqwest::ClientBuilder::new() .danger_accept_invalid_certs(true) .build() .unwrap(); - let response = match client.get(format!("{}/v1/logout", &addr)).send().await { + let response = match client.get(rsclient.make_url("/v1/logout")).send().await { Ok(value) => value, Err(error) => { - panic!("Failed to query {:?} : {:#?}", addr, error); + panic!( + "Failed to query {:?} : {:#?}", + rsclient.make_url("/v1/logout"), + error + ); } }; eprintln!("response: {:#?}", response); @@ -652,16 +660,19 @@ async fn test_v1_raw_logout(rsclient: KanidmClient) { #[kanidmd_testkit::test] async fn test_status_endpoint(rsclient: KanidmClient) { // We need to do manual reqwests here. - let addr = rsclient.get_url(); let client = reqwest::ClientBuilder::new() .danger_accept_invalid_certs(true) .build() .unwrap(); - let response = match client.get(format!("{}/status", &addr)).send().await { + let response = match client.get(rsclient.make_url("/status")).send().await { Ok(value) => value, Err(error) => { - panic!("Failed to query {:?} : {:#?}", addr, error); + panic!( + "Failed to query {:?} : {:#?}", + rsclient.make_url("/status"), + error + ); } }; eprintln!("response: {:#?}", response); diff --git a/server/testkit/tests/http_manifest.rs b/server/testkit/tests/http_manifest.rs index 0c9a9e32d..a3a40432c 100644 --- a/server/testkit/tests/http_manifest.rs +++ b/server/testkit/tests/http_manifest.rs @@ -3,13 +3,16 @@ use kanidm_client::KanidmClient; #[kanidmd_testkit::test] async fn test_https_manifest(rsclient: KanidmClient) { // We need to do manual reqwests here. - let addr = rsclient.get_url(); // here we test the /ui/ endpoint which should have the headers - let response = match reqwest::get(format!("{}/manifest.webmanifest", &addr)).await { + let response = match reqwest::get(rsclient.make_url("/manifest.webmanifest")).await { Ok(value) => value, Err(error) => { - panic!("Failed to query {:?} : {:#?}", addr, error); + panic!( + "Failed to query {:?} : {:#?}", + rsclient.make_url("/manifest.webmanifest"), + error + ); } }; eprintln!("response: {:#?}", response); diff --git a/server/testkit/tests/https_middleware.rs b/server/testkit/tests/https_middleware.rs index 4cb97b6ba..2179aa6d2 100644 --- a/server/testkit/tests/https_middleware.rs +++ b/server/testkit/tests/https_middleware.rs @@ -3,13 +3,16 @@ use kanidm_client::KanidmClient; #[kanidmd_testkit::test] async fn test_https_middleware_headers(rsclient: KanidmClient) { // We need to do manual reqwests here. - let addr = rsclient.get_url(); // here we test the /ui/ endpoint which should have the headers - let response = match reqwest::get(format!("{}/ui", &addr)).await { + let response = match reqwest::get(rsclient.make_url("/ui")).await { Ok(value) => value, Err(error) => { - panic!("Failed to query {:?} : {:#?}", addr, error); + panic!( + "Failed to query {:?} : {:#?}", + rsclient.make_url("/ui"), + error + ); } }; eprintln!("response: {:#?}", response); @@ -21,10 +24,14 @@ async fn test_https_middleware_headers(rsclient: KanidmClient) { assert_ne!(response.headers().get("content-security-policy"), None); // here we test the /ui/login endpoint which should have the headers - let response = match reqwest::get(format!("{}/ui/login", &addr)).await { + let response = match reqwest::get(rsclient.make_url("/ui/login")).await { Ok(value) => value, Err(error) => { - panic!("Failed to query {:?} : {:#?}", addr, error); + panic!( + "Failed to query {:?} : {:#?}", + rsclient.make_url("/ui/login"), + error + ); } }; eprintln!("response: {:#?}", response); diff --git a/server/testkit/tests/integration.rs b/server/testkit/tests/integration.rs index aee818260..8d993eab0 100644 --- a/server/testkit/tests/integration.rs +++ b/server/testkit/tests/integration.rs @@ -82,7 +82,7 @@ async fn test_webdriver_user_login(rsclient: kanidm_client::KanidmClient) { handle_error!( c, - c.goto(rsclient.get_url().to_string()).await, + c.goto(&rsclient.get_url().to_string()).await, "Couldn't get URL" ); diff --git a/server/testkit/tests/oauth2_test.rs b/server/testkit/tests/oauth2_test.rs index e8a32dabc..92a1e941a 100644 --- a/server/testkit/tests/oauth2_test.rs +++ b/server/testkit/tests/oauth2_test.rs @@ -121,8 +121,6 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) { .await .expect("No user auth token found"); - let url = rsclient.get_url().to_string(); - // We need a new reqwest client here. // from here, we can now begin what would be a "interaction" to the oauth server. @@ -137,10 +135,7 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) { let response = client .request( reqwest::Method::OPTIONS, - format!( - "{}/oauth2/openid/test_integration/.well-known/openid-configuration", - url - ), + rsclient.make_url("/oauth2/openid/test_integration/.well-known/openid-configuration"), ) .send() .await @@ -156,10 +151,7 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) { assert!(cors_header.eq("*")); let response = client - .get(format!( - "{}/oauth2/openid/test_integration/.well-known/openid-configuration", - url - )) + .get(rsclient.make_url("/oauth2/openid/test_integration/.well-known/openid-configuration")) .send() .await .expect("Failed to send request."); @@ -176,36 +168,24 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) { // Most values are checked in idm/oauth2.rs, but we want to sanity check // the urls here as an extended function smoke test. - assert!( - discovery.issuer == Url::parse(&format!("{}/oauth2/openid/test_integration", url)).unwrap() - ); + assert!(discovery.issuer == rsclient.make_url("/oauth2/openid/test_integration")); - assert!(discovery.authorization_endpoint == Url::parse(&format!("{}/ui/oauth2", url)).unwrap()); + assert!(discovery.authorization_endpoint == rsclient.make_url("/ui/oauth2")); - assert!(discovery.token_endpoint == Url::parse(&format!("{}/oauth2/token", url)).unwrap()); + assert!(discovery.token_endpoint == rsclient.make_url("/oauth2/token")); assert!( discovery.userinfo_endpoint - == Some( - Url::parse(&format!("{}/oauth2/openid/test_integration/userinfo", url)).unwrap() - ) + == Some(rsclient.make_url("/oauth2/openid/test_integration/userinfo")) ); assert!( - discovery.jwks_uri - == Url::parse(&format!( - "{}/oauth2/openid/test_integration/public_key.jwk", - url - )) - .unwrap() + discovery.jwks_uri == rsclient.make_url("/oauth2/openid/test_integration/public_key.jwk") ); // Step 0 - get the jwks public key. let response = client - .get(format!( - "{}/oauth2/openid/test_integration/public_key.jwk", - url - )) + .get(rsclient.make_url("/oauth2/openid/test_integration/public_key.jwk")) .send() .await .expect("Failed to send request."); @@ -231,7 +211,7 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) { let (pkce_code_challenge, pkce_code_verifier) = PkceCodeChallenge::new_random_sha256(); let response = client - .get(format!("{}/oauth2/authorise", url)) + .get(rsclient.make_url("/oauth2/authorise")) .bearer_auth(oauth_test_uat.clone()) .query(&[ ("response_type", "code"), @@ -271,7 +251,7 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) { // state and code. let response = client - .get(format!("{}/oauth2/authorise/permit", url)) + .get(rsclient.make_url("/oauth2/authorise/permit")) .bearer_auth(oauth_test_uat) .query(&[("token", consent_token.as_str())]) .send() @@ -312,7 +292,7 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) { .into(); let response = client - .post(format!("{}/oauth2/token", url)) + .post(rsclient.make_url("/oauth2/token")) .basic_auth("test_integration", Some(client_secret.clone())) .form(&form_req) .send() @@ -339,7 +319,7 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) { }; let response = client - .post(format!("{}/oauth2/token/introspect", url)) + .post(rsclient.make_url("/oauth2/token/introspect")) .basic_auth("test_integration", Some(client_secret)) .form(&intr_request) .send() @@ -381,13 +361,13 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) { // This is mostly checked inside of idm/oauth2.rs. This is more to check the oidc // token and the userinfo endpoints. - assert!(oidc.iss == Url::parse(&format!("{}/oauth2/openid/test_integration", url)).unwrap()); + assert!(oidc.iss == rsclient.make_url("/oauth2/openid/test_integration")); eprintln!("{:?}", oidc.s_claims.email); assert!(oidc.s_claims.email.as_deref() == Some("oauth_test@localhost")); assert!(oidc.s_claims.email_verified == Some(true)); let response = client - .get(format!("{}/oauth2/openid/test_integration/userinfo", url)) + .get(rsclient.make_url("/oauth2/openid/test_integration/userinfo")) .bearer_auth(atr.access_token.clone()) .send() .await @@ -486,8 +466,6 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) { .await .expect("No user auth token found"); - let url = rsclient.get_url().to_string(); - // We need a new reqwest client here. // from here, we can now begin what would be a "interaction" to the oauth server. @@ -500,10 +478,7 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) { // Step 0 - get the jwks public key. let response = client - .get(format!( - "{}/oauth2/openid/test_integration/public_key.jwk", - url - )) + .get(rsclient.make_url("/oauth2/openid/test_integration/public_key.jwk")) .send() .await .expect("Failed to send request."); @@ -528,7 +503,7 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) { let (pkce_code_challenge, pkce_code_verifier) = PkceCodeChallenge::new_random_sha256(); let response = client - .get(format!("{}/oauth2/authorise", url)) + .get(rsclient.make_url("/oauth2/authorise")) .bearer_auth(oauth_test_uat.clone()) .query(&[ ("response_type", "code"), @@ -567,7 +542,7 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) { // Step 2 - we now send the consent get to the server which yields a redirect with a // state and code. let response = client - .get(format!("{}/oauth2/authorise/permit", url)) + .get(rsclient.make_url("/oauth2/authorise/permit")) .bearer_auth(oauth_test_uat) .query(&[("token", consent_token.as_str())]) .send() @@ -611,7 +586,7 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) { }; let response = client - .post(format!("{}/oauth2/token", url)) + .post(rsclient.make_url("/oauth2/token")) .form(&form_req) .send() .await @@ -636,7 +611,7 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) { // This is mostly checked inside of idm/oauth2.rs. This is more to check the oidc // token and the userinfo endpoints. - assert!(oidc.iss == Url::parse(&format!("{}/oauth2/openid/test_integration", url)).unwrap()); + assert!(oidc.iss == rsclient.make_url("/oauth2/openid/test_integration")); eprintln!("{:?}", oidc.s_claims.email); assert!(oidc.s_claims.email.as_deref() == Some("oauth_test@localhost")); assert!(oidc.s_claims.email_verified == Some(true)); @@ -645,7 +620,7 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) { let response = client .request( reqwest::Method::OPTIONS, - format!("{}/oauth2/openid/test_integration/userinfo", url), + rsclient.make_url("/oauth2/openid/test_integration/userinfo"), ) .send() .await @@ -661,7 +636,7 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) { assert!(cors_header.eq("*")); let response = client - .get(format!("{}/oauth2/openid/test_integration/userinfo", url)) + .get(rsclient.make_url("/oauth2/openid/test_integration/userinfo")) .bearer_auth(atr.access_token.clone()) .send() .await @@ -695,7 +670,6 @@ async fn test_oauth2_token_post_bad_bodies(rsclient: KanidmClient) { .await; assert!(res.is_ok()); - let url = rsclient.get_url().to_string(); let client = reqwest::Client::builder() .redirect(reqwest::redirect::Policy::none()) .no_proxy() @@ -704,7 +678,7 @@ async fn test_oauth2_token_post_bad_bodies(rsclient: KanidmClient) { // test for a bad-body request on token let response = client - .post(format!("{}/oauth2/token", url)) + .post(rsclient.make_url("/oauth2/token")) .form(&serde_json::json!({})) // .bearer_auth(atr.access_token.clone()) .send() @@ -715,7 +689,7 @@ async fn test_oauth2_token_post_bad_bodies(rsclient: KanidmClient) { // test for a bad-auth request let response = client - .post(format!("{}/oauth2/token/introspect", url)) + .post(rsclient.make_url("/oauth2/token/introspect")) .form(&serde_json::json!({ "token": "lol" })) .send() .await @@ -731,7 +705,6 @@ async fn test_oauth2_token_revoke_post(rsclient: KanidmClient) { .await; assert!(res.is_ok()); - let url = rsclient.get_url().to_string(); let client = reqwest::Client::builder() .redirect(reqwest::redirect::Policy::none()) .no_proxy() @@ -740,7 +713,7 @@ async fn test_oauth2_token_revoke_post(rsclient: KanidmClient) { // test for a bad-body request on token let response = client - .post(format!("{}/oauth2/token/revoke", url)) + .post(rsclient.make_url("/oauth2/token/revoke")) .form(&serde_json::json!({})) .bearer_auth("lolol") .send() @@ -751,7 +724,7 @@ async fn test_oauth2_token_revoke_post(rsclient: KanidmClient) { // test for a invalid format request on token let response = client - .post(format!("{}/oauth2/token/revoke", url)) + .post(rsclient.make_url("/oauth2/token/revoke")) .json("") .bearer_auth("lolol") .send() @@ -763,7 +736,7 @@ async fn test_oauth2_token_revoke_post(rsclient: KanidmClient) { // test for a bad-body request on token let response = client - .post(format!("{}/oauth2/token/revoke", url)) + .post(rsclient.make_url("/oauth2/token/revoke")) .form(&serde_json::json!({})) .bearer_auth("Basic lolol") .send() @@ -774,7 +747,7 @@ async fn test_oauth2_token_revoke_post(rsclient: KanidmClient) { // test for a bad-body request on token let response = client - .post(format!("{}/oauth2/token/revoke", url)) + .post(rsclient.make_url("/oauth2/token/revoke")) .body(serde_json::json!({}).to_string()) .bearer_auth("Basic lolol") .send() diff --git a/server/testkit/tests/person.rs b/server/testkit/tests/person.rs index 608f7af94..c6eaa848b 100644 --- a/server/testkit/tests/person.rs +++ b/server/testkit/tests/person.rs @@ -6,7 +6,6 @@ use reqwest::header::CONTENT_TYPE; #[kanidmd_testkit::test] async fn test_v1_person_patch(rsclient: KanidmClient) { // We need to do manual reqwests here. - let addr = rsclient.get_url(); let client = reqwest::ClientBuilder::new() .danger_accept_invalid_certs(true) .build() @@ -15,7 +14,7 @@ async fn test_v1_person_patch(rsclient: KanidmClient) { let post_body = serde_json::json!({"attrs": { "email" : "crab@example.com"}}).to_string(); let response = match client - .patch(format!("{}/v1/person/foo", &addr)) + .patch(rsclient.make_url("/v1/person/foo")) .header(CONTENT_TYPE, APPLICATION_JSON) .body(post_body) .send() @@ -23,7 +22,11 @@ async fn test_v1_person_patch(rsclient: KanidmClient) { { Ok(value) => value, Err(error) => { - panic!("Failed to query {:?} : {:#?}", addr, error); + panic!( + "Failed to query {:?} : {:#?}", + rsclient.make_url("/v1/person/foo"), + error + ); } }; eprintln!("response: {:#?}", response); diff --git a/server/testkit/tests/proto_v1_test.rs b/server/testkit/tests/proto_v1_test.rs index c93734b0a..2313b34c8 100644 --- a/server/testkit/tests/proto_v1_test.rs +++ b/server/testkit/tests/proto_v1_test.rs @@ -120,6 +120,7 @@ async fn test_server_search(rsclient: KanidmClient) { // First show we are un-authenticated. let pre_res = rsclient.whoami().await; // This means it was okay whoami, but no uat attached. + println!("Response: {:?}", pre_res); assert!(pre_res.unwrap().is_none()); let res = rsclient diff --git a/server/testkit/tests/scim_test.rs b/server/testkit/tests/scim_test.rs index 01034ff89..0eec9dee9 100644 --- a/server/testkit/tests/scim_test.rs +++ b/server/testkit/tests/scim_test.rs @@ -93,7 +93,6 @@ async fn test_sync_account_lifecycle(rsclient: KanidmClient) { #[kanidmd_testkit::test] async fn test_scim_sync_get(rsclient: KanidmClient) { // We need to do manual reqwests here. - let addr = rsclient.get_url(); let mut headers = reqwest::header::HeaderMap::new(); headers.insert( @@ -107,10 +106,14 @@ async fn test_scim_sync_get(rsclient: KanidmClient) { .build() .unwrap(); // here we test the /ui/ endpoint which should have the headers - let response = match client.get(format!("{}/scim/v1/Sync", addr)).send().await { + let response = match client.get(rsclient.make_url("/scim/v1/Sync")).send().await { Ok(value) => value, Err(error) => { - panic!("Failed to query {:?} : {:#?}", addr, error); + panic!( + "Failed to query {:?} : {:#?}", + rsclient.make_url("/scim/v1/Sync"), + error + ); } }; eprintln!("response: {:#?}", response); diff --git a/server/testkit/tests/self.rs b/server/testkit/tests/self.rs index ad92b8281..86d373657 100644 --- a/server/testkit/tests/self.rs +++ b/server/testkit/tests/self.rs @@ -4,20 +4,23 @@ use kanidm_client::KanidmClient; #[kanidmd_testkit::test] async fn test_v1_self_applinks(rsclient: KanidmClient) { // We need to do manual reqwests here. - let addr = rsclient.get_url(); let client = reqwest::ClientBuilder::new() .danger_accept_invalid_certs(true) .build() .unwrap(); let response = match client - .get(format!("{}/v1/self/_applinks", &addr)) + .get(rsclient.make_url("/v1/self/_applinks")) .send() .await { Ok(value) => value, Err(error) => { - panic!("Failed to query {:?} : {:#?}", addr, error); + panic!( + "Failed to query {:?} : {:#?}", + rsclient.make_url("/v1/self/_applinks"), + error + ); } }; eprintln!("response: {:#?}", response); @@ -31,16 +34,19 @@ async fn test_v1_self_applinks(rsclient: KanidmClient) { #[kanidmd_testkit::test] async fn test_v1_self_whoami_uat(rsclient: KanidmClient) { // We need to do manual reqwests here. - let addr = rsclient.get_url(); let client = reqwest::ClientBuilder::new() .danger_accept_invalid_certs(true) .build() .unwrap(); - let response = match client.get(format!("{}/v1/self/_uat", &addr)).send().await { + let response = match client.get(rsclient.make_url("/v1/self/_uat")).send().await { Ok(value) => value, Err(error) => { - panic!("Failed to query {:?} : {:#?}", addr, error); + panic!( + "Failed to query {:?} : {:#?}", + rsclient.make_url("/v1/self/_uat"), + error + ); } }; eprintln!("response: {:#?}", response); diff --git a/server/testkit/tests/service_account.rs b/server/testkit/tests/service_account.rs index fedbc9888..be0dcaed5 100644 --- a/server/testkit/tests/service_account.rs +++ b/server/testkit/tests/service_account.rs @@ -4,7 +4,6 @@ use kanidm_client::KanidmClient; #[kanidmd_testkit::test] async fn test_v1_service_account_id_attr_attr_delete(rsclient: KanidmClient) { // We need to do manual reqwests here. - let addr = rsclient.get_url(); let client = reqwest::ClientBuilder::new() .danger_accept_invalid_certs(true) .build() @@ -13,13 +12,17 @@ async fn test_v1_service_account_id_attr_attr_delete(rsclient: KanidmClient) { // let post_body = serde_json::json!({"filter": "self"}).to_string(); let response = match client - .delete(format!("{}/v1/service_account/admin/_attr/email", &addr)) + .delete(rsclient.make_url("/v1/service_account/admin/_attr/email")) .send() .await { Ok(value) => value, Err(error) => { - panic!("Failed to query {:?} : {:#?}", addr, error); + panic!( + "Failed to query {:?} : {:#?}", + rsclient.make_url("/v1/service_account/admin/_attr/email"), + error + ); } }; eprintln!("response: {:#?}", response); diff --git a/server/testkit/tests/system.rs b/server/testkit/tests/system.rs index b2328addd..6c5c22095 100644 --- a/server/testkit/tests/system.rs +++ b/server/testkit/tests/system.rs @@ -3,22 +3,24 @@ use kanidm_client::KanidmClient; /// This literally tests that the thing exists and responds in a way we expect, probably worth testing it better... #[kanidmd_testkit::test] async fn test_v1_system_post_attr(rsclient: KanidmClient) { - // We need to do manual reqwests here. - let addr = rsclient.get_url(); let client = reqwest::ClientBuilder::new() .danger_accept_invalid_certs(true) .build() .unwrap(); let response = match client - .post(format!("{}/v1/system/_attr/domain_name", &addr)) + .post(rsclient.make_url("/v1/system/_attr/domain_name")) .json(&serde_json::json!({"filter": "self"})) .send() .await { Ok(value) => value, Err(error) => { - panic!("Failed to query {:?} : {:#?}", addr, error); + panic!( + "Failed to query {:?} : {:#?}", + rsclient.make_url("v1/system/_attr/domain_name"), + error + ); } }; eprintln!("response: {:#?}", response); diff --git a/server/testkit/tests/unix.rs b/server/testkit/tests/unix.rs new file mode 100644 index 000000000..09713b9ee --- /dev/null +++ b/server/testkit/tests/unix.rs @@ -0,0 +1,46 @@ +use kanidm_client::KanidmClient; +use kanidmd_testkit::*; + +#[kanidmd_testkit::test] +async fn account_id_unix_token(rsclient: KanidmClient) { + login_put_admin_idm_admins(&rsclient).await; + + create_user(&rsclient, "group_manager", "idm_group_manage_priv").await; + // create test user without creating new groups + create_user(&rsclient, NOT_ADMIN_TEST_USERNAME, "idm_admins").await; + login_account(&rsclient, "group_manager").await; + + let response = rsclient + .idm_account_unix_token_get(NOT_ADMIN_TEST_USERNAME) + .await; + assert!(response.is_err()); + if let Err(val) = response { + assert!(format!("{:?}", val).contains("404")); + } + + let response = rsclient.idm_account_unix_token_get("lol").await; + assert!(response.is_err()); + if let Err(val) = response { + assert!(format!("{:?}", val).contains("404")); + } + + // testing empty results + let response = rsclient.idm_account_unix_token_get("").await; + assert!(response.is_err()); + if let Err(val) = response { + assert!(format!("{:?}", val).contains("400")); + } + + login_put_admin_idm_admins(&rsclient).await; + + rsclient + .idm_person_account_unix_extend(NOT_ADMIN_TEST_USERNAME, None, None) + .await + .unwrap(); + + // testing NOT_ADMIN_TEST_USERNAME has a token result, since we just added one + assert!(rsclient + .idm_account_unix_token_get(NOT_ADMIN_TEST_USERNAME) + .await + .is_ok()); +} diff --git a/tools/cli/src/cli/person.rs b/tools/cli/src/cli/person.rs index 0962517f8..1aa78a46d 100644 --- a/tools/cli/src/cli/person.rs +++ b/tools/cli/src/cli/person.rs @@ -16,7 +16,6 @@ use qrcode::render::unicode; use qrcode::QrCode; use time::format_description::well_known::Rfc3339; use time::OffsetDateTime; -use url::Url; use uuid::Uuid; use crate::webauthn::get_authenticator; @@ -603,14 +602,7 @@ impl AccountCredential { .await { Ok(cuintent_token) => { - let mut url = match Url::parse(client.get_url()) { - Ok(u) => u, - Err(e) => { - error!("Unable to parse url - {:?}", e); - return; - } - }; - url.set_path("/ui/reset"); + let mut url = client.make_url("/ui/reset"); url.query_pairs_mut() .append_pair("token", cuintent_token.token.as_str()); diff --git a/unix_integration/src/db.rs b/unix_integration/src/db.rs index 019387dc6..89e5b65c1 100644 --- a/unix_integration/src/db.rs +++ b/unix_integration/src/db.rs @@ -78,11 +78,19 @@ pub struct DbTxn<'a> { require_tpm: Option<&'a tpm::TpmConfig>, } +#[derive(Debug)] +/// Errors coming back from the `Db` struct +pub enum DbError { + Sqlite, + Tpm, +} + impl Db { - pub fn new(path: &str, tpm_policy: &TpmPolicy) -> Result { + pub fn new(path: &str, tpm_policy: &TpmPolicy) -> Result { let before = unsafe { umask(0o0027) }; let conn = Connection::open(path).map_err(|e| { error!(err = ?e, "rusqulite error"); + DbError::Sqlite })?; let _ = unsafe { umask(before) }; // We only build a single thread. If we need more than one, we'll @@ -96,7 +104,9 @@ impl Db { let require_tpm = match tpm_policy { TpmPolicy::Ignore => None, TpmPolicy::IfPossible(tcti_str) => Db::tpm_setup_context(tcti_str, &conn).ok(), - TpmPolicy::Required(tcti_str) => Some(Db::tpm_setup_context(tcti_str, &conn)?), + TpmPolicy::Required(tcti_str) => { + Some(Db::tpm_setup_context(tcti_str, &conn).map_err(|_| DbError::Tpm)?) + } }; Ok(Db { @@ -784,16 +794,19 @@ impl<'a> Drop for DbTxn<'a> { #[cfg(not(feature = "tpm"))] pub(crate) mod tpm { - use super::Db; + use super::{Db, DbError}; use rusqlite::Connection; pub struct TpmConfig {} impl Db { - pub fn tpm_setup_context(_tcti_str: &str, _conn: &Connection) -> Result { + pub fn tpm_setup_context( + _tcti_str: &str, + _conn: &Connection, + ) -> Result { warn!("tpm feature is not available in this build"); - Err(()) + Err(DbError::Tpm) } } }