diff --git a/Cargo.lock b/Cargo.lock index 0efab0c..667c1c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3781,6 +3781,7 @@ dependencies = [ "serde", "serde_json", "serde_with", + "thiserror", "tokio", "tracing", "tracing-subscriber", diff --git a/apps/yourcloud/Cargo.toml b/apps/yourcloud/Cargo.toml index 6dc8e82..b8db58b 100644 --- a/apps/yourcloud/Cargo.toml +++ b/apps/yourcloud/Cargo.toml @@ -21,4 +21,5 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } urlencoding = "2.1.3" http_client = { workspace = true } discord_bot = { workspace = true } +thiserror = { workspace = true } openidconnect = "3.5.0" diff --git a/apps/yourcloud/examples/oidc.rs b/apps/yourcloud/examples/oidc.rs index 804d0c7..4911b38 100644 --- a/apps/yourcloud/examples/oidc.rs +++ b/apps/yourcloud/examples/oidc.rs @@ -1,4 +1,6 @@ +use reqwest::redirect::Policy; use std::io::{self, BufRead}; +use thiserror::Error; use color_eyre::{eyre::eyre, Result}; use openidconnect::core::{ @@ -6,17 +8,58 @@ use openidconnect::core::{ }; use openidconnect::AuthorizationCode; use openidconnect::{ - AccessTokenHash, ClientId, CsrfToken, IssuerUrl, Nonce, PkceCodeChallenge, RedirectUrl, Scope, + AccessTokenHash, ClientId, CsrfToken, HttpResponse, IssuerUrl, Nonce, PkceCodeChallenge, + RedirectUrl, Scope, }; -use openidconnect::reqwest::http_client; - -const ISSUER_URL: &str = "https://idm.h.lyte.dev/oauth2/openid/yourcloud-dev"; -const CLIENT_ID: &str = "yourcloud-dev"; -// const CLIENT_SECRET: &str = "client_secret"; +const ISSUER_URL: &str = "https://localhost:8443/oauth2/openid/yourcloud"; +const CLIENT_ID: &str = "yourcloud"; const REDIRECT_URL: &str = "http://localhost:3000/oauth2/handler"; const SCOPES: [&str; 3] = ["read", "write", "email"]; +#[derive(Debug, Error)] +pub enum Error { + /// Error returned by reqwest crate. + #[error("request failed")] + Reqwest(#[source] reqwest::Error), + /// I/O error. + #[error("I/O error")] + Io(#[source] std::io::Error), + /// Other error. + #[error("Other error: {}", _0)] + Other(String), +} + +fn http_client(request: openidconnect::HttpRequest) -> Result { + let client = reqwest::Client::builder() + // Following redirects opens the client up to SSRF vulnerabilities. + .redirect(Policy::none()) + .build() + .map_err(Error::Reqwest)?; + + let mut request_builder = client + .request(request.method, request.url.as_str()) + .body(request.body); + + for (name, value) in &request.headers { + request_builder = request_builder.header(name.as_str(), value.as_bytes()); + } + let mut response = client + .execute(request_builder.build().map_err(Error::Reqwest)?) + .map_err(Error::Reqwest)?; + + let mut body = Vec::new(); + response.read_to_end(&mut body).map_err(Error::Io)?; + + { + Ok(HttpResponse { + status_code: response.status(), + headers: response.headers().to_owned(), + body, + }) + } +} + fn main() -> Result<()> { // Use OpenID Connect Discovery to fetch the provider metadata. use openidconnect::{OAuth2TokenResponse, TokenResponse}; diff --git a/config/kanidm/basic-setup.sh b/config/kanidm/basic-setup.sh old mode 100644 new mode 100755 index 8b13789..b7da44c --- a/config/kanidm/basic-setup.sh +++ b/config/kanidm/basic-setup.sh @@ -1 +1,88 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i bash -p ripgrep oath-toolkit +FAKE_PASSWORD="f0rTkN0x1s_cool" +function kdrun { + podman exec -it kanidm "$@" +} +echo "Resetting kanidm admin credentials" +admin_password="$(kdrun kanidmd recover-account admin | rg '.*new_password: "([^"]+)"' -r '$1')" +idm_password="$(kdrun kanidmd recover-account idm_admin | rg '.*new_password: "([^"]+)"' -r '$1')" + +echo "admin password: $admin_password" +echo "idm_admin password: $idm_password" + +# start a kanidm client in the background for an hour +podman kill kanidm-client || true +sleep 0.2 + +podman run -itd --rm \ + --network host \ + --name kanidm-client \ + -v "$PWD/client.toml:/root/.config/kanidm:ro" \ + docker.io/kanidm/tools \ + bash -c 'sleep 3600' \ + >/dev/null 2>&1 +sleep 0.2 + +function krun { + podman exec -it kanidm-client "$@" +} + +# setup sessions for both admin accounts +echo "$admin_password" | krun kanidm login -D admin +echo "$idm_password" | krun kanidm login -D idm_admin + +function create_user { + username="$1"; shift + echo "Creating person (user) '${username}'..." + # krun kanidm person delete "$username" --name idm_admin + krun kanidm person create "$username" "$username user" --name idm_admin + krun kanidm person update "$username" --legalname "$username Lastname" --mail "${username}@example.com" --name idm_admin + + # TODO: this doesn't seem to work? can't seem to commit changes + ( + sleep 0.1 + echo "pass" + sleep 0.1 + echo "$FAKE_PASSWORD" + sleep 0.1 + echo "$FAKE_PASSWORD" + sleep 0.1 + echo "totp" + sleep 0.1 + echo "totpname" + sleep 0.25 + totp_uri="$(rg 'TOTP URI: (.+)' /tmp/create-user-log.txt -r '$1')" + totp_secret="$(echo "$totp_uri" | rg '.*?secret=([^&]+).*' -r '$1')" + totp_code="$(oathtool --totp=SHA256 -b "$totp_secret")" + echo "$totp_code" + sleep 0.1 + echo "commit" + sleep 0.1 + echo "y" + sleep 0.1 + echo "end" + ) | krun kanidm person credential update "$username" --name idm_admin | tee /tmp/create-user-log.txt +} + +# some groups +app="yourcloud" +adm_group="${app}--admins" +krun kanidm group create "$adm_group" --name idm_admin + +# create our OAuth 2 application +krun kanidm system oauth2 create "$app" "Yourcloud" "http://localhost:3000" --name idm_admin +krun kanidm system oauth2 update-scope-map "$app" "$adm_group" admin openid email read --name idm_admin + +# TODO: expired/disabled users? +# for u in alice bob user1 user2 user3; do +# create_user "$u" +# done +for u in gilfoyle dinesh; do + create_user "$u" +done + +# add users to groups +krun kanidm group add-members "$adm_group" gilfoyle --name idm_admin +krun kanidm group add-members "$adm_group" dinesh --name idm_admin diff --git a/config/kanidm/client.toml b/config/kanidm/client.toml new file mode 100644 index 0000000..d8e6189 --- /dev/null +++ b/config/kanidm/client.toml @@ -0,0 +1,2 @@ +uri = "https://localhost:8443" +verify_ca = false diff --git a/config/kanidm/run-in-podman.sh b/config/kanidm/run-in-podman.sh index bd47eb4..29964f9 100755 --- a/config/kanidm/run-in-podman.sh +++ b/config/kanidm/run-in-podman.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash podman run -itd --rm \ -p 8443:8443 \ - -v "$PWD/server.toml:/data/server.toml" \ - -v "$PWD/chain.pem:/data/chain.pem" \ - -v "$PWD/key.pem:/data/key.pem" \ + -v "$PWD/server.toml:/data/server.toml:ro" \ + -v "$PWD/chain.pem:/data/chain.pem:ro" \ + -v "$PWD/key.pem:/data/key.pem:ro" \ --name kanidm \ docker.io/kanidm/server:latest