error handling and web server logging fixes (#1960)

* Fixing the setup_dev_environment script
* clippy calming
* handle_internalunixusertokenread throwing 500's without context
Fixes #1958
This commit is contained in:
James Hodgkinson 2023-08-14 20:47:49 +10:00 committed by GitHub
parent aba9f6a724
commit 83f189fed3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 399 additions and 239 deletions

View file

@ -204,7 +204,7 @@ impl KanidmClientBuilder {
let verify_ca = kcc.verify_ca.unwrap_or(verify_ca); let verify_ca = kcc.verify_ca.unwrap_or(verify_ca);
let verify_hostnames = kcc.verify_hostnames.unwrap_or(verify_hostnames); let verify_hostnames = kcc.verify_hostnames.unwrap_or(verify_hostnames);
let ca = match kcc.ca_path { 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, None => ca,
}; };
@ -271,7 +271,7 @@ impl KanidmClientBuilder {
ClientError::ConfigParseIssue(format!("{:?}", e)) 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); error!("{:?}", e);
ClientError::ConfigParseIssue(format!("{:?}", 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() let client_builder = reqwest::Client::builder()
.user_agent(KanidmClientBuilder::user_agent()) .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 { impl KanidmClient {
pub fn get_origin(&self) -> &Url { pub fn get_origin(&self) -> &Url {
&self.origin &self.origin
} }
pub fn get_url(&self) -> &str { /// Returns the base URL of the server
self.addr.as_str() pub fn get_url(&self) -> Url {
#[allow(clippy::panic)]
match self.addr.parse::<Url>() {
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) { pub async fn set_token(&self, new_token: String) {
@ -474,6 +510,7 @@ impl KanidmClient {
Ok(()) Ok(())
} }
/// Check that we're getting the right version back from the server.
async fn expect_version(&self, response: &reqwest::Response) { async fn expect_version(&self, response: &reqwest::Response) {
let mut guard = self.check_version.lock().await; let mut guard = self.check_version.lock().await;
@ -508,13 +545,11 @@ impl KanidmClient {
dest: &str, dest: &str,
request: &R, request: &R,
) -> Result<T, ClientError> { ) -> Result<T, ClientError> {
let dest = format!("{}{}", self.get_url(), dest);
let req_string = serde_json::to_string(request).map_err(ClientError::JsonEncode)?; let req_string = serde_json::to_string(request).map_err(ClientError::JsonEncode)?;
let response = self let response = self
.client .client
.post(dest.as_str()) .post(self.make_url(dest))
.body(req_string) .body(req_string)
.header(CONTENT_TYPE, APPLICATION_JSON); .header(CONTENT_TYPE, APPLICATION_JSON);
@ -552,13 +587,12 @@ impl KanidmClient {
dest: &str, dest: &str,
request: R, request: R,
) -> Result<T, ClientError> { ) -> Result<T, ClientError> {
let dest = format!("{}{}", self.get_url(), dest);
trace!("perform_auth_post_request connecting to {}", dest); trace!("perform_auth_post_request connecting to {}", dest);
let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?; let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?;
let response = self let response = self
.client .client
.post(dest.as_str()) .post(self.make_url(dest))
.body(req_string) .body(req_string)
.header(CONTENT_TYPE, APPLICATION_JSON); .header(CONTENT_TYPE, APPLICATION_JSON);
@ -626,12 +660,10 @@ impl KanidmClient {
dest: &str, dest: &str,
request: R, request: R,
) -> Result<T, ClientError> { ) -> Result<T, ClientError> {
let dest = format!("{}{}", self.get_url(), dest);
let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?; let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?;
let response = self let response = self
.client .client
.post(dest.as_str()) .post(self.make_url(dest))
.body(req_string) .body(req_string)
.header(CONTENT_TYPE, APPLICATION_JSON); .header(CONTENT_TYPE, APPLICATION_JSON);
@ -678,13 +710,11 @@ impl KanidmClient {
dest: &str, dest: &str,
request: R, request: R,
) -> Result<T, ClientError> { ) -> Result<T, ClientError> {
let dest = format!("{}{}", self.get_url(), dest);
let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?; let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?;
let response = self let response = self
.client .client
.put(dest.as_str()) .put(self.make_url(dest))
.header(CONTENT_TYPE, APPLICATION_JSON); .header(CONTENT_TYPE, APPLICATION_JSON);
let response = { let response = {
let tguard = self.bearer_token.read().await; let tguard = self.bearer_token.read().await;
@ -734,12 +764,10 @@ impl KanidmClient {
dest: &str, dest: &str,
request: R, request: R,
) -> Result<T, ClientError> { ) -> Result<T, ClientError> {
let dest = format!("{}{}", self.get_url(), dest);
let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?; let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?;
let response = self let response = self
.client .client
.patch(dest.as_str()) .patch(self.make_url(dest))
.body(req_string) .body(req_string)
.header(CONTENT_TYPE, APPLICATION_JSON); .header(CONTENT_TYPE, APPLICATION_JSON);
@ -782,10 +810,11 @@ impl KanidmClient {
} }
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
async fn perform_get_request<T: DeserializeOwned>(&self, dest: &str) -> Result<T, ClientError> { pub async fn perform_get_request<T: DeserializeOwned>(
let dest = format!("{}{}", self.get_url(), dest); &self,
let response = self.client.get(dest.as_str()); dest: &str,
) -> Result<T, ClientError> {
let response = self.client.get(self.make_url(dest));
let response = { let response = {
let tguard = self.bearer_token.read().await; let tguard = self.bearer_token.read().await;
if let Some(token) = &(*tguard) { if let Some(token) = &(*tguard) {
@ -826,11 +855,9 @@ impl KanidmClient {
} }
async fn perform_delete_request(&self, dest: &str) -> Result<(), ClientError> { async fn perform_delete_request(&self, dest: &str) -> Result<(), ClientError> {
let dest = format!("{}{}", self.get_url(), dest);
let response = self let response = self
.client .client
.delete(dest.as_str()) .delete(self.make_url(dest))
.header(CONTENT_TYPE, APPLICATION_JSON); .header(CONTENT_TYPE, APPLICATION_JSON);
let response = { let response = {
@ -876,12 +903,10 @@ impl KanidmClient {
dest: &str, dest: &str,
request: R, request: R,
) -> Result<(), ClientError> { ) -> Result<(), ClientError> {
let dest = format!("{}{}", self.get_url(), dest);
let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?; let req_string = serde_json::to_string(&request).map_err(ClientError::JsonEncode)?;
let response = self let response = self
.client .client
.delete(dest.as_str()) .delete(self.make_url(dest))
.body(req_string) .body(req_string)
.header(CONTENT_TYPE, APPLICATION_JSON); .header(CONTENT_TYPE, APPLICATION_JSON);
@ -1352,10 +1377,7 @@ impl KanidmClient {
} }
pub async fn whoami(&self) -> Result<Option<Entry>, ClientError> { pub async fn whoami(&self) -> Result<Option<Entry>, ClientError> {
let whoami_dest = [self.addr.as_str(), "/v1/self"].concat(); let response = self.client.get(self.make_url("/v1/self"));
// format!("{}/v1/self", self.addr);
debug!("{:?}", whoami_dest);
let response = self.client.get(whoami_dest.as_str());
let response = { let response = {
let tguard = self.bearer_token.read().await; let tguard = self.bearer_token.read().await;
@ -1429,15 +1451,14 @@ impl KanidmClient {
} }
pub async fn idm_group_get(&self, id: &str) -> Result<Option<Entry>, ClientError> { pub async fn idm_group_get(&self, id: &str) -> Result<Option<Entry>, ClientError> {
self.perform_get_request(format!("/v1/group/{}", id).as_str()) self.perform_get_request(&format!("/v1/group/{}", id)).await
.await
} }
pub async fn idm_group_get_members( pub async fn idm_group_get_members(
&self, &self,
id: &str, id: &str,
) -> Result<Option<Vec<String>>, ClientError> { ) -> Result<Option<Vec<String>>, ClientError> {
self.perform_get_request(format!("/v1/group/{}/_attr/member", id).as_str()) self.perform_get_request(&format!("/v1/group/{}/_attr/member", id))
.await .await
} }
@ -1457,7 +1478,7 @@ impl KanidmClient {
members: &[&str], members: &[&str],
) -> Result<(), ClientError> { ) -> Result<(), ClientError> {
let m: Vec<_> = members.iter().map(|v| (*v).to_string()).collect(); 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 .await
} }
@ -1467,7 +1488,7 @@ impl KanidmClient {
members: &[&str], members: &[&str],
) -> Result<(), ClientError> { ) -> Result<(), ClientError> {
let m: Vec<_> = members.iter().map(|v| (*v).to_string()).collect(); 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 .await
} }
@ -1477,24 +1498,19 @@ impl KanidmClient {
members: &[&str], members: &[&str],
) -> Result<(), ClientError> { ) -> Result<(), ClientError> {
debug!( debug!(
"{}", "Asked to remove members {} from {}",
&[
"Asked to remove members ",
&members.join(","), &members.join(","),
" from ",
group group
]
.concat()
); );
self.perform_delete_request_with_body( self.perform_delete_request_with_body(
["/v1/group/", group, "/_attr/member"].concat().as_str(), &format!("/v1/group/{}/_attr/member", group),
&members, &members,
) )
.await .await
} }
pub async fn idm_group_purge_members(&self, id: &str) -> Result<(), ClientError> { 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 .await
} }
@ -1504,26 +1520,24 @@ impl KanidmClient {
gidnumber: Option<u32>, gidnumber: Option<u32>,
) -> Result<(), ClientError> { ) -> Result<(), ClientError> {
let gx = GroupUnixExtend { gidnumber }; 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 .await
} }
pub async fn idm_group_unix_token_get(&self, id: &str) -> Result<UnixGroupToken, ClientError> { pub async fn idm_group_unix_token_get(&self, id: &str) -> Result<UnixGroupToken, ClientError> {
self.perform_get_request(["/v1/group/", id, "/_unix/_token"].concat().as_str()) self.perform_get_request(&format!("/v1/group/{}/_unix/_token", id))
.await .await
} }
pub async fn idm_group_delete(&self, id: &str) -> Result<(), ClientError> { 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 .await
} }
// ==== ACCOUNTS // ==== ACCOUNTS
pub async fn idm_account_unix_token_get(&self, id: &str) -> Result<UnixUserToken, ClientError> { pub async fn idm_account_unix_token_get(&self, id: &str) -> Result<UnixUserToken, ClientError> {
// Format doesn't work in async self.perform_get_request(&format!("/v1/account/{}/_unix/_token", id))
// format!("/v1/account/{}/_unix/_token", id).as_str()
self.perform_get_request(["/v1/account/", id, "/_unix/_token"].concat().as_str())
.await .await
} }
@ -1535,14 +1549,13 @@ impl KanidmClient {
ttl: Option<u32>, ttl: Option<u32>,
) -> Result<CUIntentToken, ClientError> { ) -> Result<CUIntentToken, ClientError> {
if let Some(ttl) = ttl { if let Some(ttl) = ttl {
self.perform_get_request( self.perform_get_request(&format!(
format!("/v1/person/{}/_credential/_update_intent?ttl={}", id, ttl).as_str(), "/v1/person/{}/_credential/_update_intent?ttl={}",
) id, ttl
))
.await .await
} else { } else {
self.perform_get_request( self.perform_get_request(&format!("/v1/person/{}/_credential/_update_intent", id))
format!("/v1/person/{}/_credential/_update_intent", id).as_str(),
)
.await .await
} }
} }
@ -1551,7 +1564,7 @@ impl KanidmClient {
&self, &self,
id: &str, id: &str,
) -> Result<(CUSessionToken, CUStatus), ClientError> { ) -> 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 .await
} }
@ -1692,7 +1705,7 @@ impl KanidmClient {
&self, &self,
id: &str, id: &str,
) -> Result<RadiusAuthToken, ClientError> { ) -> Result<RadiusAuthToken, ClientError> {
self.perform_get_request(format!("/v1/account/{}/_radius/_token", id).as_str()) self.perform_get_request(&format!("/v1/account/{}/_radius/_token", id))
.await .await
} }
@ -1704,7 +1717,7 @@ impl KanidmClient {
let req = SingleStringRequest { let req = SingleStringRequest {
value: cred.to_string(), 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 .await
} }
@ -1714,12 +1727,12 @@ impl KanidmClient {
id: &str, id: &str,
tag: &str, tag: &str,
) -> Result<Option<String>, ClientError> { ) -> Result<Option<String>, 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 .await
} }
pub async fn idm_account_get_ssh_pubkeys(&self, id: &str) -> Result<Vec<String>, ClientError> { pub async fn idm_account_get_ssh_pubkeys(&self, id: &str) -> Result<Vec<String>, ClientError> {
self.perform_get_request(format!("/v1/account/{}/_ssh_pubkeys", id).as_str()) self.perform_get_request(&format!("/v1/account/{}/_ssh_pubkeys", id))
.await .await
} }
@ -1783,7 +1796,7 @@ impl KanidmClient {
&self, &self,
id: &str, id: &str,
) -> Result<Option<Entry>, ClientError> { ) -> Result<Option<Entry>, ClientError> {
self.perform_get_request(format!("/v1/schema/attributetype/{}", id).as_str()) self.perform_get_request(&format!("/v1/schema/attributetype/{}", id))
.await .await
} }
@ -1792,7 +1805,7 @@ impl KanidmClient {
} }
pub async fn idm_schema_classtype_get(&self, id: &str) -> Result<Option<Entry>, ClientError> { pub async fn idm_schema_classtype_get(&self, id: &str) -> Result<Option<Entry>, ClientError> {
self.perform_get_request(format!("/v1/schema/classtype/{}", id).as_str()) self.perform_get_request(&format!("/v1/schema/classtype/{}", id))
.await .await
} }
@ -1802,12 +1815,12 @@ impl KanidmClient {
} }
pub async fn recycle_bin_get(&self, id: &str) -> Result<Option<Entry>, ClientError> { pub async fn recycle_bin_get(&self, id: &str) -> Result<Option<Entry>, ClientError> {
self.perform_get_request(format!("/v1/recycle_bin/{}", id).as_str()) self.perform_get_request(&format!("/v1/recycle_bin/{}", id))
.await .await
} }
pub async fn recycle_bin_revive(&self, id: &str) -> Result<(), ClientError> { 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 .await
} }
} }

View file

@ -6,7 +6,7 @@ use base64::{engine::general_purpose, Engine as _};
// We do this here so it's only actually run and checked once. // We do this here so it's only actually run and checked once.
fn determine_git_rev() -> Option<String> { fn determine_git_rev() -> Option<String> {
let path = PathBuf::from("../../"); let path = PathBuf::from("../../");
let repo = git2::Repository::open(&path).ok()?; let repo = git2::Repository::open(path).ok()?;
let head = repo.head().ok()?; let head = repo.head().ok()?;
let commit = head.peel_to_commit().ok()?; let commit = head.peel_to_commit().ok()?;
let mut commit_id = commit.id().to_string(); let mut commit_id = commit.id().to_string();

View file

@ -1,3 +1,5 @@
#![allow(non_upper_case_globals)]
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
@ -294,14 +296,6 @@ pub struct Claim {
// pub expiry: DateTime // 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)] #[derive(Debug, Serialize, Deserialize, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
#[derive(TryFromPrimitive)] #[derive(TryFromPrimitive)]

View file

@ -25,31 +25,34 @@ if [ -z "${REMOVE_TEST_DB}" ]; then
fi fi
fi fi
if [ ! -f run_insecure_dev_server.sh ]; then if [ ! -f run_insecure_dev_server.sh ]; then
echo "Please run from the server/daemon dir!" echo "Please run from the server/daemon dir!"
exit 1 exit 1
fi 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... # wait for them to shut down the server if it's running...
while true while true; do
do if [ "$(pgrep kanidmd | wc -l)" -eq 1 ]; then
if [ "$(pgrep kanidmd | wc -l )" -eq 0 ]; then
break break
fi fi
echo "Stop the kanidmd server first please!" echo "Start the kanidmd server first please!"
sleep 1
done
# defaults while true; do
KANIDM_CONFIG="../../examples/insecure_server.toml" echo "Waiting for you to start the server... testing ${KANIDM_URL}"
KANIDM_URL="$(rg origin "${KANIDM_CONFIG}" | awk '{print $NF}' | tr -d '"')" curl --cacert "${KANIDM_CA_PATH}" -fs "${KANIDM_URL}" >/dev/null && break
KANIDM_CA_PATH="/tmp/kanidm/ca.pem" sleep 2
done
done
# needed for the CLI tools to do their thing # needed for the CLI tools to do their thing
export KANIDM_URL export KANIDM_URL
export KANIDM_CA_PATH export KANIDM_CA_PATH
export KANIDM_CONFIG export KANIDM_CONFIG_FILE
# string things # string things
TEST_USER_NAME="testuser" TEST_USER_NAME="testuser"
@ -68,19 +71,20 @@ if [ "${REMOVE_TEST_DB}" -eq 1 ]; then
fi fi
echo "Reset the admin user" 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 "admin pass: '${ADMIN_PASS}'"
echo "Reset the idm_admin user" 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}'" 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" echo "login with admin"
${KANIDM} login -D admin --password "${ADMIN_PASS}" ${KANIDM} login -D admin --password "${ADMIN_PASS}"
echo "login with idm_admin" echo "login with idm_admin"

View file

@ -2,12 +2,16 @@
# makes sure the repos are configured because the containers are derpy sometimes # makes sure the repos are configured because the containers are derpy sometimes
set -e
#disable the openh264 repo #disable the openh264 repo
if [ "$(zypper lr | grep -ci 'repo-openh264')" -eq 1 ]; then 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 fi
# add the non-oss repo if it doesn't exist # 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 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 zypper ar -f -n 'Non-OSS' http://download.opensuse.org/tumbleweed/repo/non-oss/ repo-non-oss
fi fi

View file

@ -618,10 +618,16 @@ impl QueryServerReadV1 {
.qs_read .qs_read
.name_to_uuid(uuid_or_name.as_str()) .name_to_uuid(uuid_or_name.as_str())
.map_err(|e| { .map_err(|e| {
// sometimes it comes back as empty which is bad, it's safe to start with `<empty` here
// because a valid username/uuid can never start with that and we're only logging it
let uuid_or_name_val = match uuid_or_name.is_empty() {
true => "<empty uuid_or_name>",
false => &uuid_or_name,
};
admin_info!( admin_info!(
err = ?e, err = ?e,
"Error resolving {} as gidnumber continuing ...", "Error resolving {} as gidnumber continuing ...",
uuid_or_name uuid_or_name_val
); );
e e
})?; })?;

View file

@ -70,7 +70,7 @@ pub async fn are_we_json_yet<B>(request: Request<B>, next: Next<B>) -> Response
} }
/// This runs at the start of the request, adding an extension with `KOpId` which has useful things inside it. /// 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<B>( pub async fn kopid_middleware<B>(
auth: Option<TypedHeader<Authorization<Bearer>>>, auth: Option<TypedHeader<Authorization<Bearer>>>,
mut request: Request<B>, mut request: Request<B>,
@ -86,10 +86,11 @@ pub async fn kopid_middleware<B>(
request.extensions_mut().insert(KOpId { eventid, uat }); request.extensions_mut().insert(KOpId { eventid, uat });
let mut response = next.run(request).await; let mut response = next.run(request).await;
#[allow(clippy::unwrap_used)] #[allow(clippy::expect_used)]
response.headers_mut().insert( response.headers_mut().insert(
"X-KANIDM-OPID", "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 response

View file

@ -5,6 +5,7 @@ mod manifest;
mod middleware; mod middleware;
mod oauth2; mod oauth2;
mod tests; mod tests;
mod trace;
mod ui; mod ui;
mod v1; mod v1;
mod v1_scim; mod v1_scim;
@ -37,6 +38,7 @@ use tokio_openssl::SslStream;
use futures_util::future::poll_fn; use futures_util::future::poll_fn;
use serde::Serialize; use serde::Serialize;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tracing::Level;
use std::io::ErrorKind; use std::io::ErrorKind;
use std::path::PathBuf; use std::path::PathBuf;
@ -45,7 +47,7 @@ use std::sync::Arc;
use std::{net::SocketAddr, str::FromStr}; use std::{net::SocketAddr, str::FromStr};
use tokio::sync::broadcast; use tokio::sync::broadcast;
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
use tower_http::trace::TraceLayer; use tower_http::trace::{DefaultOnRequest, TraceLayer};
use uuid::Uuid; use uuid::Uuid;
use crate::CoreAction; 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 let app = app
.merge(static_routes) .merge(static_routes)
.layer(from_fn_with_state( .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::version_middleware))
.layer(from_fn( .layer(from_fn(
middleware::hsts_header::strict_transport_security_layer, 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 // layer which checks the responses have a content-type of JSON when we're in debug mode
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
let app = app.layer(from_fn(middleware::are_we_json_yet)); let app = app.layer(from_fn(middleware::are_we_json_yet));
let app = app 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) .with_state(state)
// the connect_info bit here lets us pick up the remote address of the client // the connect_info bit here lets us pick up the remote address of the client
.into_make_service_with_connect_info::<SocketAddr>(); .into_make_service_with_connect_info::<SocketAddr>();
@ -345,7 +353,6 @@ async fn server_loop(
} }
} }
// #[instrument(name = "handle-connection", level = "debug", skip_all)]
/// This handles an individual connection. /// This handles an individual connection.
async fn handle_conn( async fn handle_conn(
acceptor: SslAcceptor, acceptor: SslAcceptor,

View file

@ -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<B> tower_http::trace::MakeSpan<B> for DefaultMakeSpanKanidmd {
#[instrument(name = "handle_request", skip_all)]
fn make_span(&mut self, request: &Request<B>) -> Span {
tracing::span!(
Level::INFO,
"request",
method = %request.method(),
uri = %request.uri(),
version = ?request.version(),
)
}
}

View file

@ -874,7 +874,7 @@ pub async fn account_delete_id_radius(
json_rest_event_delete_id_attr(state, id, attr, filter, None, kopid).await 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<ServerState>, State(state): State<ServerState>,
Path(id): Path<String>, Path(id): Path<String>,
Extension(kopid): Extension<KOpId>, Extension(kopid): Extension<KOpId>,
@ -893,6 +893,7 @@ pub async fn account_get_id_radius_token(
} }
/// Expects an `AccountUnixExtend` object /// Expects an `AccountUnixExtend` object
#[instrument(name = "account_post_id_unix", level = "INFO", skip(id, state, kopid))]
pub async fn account_post_id_unix( pub async fn account_post_id_unix(
State(state): State<ServerState>, State(state): State<ServerState>,
Path(id): Path<String>, Path(id): Path<String>,
@ -906,15 +907,46 @@ pub async fn account_post_id_unix(
to_axum_response(res) 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<ServerState>, State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>, Extension(kopid): Extension<KOpId>,
Path(id): Path<String>, Path(id): Path<String>,
) -> impl IntoResponse { ) -> 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 let res = state
.qe_r_ref .qe_r_ref
.handle_internalunixusertokenread(kopid.uat, id, kopid.eventid) .handle_internalunixusertokenread(kopid.uat, id, kopid.eventid)
.await; .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) to_axum_response(res)
} }
@ -1480,8 +1512,8 @@ pub fn router(state: ServerState) -> Router<ServerState> {
) )
.route( .route(
"/v1/person/:id/_radius/_token", "/v1/person/:id/_radius/_token",
get(account_get_id_radius_token), get(account_id_radius_token),
) // TODO: make radius token cacheable ) // TODO: make this cacheable
.route("/v1/person/:id/_unix", post(account_post_id_unix)) .route("/v1/person/:id/_unix", post(account_post_id_unix))
.route( .route(
"/v1/person/:id/_unix/_credential", "/v1/person/:id/_unix/_credential",
@ -1551,11 +1583,11 @@ pub fn router(state: ServerState) -> Router<ServerState> {
) )
.route( .route(
"/v1/account/:id/_unix/_token", "/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( .route(
"/v1/account/:id/_radius/_token", "/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( .route(
"/v1/account/:id/_ssh_pubkeys", "/v1/account/:id/_ssh_pubkeys",

View file

@ -99,25 +99,24 @@ pub async fn create_user(rsclient: &KanidmClient, id: &str, group_name: &str) {
.expect("Failed to create the user"); .expect("Failed to create the user");
// Create group and add to user to test read attr: member_of // Create group and add to user to test read attr: member_of
#[allow(clippy::expect_used)] #[allow(clippy::panic)]
if rsclient if rsclient
.idm_group_get(group_name) .idm_group_get(group_name)
.await .await
.expect("Failed to get group") .unwrap_or_else(|_| panic!("Failed to get group {}", group_name))
.is_none() .is_none()
{ {
#[allow(clippy::expect_used)] #[allow(clippy::panic)]
rsclient rsclient
.idm_group_create(group_name) .idm_group_create(group_name)
.await .await
.expect("Failed to create group"); .unwrap_or_else(|_| panic!("Failed to create group {}", group_name));
} }
#[allow(clippy::panic)]
#[allow(clippy::expect_used)]
rsclient rsclient
.idm_group_add_members(group_name, &[id]) .idm_group_add_members(group_name, &[id])
.await .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( pub async fn create_user_with_all_attrs(

View file

@ -554,12 +554,15 @@ async fn test_self_write_mail_priv_people(rsclient: KanidmClient) {
#[kanidmd_testkit::test] #[kanidmd_testkit::test]
async fn test_https_robots_txt(rsclient: KanidmClient) { async fn test_https_robots_txt(rsclient: KanidmClient) {
// We need to do manual reqwests here. // 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, Ok(value) => value,
Err(error) => { Err(error) => {
panic!("Failed to query {:?} : {:#?}", addr, error); panic!(
"Failed to query {:?} : {:#?}",
rsclient.make_url("/robots.txt"),
error
);
} }
}; };
eprintln!("response: {:#?}", response); eprintln!("response: {:#?}", response);
@ -577,9 +580,7 @@ async fn test_https_robots_txt(rsclient: KanidmClient) {
// #[kanidmd_testkit::test] // #[kanidmd_testkit::test]
// async fn test_https_routemap(rsclient: KanidmClient) { // async fn test_https_routemap(rsclient: KanidmClient) {
// // We need to do manual reqwests here. // // We need to do manual reqwests here.
// let addr = rsclient.get_url(); // let response = match reqwest::get(rsclient.make_url("/v1/routemap")).await {
// let response = match reqwest::get(format!("{}/v1/routemap", &addr)).await {
// Ok(value) => value, // Ok(value) => value,
// Err(error) => { // Err(error) => {
// panic!("Failed to query {:?} : {:#?}", addr, error); // panic!("Failed to query {:?} : {:#?}", addr, error);
@ -598,7 +599,7 @@ async fn test_https_robots_txt(rsclient: KanidmClient) {
#[kanidmd_testkit::test] #[kanidmd_testkit::test]
async fn test_v1_raw_delete(rsclient: KanidmClient) { async fn test_v1_raw_delete(rsclient: KanidmClient) {
// We need to do manual reqwests here. // We need to do manual reqwests here.
let addr = rsclient.get_url();
let client = reqwest::ClientBuilder::new() let client = reqwest::ClientBuilder::new()
.danger_accept_invalid_certs(true) .danger_accept_invalid_certs(true)
.build() .build()
@ -607,7 +608,7 @@ async fn test_v1_raw_delete(rsclient: KanidmClient) {
let post_body = serde_json::json!({"filter": "self"}).to_string(); let post_body = serde_json::json!({"filter": "self"}).to_string();
let response = match client let response = match client
.post(format!("{}/v1/raw/delete", &addr)) .post(rsclient.make_url("/v1/raw/delete"))
.header(CONTENT_TYPE, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON)
.body(post_body) .body(post_body)
.send() .send()
@ -615,7 +616,11 @@ async fn test_v1_raw_delete(rsclient: KanidmClient) {
{ {
Ok(value) => value, Ok(value) => value,
Err(error) => { Err(error) => {
panic!("Failed to query {:?} : {:#?}", addr, error); panic!(
"Failed to query {:?} : {:#?}",
rsclient.make_url("/v1/raw/delete"),
error
);
} }
}; };
eprintln!("response: {:#?}", response); eprintln!("response: {:#?}", response);
@ -629,16 +634,19 @@ async fn test_v1_raw_delete(rsclient: KanidmClient) {
#[kanidmd_testkit::test] #[kanidmd_testkit::test]
async fn test_v1_raw_logout(rsclient: KanidmClient) { async fn test_v1_raw_logout(rsclient: KanidmClient) {
// We need to do manual reqwests here. // We need to do manual reqwests here.
let addr = rsclient.get_url();
let client = reqwest::ClientBuilder::new() let client = reqwest::ClientBuilder::new()
.danger_accept_invalid_certs(true) .danger_accept_invalid_certs(true)
.build() .build()
.unwrap(); .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, Ok(value) => value,
Err(error) => { Err(error) => {
panic!("Failed to query {:?} : {:#?}", addr, error); panic!(
"Failed to query {:?} : {:#?}",
rsclient.make_url("/v1/logout"),
error
);
} }
}; };
eprintln!("response: {:#?}", response); eprintln!("response: {:#?}", response);
@ -652,16 +660,19 @@ async fn test_v1_raw_logout(rsclient: KanidmClient) {
#[kanidmd_testkit::test] #[kanidmd_testkit::test]
async fn test_status_endpoint(rsclient: KanidmClient) { async fn test_status_endpoint(rsclient: KanidmClient) {
// We need to do manual reqwests here. // We need to do manual reqwests here.
let addr = rsclient.get_url();
let client = reqwest::ClientBuilder::new() let client = reqwest::ClientBuilder::new()
.danger_accept_invalid_certs(true) .danger_accept_invalid_certs(true)
.build() .build()
.unwrap(); .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, Ok(value) => value,
Err(error) => { Err(error) => {
panic!("Failed to query {:?} : {:#?}", addr, error); panic!(
"Failed to query {:?} : {:#?}",
rsclient.make_url("/status"),
error
);
} }
}; };
eprintln!("response: {:#?}", response); eprintln!("response: {:#?}", response);

View file

@ -3,13 +3,16 @@ use kanidm_client::KanidmClient;
#[kanidmd_testkit::test] #[kanidmd_testkit::test]
async fn test_https_manifest(rsclient: KanidmClient) { async fn test_https_manifest(rsclient: KanidmClient) {
// We need to do manual reqwests here. // We need to do manual reqwests here.
let addr = rsclient.get_url();
// here we test the /ui/ endpoint which should have the headers // 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, Ok(value) => value,
Err(error) => { Err(error) => {
panic!("Failed to query {:?} : {:#?}", addr, error); panic!(
"Failed to query {:?} : {:#?}",
rsclient.make_url("/manifest.webmanifest"),
error
);
} }
}; };
eprintln!("response: {:#?}", response); eprintln!("response: {:#?}", response);

View file

@ -3,13 +3,16 @@ use kanidm_client::KanidmClient;
#[kanidmd_testkit::test] #[kanidmd_testkit::test]
async fn test_https_middleware_headers(rsclient: KanidmClient) { async fn test_https_middleware_headers(rsclient: KanidmClient) {
// We need to do manual reqwests here. // We need to do manual reqwests here.
let addr = rsclient.get_url();
// here we test the /ui/ endpoint which should have the headers // 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, Ok(value) => value,
Err(error) => { Err(error) => {
panic!("Failed to query {:?} : {:#?}", addr, error); panic!(
"Failed to query {:?} : {:#?}",
rsclient.make_url("/ui"),
error
);
} }
}; };
eprintln!("response: {:#?}", response); eprintln!("response: {:#?}", response);
@ -21,10 +24,14 @@ async fn test_https_middleware_headers(rsclient: KanidmClient) {
assert_ne!(response.headers().get("content-security-policy"), None); assert_ne!(response.headers().get("content-security-policy"), None);
// here we test the /ui/login endpoint which should have the headers // 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, Ok(value) => value,
Err(error) => { Err(error) => {
panic!("Failed to query {:?} : {:#?}", addr, error); panic!(
"Failed to query {:?} : {:#?}",
rsclient.make_url("/ui/login"),
error
);
} }
}; };
eprintln!("response: {:#?}", response); eprintln!("response: {:#?}", response);

View file

@ -82,7 +82,7 @@ async fn test_webdriver_user_login(rsclient: kanidm_client::KanidmClient) {
handle_error!( handle_error!(
c, c,
c.goto(rsclient.get_url().to_string()).await, c.goto(&rsclient.get_url().to_string()).await,
"Couldn't get URL" "Couldn't get URL"
); );

View file

@ -121,8 +121,6 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) {
.await .await
.expect("No user auth token found"); .expect("No user auth token found");
let url = rsclient.get_url().to_string();
// We need a new reqwest client here. // We need a new reqwest client here.
// from here, we can now begin what would be a "interaction" to the oauth server. // 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 let response = client
.request( .request(
reqwest::Method::OPTIONS, reqwest::Method::OPTIONS,
format!( rsclient.make_url("/oauth2/openid/test_integration/.well-known/openid-configuration"),
"{}/oauth2/openid/test_integration/.well-known/openid-configuration",
url
),
) )
.send() .send()
.await .await
@ -156,10 +151,7 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) {
assert!(cors_header.eq("*")); assert!(cors_header.eq("*"));
let response = client let response = client
.get(format!( .get(rsclient.make_url("/oauth2/openid/test_integration/.well-known/openid-configuration"))
"{}/oauth2/openid/test_integration/.well-known/openid-configuration",
url
))
.send() .send()
.await .await
.expect("Failed to send request."); .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 // Most values are checked in idm/oauth2.rs, but we want to sanity check
// the urls here as an extended function smoke test. // the urls here as an extended function smoke test.
assert!( assert!(discovery.issuer == rsclient.make_url("/oauth2/openid/test_integration"));
discovery.issuer == Url::parse(&format!("{}/oauth2/openid/test_integration", url)).unwrap()
);
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!( assert!(
discovery.userinfo_endpoint discovery.userinfo_endpoint
== Some( == Some(rsclient.make_url("/oauth2/openid/test_integration/userinfo"))
Url::parse(&format!("{}/oauth2/openid/test_integration/userinfo", url)).unwrap()
)
); );
assert!( assert!(
discovery.jwks_uri discovery.jwks_uri == rsclient.make_url("/oauth2/openid/test_integration/public_key.jwk")
== Url::parse(&format!(
"{}/oauth2/openid/test_integration/public_key.jwk",
url
))
.unwrap()
); );
// Step 0 - get the jwks public key. // Step 0 - get the jwks public key.
let response = client let response = client
.get(format!( .get(rsclient.make_url("/oauth2/openid/test_integration/public_key.jwk"))
"{}/oauth2/openid/test_integration/public_key.jwk",
url
))
.send() .send()
.await .await
.expect("Failed to send request."); .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 (pkce_code_challenge, pkce_code_verifier) = PkceCodeChallenge::new_random_sha256();
let response = client let response = client
.get(format!("{}/oauth2/authorise", url)) .get(rsclient.make_url("/oauth2/authorise"))
.bearer_auth(oauth_test_uat.clone()) .bearer_auth(oauth_test_uat.clone())
.query(&[ .query(&[
("response_type", "code"), ("response_type", "code"),
@ -271,7 +251,7 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) {
// state and code. // state and code.
let response = client let response = client
.get(format!("{}/oauth2/authorise/permit", url)) .get(rsclient.make_url("/oauth2/authorise/permit"))
.bearer_auth(oauth_test_uat) .bearer_auth(oauth_test_uat)
.query(&[("token", consent_token.as_str())]) .query(&[("token", consent_token.as_str())])
.send() .send()
@ -312,7 +292,7 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) {
.into(); .into();
let response = client let response = client
.post(format!("{}/oauth2/token", url)) .post(rsclient.make_url("/oauth2/token"))
.basic_auth("test_integration", Some(client_secret.clone())) .basic_auth("test_integration", Some(client_secret.clone()))
.form(&form_req) .form(&form_req)
.send() .send()
@ -339,7 +319,7 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) {
}; };
let response = client let response = client
.post(format!("{}/oauth2/token/introspect", url)) .post(rsclient.make_url("/oauth2/token/introspect"))
.basic_auth("test_integration", Some(client_secret)) .basic_auth("test_integration", Some(client_secret))
.form(&intr_request) .form(&intr_request)
.send() .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 // This is mostly checked inside of idm/oauth2.rs. This is more to check the oidc
// token and the userinfo endpoints. // 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); eprintln!("{:?}", oidc.s_claims.email);
assert!(oidc.s_claims.email.as_deref() == Some("oauth_test@localhost")); assert!(oidc.s_claims.email.as_deref() == Some("oauth_test@localhost"));
assert!(oidc.s_claims.email_verified == Some(true)); assert!(oidc.s_claims.email_verified == Some(true));
let response = client 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()) .bearer_auth(atr.access_token.clone())
.send() .send()
.await .await
@ -486,8 +466,6 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) {
.await .await
.expect("No user auth token found"); .expect("No user auth token found");
let url = rsclient.get_url().to_string();
// We need a new reqwest client here. // We need a new reqwest client here.
// from here, we can now begin what would be a "interaction" to the oauth server. // 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. // Step 0 - get the jwks public key.
let response = client let response = client
.get(format!( .get(rsclient.make_url("/oauth2/openid/test_integration/public_key.jwk"))
"{}/oauth2/openid/test_integration/public_key.jwk",
url
))
.send() .send()
.await .await
.expect("Failed to send request."); .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 (pkce_code_challenge, pkce_code_verifier) = PkceCodeChallenge::new_random_sha256();
let response = client let response = client
.get(format!("{}/oauth2/authorise", url)) .get(rsclient.make_url("/oauth2/authorise"))
.bearer_auth(oauth_test_uat.clone()) .bearer_auth(oauth_test_uat.clone())
.query(&[ .query(&[
("response_type", "code"), ("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 // Step 2 - we now send the consent get to the server which yields a redirect with a
// state and code. // state and code.
let response = client let response = client
.get(format!("{}/oauth2/authorise/permit", url)) .get(rsclient.make_url("/oauth2/authorise/permit"))
.bearer_auth(oauth_test_uat) .bearer_auth(oauth_test_uat)
.query(&[("token", consent_token.as_str())]) .query(&[("token", consent_token.as_str())])
.send() .send()
@ -611,7 +586,7 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) {
}; };
let response = client let response = client
.post(format!("{}/oauth2/token", url)) .post(rsclient.make_url("/oauth2/token"))
.form(&form_req) .form(&form_req)
.send() .send()
.await .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 // This is mostly checked inside of idm/oauth2.rs. This is more to check the oidc
// token and the userinfo endpoints. // 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); eprintln!("{:?}", oidc.s_claims.email);
assert!(oidc.s_claims.email.as_deref() == Some("oauth_test@localhost")); assert!(oidc.s_claims.email.as_deref() == Some("oauth_test@localhost"));
assert!(oidc.s_claims.email_verified == Some(true)); assert!(oidc.s_claims.email_verified == Some(true));
@ -645,7 +620,7 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) {
let response = client let response = client
.request( .request(
reqwest::Method::OPTIONS, reqwest::Method::OPTIONS,
format!("{}/oauth2/openid/test_integration/userinfo", url), rsclient.make_url("/oauth2/openid/test_integration/userinfo"),
) )
.send() .send()
.await .await
@ -661,7 +636,7 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) {
assert!(cors_header.eq("*")); assert!(cors_header.eq("*"));
let response = client 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()) .bearer_auth(atr.access_token.clone())
.send() .send()
.await .await
@ -695,7 +670,6 @@ async fn test_oauth2_token_post_bad_bodies(rsclient: KanidmClient) {
.await; .await;
assert!(res.is_ok()); assert!(res.is_ok());
let url = rsclient.get_url().to_string();
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::none()) .redirect(reqwest::redirect::Policy::none())
.no_proxy() .no_proxy()
@ -704,7 +678,7 @@ async fn test_oauth2_token_post_bad_bodies(rsclient: KanidmClient) {
// test for a bad-body request on token // test for a bad-body request on token
let response = client let response = client
.post(format!("{}/oauth2/token", url)) .post(rsclient.make_url("/oauth2/token"))
.form(&serde_json::json!({})) .form(&serde_json::json!({}))
// .bearer_auth(atr.access_token.clone()) // .bearer_auth(atr.access_token.clone())
.send() .send()
@ -715,7 +689,7 @@ async fn test_oauth2_token_post_bad_bodies(rsclient: KanidmClient) {
// test for a bad-auth request // test for a bad-auth request
let response = client let response = client
.post(format!("{}/oauth2/token/introspect", url)) .post(rsclient.make_url("/oauth2/token/introspect"))
.form(&serde_json::json!({ "token": "lol" })) .form(&serde_json::json!({ "token": "lol" }))
.send() .send()
.await .await
@ -731,7 +705,6 @@ async fn test_oauth2_token_revoke_post(rsclient: KanidmClient) {
.await; .await;
assert!(res.is_ok()); assert!(res.is_ok());
let url = rsclient.get_url().to_string();
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::none()) .redirect(reqwest::redirect::Policy::none())
.no_proxy() .no_proxy()
@ -740,7 +713,7 @@ async fn test_oauth2_token_revoke_post(rsclient: KanidmClient) {
// test for a bad-body request on token // test for a bad-body request on token
let response = client let response = client
.post(format!("{}/oauth2/token/revoke", url)) .post(rsclient.make_url("/oauth2/token/revoke"))
.form(&serde_json::json!({})) .form(&serde_json::json!({}))
.bearer_auth("lolol") .bearer_auth("lolol")
.send() .send()
@ -751,7 +724,7 @@ async fn test_oauth2_token_revoke_post(rsclient: KanidmClient) {
// test for a invalid format request on token // test for a invalid format request on token
let response = client let response = client
.post(format!("{}/oauth2/token/revoke", url)) .post(rsclient.make_url("/oauth2/token/revoke"))
.json("") .json("")
.bearer_auth("lolol") .bearer_auth("lolol")
.send() .send()
@ -763,7 +736,7 @@ async fn test_oauth2_token_revoke_post(rsclient: KanidmClient) {
// test for a bad-body request on token // test for a bad-body request on token
let response = client let response = client
.post(format!("{}/oauth2/token/revoke", url)) .post(rsclient.make_url("/oauth2/token/revoke"))
.form(&serde_json::json!({})) .form(&serde_json::json!({}))
.bearer_auth("Basic lolol") .bearer_auth("Basic lolol")
.send() .send()
@ -774,7 +747,7 @@ async fn test_oauth2_token_revoke_post(rsclient: KanidmClient) {
// test for a bad-body request on token // test for a bad-body request on token
let response = client let response = client
.post(format!("{}/oauth2/token/revoke", url)) .post(rsclient.make_url("/oauth2/token/revoke"))
.body(serde_json::json!({}).to_string()) .body(serde_json::json!({}).to_string())
.bearer_auth("Basic lolol") .bearer_auth("Basic lolol")
.send() .send()

View file

@ -6,7 +6,6 @@ use reqwest::header::CONTENT_TYPE;
#[kanidmd_testkit::test] #[kanidmd_testkit::test]
async fn test_v1_person_patch(rsclient: KanidmClient) { async fn test_v1_person_patch(rsclient: KanidmClient) {
// We need to do manual reqwests here. // We need to do manual reqwests here.
let addr = rsclient.get_url();
let client = reqwest::ClientBuilder::new() let client = reqwest::ClientBuilder::new()
.danger_accept_invalid_certs(true) .danger_accept_invalid_certs(true)
.build() .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 post_body = serde_json::json!({"attrs": { "email" : "crab@example.com"}}).to_string();
let response = match client let response = match client
.patch(format!("{}/v1/person/foo", &addr)) .patch(rsclient.make_url("/v1/person/foo"))
.header(CONTENT_TYPE, APPLICATION_JSON) .header(CONTENT_TYPE, APPLICATION_JSON)
.body(post_body) .body(post_body)
.send() .send()
@ -23,7 +22,11 @@ async fn test_v1_person_patch(rsclient: KanidmClient) {
{ {
Ok(value) => value, Ok(value) => value,
Err(error) => { Err(error) => {
panic!("Failed to query {:?} : {:#?}", addr, error); panic!(
"Failed to query {:?} : {:#?}",
rsclient.make_url("/v1/person/foo"),
error
);
} }
}; };
eprintln!("response: {:#?}", response); eprintln!("response: {:#?}", response);

View file

@ -120,6 +120,7 @@ async fn test_server_search(rsclient: KanidmClient) {
// First show we are un-authenticated. // First show we are un-authenticated.
let pre_res = rsclient.whoami().await; let pre_res = rsclient.whoami().await;
// This means it was okay whoami, but no uat attached. // This means it was okay whoami, but no uat attached.
println!("Response: {:?}", pre_res);
assert!(pre_res.unwrap().is_none()); assert!(pre_res.unwrap().is_none());
let res = rsclient let res = rsclient

View file

@ -93,7 +93,6 @@ async fn test_sync_account_lifecycle(rsclient: KanidmClient) {
#[kanidmd_testkit::test] #[kanidmd_testkit::test]
async fn test_scim_sync_get(rsclient: KanidmClient) { async fn test_scim_sync_get(rsclient: KanidmClient) {
// We need to do manual reqwests here. // We need to do manual reqwests here.
let addr = rsclient.get_url();
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert( headers.insert(
@ -107,10 +106,14 @@ async fn test_scim_sync_get(rsclient: KanidmClient) {
.build() .build()
.unwrap(); .unwrap();
// here we test the /ui/ endpoint which should have the headers // 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, Ok(value) => value,
Err(error) => { Err(error) => {
panic!("Failed to query {:?} : {:#?}", addr, error); panic!(
"Failed to query {:?} : {:#?}",
rsclient.make_url("/scim/v1/Sync"),
error
);
} }
}; };
eprintln!("response: {:#?}", response); eprintln!("response: {:#?}", response);

View file

@ -4,20 +4,23 @@ use kanidm_client::KanidmClient;
#[kanidmd_testkit::test] #[kanidmd_testkit::test]
async fn test_v1_self_applinks(rsclient: KanidmClient) { async fn test_v1_self_applinks(rsclient: KanidmClient) {
// We need to do manual reqwests here. // We need to do manual reqwests here.
let addr = rsclient.get_url();
let client = reqwest::ClientBuilder::new() let client = reqwest::ClientBuilder::new()
.danger_accept_invalid_certs(true) .danger_accept_invalid_certs(true)
.build() .build()
.unwrap(); .unwrap();
let response = match client let response = match client
.get(format!("{}/v1/self/_applinks", &addr)) .get(rsclient.make_url("/v1/self/_applinks"))
.send() .send()
.await .await
{ {
Ok(value) => value, Ok(value) => value,
Err(error) => { Err(error) => {
panic!("Failed to query {:?} : {:#?}", addr, error); panic!(
"Failed to query {:?} : {:#?}",
rsclient.make_url("/v1/self/_applinks"),
error
);
} }
}; };
eprintln!("response: {:#?}", response); eprintln!("response: {:#?}", response);
@ -31,16 +34,19 @@ async fn test_v1_self_applinks(rsclient: KanidmClient) {
#[kanidmd_testkit::test] #[kanidmd_testkit::test]
async fn test_v1_self_whoami_uat(rsclient: KanidmClient) { async fn test_v1_self_whoami_uat(rsclient: KanidmClient) {
// We need to do manual reqwests here. // We need to do manual reqwests here.
let addr = rsclient.get_url();
let client = reqwest::ClientBuilder::new() let client = reqwest::ClientBuilder::new()
.danger_accept_invalid_certs(true) .danger_accept_invalid_certs(true)
.build() .build()
.unwrap(); .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, Ok(value) => value,
Err(error) => { Err(error) => {
panic!("Failed to query {:?} : {:#?}", addr, error); panic!(
"Failed to query {:?} : {:#?}",
rsclient.make_url("/v1/self/_uat"),
error
);
} }
}; };
eprintln!("response: {:#?}", response); eprintln!("response: {:#?}", response);

View file

@ -4,7 +4,6 @@ use kanidm_client::KanidmClient;
#[kanidmd_testkit::test] #[kanidmd_testkit::test]
async fn test_v1_service_account_id_attr_attr_delete(rsclient: KanidmClient) { async fn test_v1_service_account_id_attr_attr_delete(rsclient: KanidmClient) {
// We need to do manual reqwests here. // We need to do manual reqwests here.
let addr = rsclient.get_url();
let client = reqwest::ClientBuilder::new() let client = reqwest::ClientBuilder::new()
.danger_accept_invalid_certs(true) .danger_accept_invalid_certs(true)
.build() .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 post_body = serde_json::json!({"filter": "self"}).to_string();
let response = match client let response = match client
.delete(format!("{}/v1/service_account/admin/_attr/email", &addr)) .delete(rsclient.make_url("/v1/service_account/admin/_attr/email"))
.send() .send()
.await .await
{ {
Ok(value) => value, Ok(value) => value,
Err(error) => { 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); eprintln!("response: {:#?}", response);

View file

@ -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... /// This literally tests that the thing exists and responds in a way we expect, probably worth testing it better...
#[kanidmd_testkit::test] #[kanidmd_testkit::test]
async fn test_v1_system_post_attr(rsclient: KanidmClient) { 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() let client = reqwest::ClientBuilder::new()
.danger_accept_invalid_certs(true) .danger_accept_invalid_certs(true)
.build() .build()
.unwrap(); .unwrap();
let response = match client 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"})) .json(&serde_json::json!({"filter": "self"}))
.send() .send()
.await .await
{ {
Ok(value) => value, Ok(value) => value,
Err(error) => { Err(error) => {
panic!("Failed to query {:?} : {:#?}", addr, error); panic!(
"Failed to query {:?} : {:#?}",
rsclient.make_url("v1/system/_attr/domain_name"),
error
);
} }
}; };
eprintln!("response: {:#?}", response); eprintln!("response: {:#?}", response);

View file

@ -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());
}

View file

@ -16,7 +16,6 @@ use qrcode::render::unicode;
use qrcode::QrCode; use qrcode::QrCode;
use time::format_description::well_known::Rfc3339; use time::format_description::well_known::Rfc3339;
use time::OffsetDateTime; use time::OffsetDateTime;
use url::Url;
use uuid::Uuid; use uuid::Uuid;
use crate::webauthn::get_authenticator; use crate::webauthn::get_authenticator;
@ -603,14 +602,7 @@ impl AccountCredential {
.await .await
{ {
Ok(cuintent_token) => { Ok(cuintent_token) => {
let mut url = match Url::parse(client.get_url()) { let mut url = client.make_url("/ui/reset");
Ok(u) => u,
Err(e) => {
error!("Unable to parse url - {:?}", e);
return;
}
};
url.set_path("/ui/reset");
url.query_pairs_mut() url.query_pairs_mut()
.append_pair("token", cuintent_token.token.as_str()); .append_pair("token", cuintent_token.token.as_str());

View file

@ -78,11 +78,19 @@ pub struct DbTxn<'a> {
require_tpm: Option<&'a tpm::TpmConfig>, require_tpm: Option<&'a tpm::TpmConfig>,
} }
#[derive(Debug)]
/// Errors coming back from the `Db` struct
pub enum DbError {
Sqlite,
Tpm,
}
impl Db { impl Db {
pub fn new(path: &str, tpm_policy: &TpmPolicy) -> Result<Self, ()> { pub fn new(path: &str, tpm_policy: &TpmPolicy) -> Result<Self, DbError> {
let before = unsafe { umask(0o0027) }; let before = unsafe { umask(0o0027) };
let conn = Connection::open(path).map_err(|e| { let conn = Connection::open(path).map_err(|e| {
error!(err = ?e, "rusqulite error"); error!(err = ?e, "rusqulite error");
DbError::Sqlite
})?; })?;
let _ = unsafe { umask(before) }; let _ = unsafe { umask(before) };
// We only build a single thread. If we need more than one, we'll // 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 { let require_tpm = match tpm_policy {
TpmPolicy::Ignore => None, TpmPolicy::Ignore => None,
TpmPolicy::IfPossible(tcti_str) => Db::tpm_setup_context(tcti_str, &conn).ok(), 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 { Ok(Db {
@ -784,16 +794,19 @@ impl<'a> Drop for DbTxn<'a> {
#[cfg(not(feature = "tpm"))] #[cfg(not(feature = "tpm"))]
pub(crate) mod tpm { pub(crate) mod tpm {
use super::Db; use super::{Db, DbError};
use rusqlite::Connection; use rusqlite::Connection;
pub struct TpmConfig {} pub struct TpmConfig {}
impl Db { impl Db {
pub fn tpm_setup_context(_tcti_str: &str, _conn: &Connection) -> Result<TpmConfig, ()> { pub fn tpm_setup_context(
_tcti_str: &str,
_conn: &Connection,
) -> Result<TpmConfig, DbError> {
warn!("tpm feature is not available in this build"); warn!("tpm feature is not available in this build");
Err(()) Err(DbError::Tpm)
} }
} }
} }