From 7086ab0f5ab3ac91928af133da61e70111ecf7e6 Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Tue, 23 Apr 2024 17:40:36 +0200 Subject: [PATCH 01/17] No caching Signed-off-by: Tobias de Bruijn --- server/wilford/src/main.rs | 3 ++- .../src/{routes => response_types}/empty.rs | 0 server/wilford/src/response_types/mod.rs | 6 +++++ .../{routes => response_types}/redirect.rs | 0 server/wilford/src/response_types/uncached.rs | 23 +++++++++++++++++++ server/wilford/src/routes/mod.rs | 2 -- server/wilford/src/routes/oauth/authorize.rs | 2 +- server/wilford/src/routes/oauth/token.rs | 14 +++++------ .../wilford/src/routes/v1/auth/authorize.rs | 2 +- server/wilford/src/routes/v1/cat/add.rs | 2 +- server/wilford/src/routes/v1/cat/remove.rs | 2 +- server/wilford/src/routes/v1/clients/add.rs | 2 +- .../wilford/src/routes/v1/clients/remove.rs | 2 +- .../routes/v1/user/permitted_scopes/add.rs | 2 +- .../routes/v1/user/permitted_scopes/remove.rs | 2 +- 15 files changed, 46 insertions(+), 18 deletions(-) rename server/wilford/src/{routes => response_types}/empty.rs (100%) create mode 100644 server/wilford/src/response_types/mod.rs rename server/wilford/src/{routes => response_types}/redirect.rs (100%) create mode 100644 server/wilford/src/response_types/uncached.rs diff --git a/server/wilford/src/main.rs b/server/wilford/src/main.rs index afc1c41..3b9393d 100644 --- a/server/wilford/src/main.rs +++ b/server/wilford/src/main.rs @@ -8,7 +8,7 @@ use database::oauth2_client::OAuth2Client; use espocrm_rs::EspoApiClient; use noiseless_tracing_actix_web::NoiselessRootSpanBuilder; use tracing::info; -use tracing_actix_web::{RootSpanBuilder, TracingLogger}; +use tracing_actix_web::TracingLogger; use tracing_subscriber::fmt::layer; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; @@ -17,6 +17,7 @@ use tracing_subscriber::EnvFilter; mod config; mod espo; mod routes; +mod response_types; #[tokio::main] async fn main() -> Result<()> { diff --git a/server/wilford/src/routes/empty.rs b/server/wilford/src/response_types/empty.rs similarity index 100% rename from server/wilford/src/routes/empty.rs rename to server/wilford/src/response_types/empty.rs diff --git a/server/wilford/src/response_types/mod.rs b/server/wilford/src/response_types/mod.rs new file mode 100644 index 0000000..fd5df81 --- /dev/null +++ b/server/wilford/src/response_types/mod.rs @@ -0,0 +1,6 @@ +mod empty; +pub use empty::Empty; +mod redirect; +pub use redirect::Redirect; +mod uncached; +pub use uncached::Uncached; diff --git a/server/wilford/src/routes/redirect.rs b/server/wilford/src/response_types/redirect.rs similarity index 100% rename from server/wilford/src/routes/redirect.rs rename to server/wilford/src/response_types/redirect.rs diff --git a/server/wilford/src/response_types/uncached.rs b/server/wilford/src/response_types/uncached.rs new file mode 100644 index 0000000..a07585f --- /dev/null +++ b/server/wilford/src/response_types/uncached.rs @@ -0,0 +1,23 @@ +use actix_web::{HttpRequest, HttpResponse, Responder}; +use actix_web::http::header::HeaderValue; +use reqwest::header::HeaderName; + +pub struct Uncached(T); + +impl Uncached { + pub fn new(responder: T) -> Self { + Self(responder) + } +} + +impl Responder for Uncached { + type Body = T::Body; + + fn respond_to(self, req: &HttpRequest) -> HttpResponse { + let mut response = self.0.respond_to(req); + let headers = response.headers_mut(); + headers.insert(HeaderName::from_static("cache-control"), HeaderValue::from_static("no-store")); + + response + } +} \ No newline at end of file diff --git a/server/wilford/src/routes/mod.rs b/server/wilford/src/routes/mod.rs index 54c934c..340b300 100644 --- a/server/wilford/src/routes/mod.rs +++ b/server/wilford/src/routes/mod.rs @@ -4,10 +4,8 @@ use actix_web::web::ServiceConfig; mod appdata; mod auth; -mod empty; mod error; mod oauth; -mod redirect; mod v1; pub struct Router; diff --git a/server/wilford/src/routes/oauth/authorize.rs b/server/wilford/src/routes/oauth/authorize.rs index 56c6f3a..822452b 100644 --- a/server/wilford/src/routes/oauth/authorize.rs +++ b/server/wilford/src/routes/oauth/authorize.rs @@ -1,6 +1,6 @@ use crate::routes::appdata::{WConfig, WDatabase}; use crate::routes::oauth::{OAuth2AuthorizationResponse, OAuth2Error, OAuth2ErrorKind}; -use crate::routes::redirect::Redirect; +use crate::response_types::Redirect; use actix_web::web; use database::oauth2_client::{AuthorizationType, OAuth2Client}; use serde::Deserialize; diff --git a/server/wilford/src/routes/oauth/token.rs b/server/wilford/src/routes/oauth/token.rs index c2dee54..aff41a5 100644 --- a/server/wilford/src/routes/oauth/token.rs +++ b/server/wilford/src/routes/oauth/token.rs @@ -1,12 +1,12 @@ use crate::routes::appdata::WDatabase; use crate::routes::oauth::OAuth2ErrorKind; -use actix_web::cookie::time; use actix_web::cookie::time::OffsetDateTime; use actix_web::web; use database::oauth2_client::{OAuth2AuthorizationCode, OAuth2Client, RefreshToken}; use serde::{Deserialize, Serialize}; use tap::TapFallible; use tracing::warn; +use crate::response_types::Uncached; #[derive(Deserialize)] pub struct Form { @@ -38,7 +38,7 @@ pub struct Response { pub async fn token( database: WDatabase, form: web::Form
, -) -> Result, OAuth2ErrorKind> { +) -> Result>, OAuth2ErrorKind> { let client = OAuth2Client::get_by_client_id(&database, &form.client_id) .await .tap_err(|e| warn!("{e}")) @@ -80,13 +80,13 @@ pub async fn token( .tap_err(|e| warn!("{e}")) .map_err(|_| OAuth2ErrorKind::ServerError)?; - Ok(web::Json(Response { + Ok(Uncached::new(web::Json(Response { access_token: atoken.token, token_type: "bearer".to_string(), scope: atoken.scopes.unwrap_or_default(), - expires_in: time::OffsetDateTime::now_utc().unix_timestamp() - atoken.expires_at, + expires_in: OffsetDateTime::now_utc().unix_timestamp() - atoken.expires_at, refresh_token: rtoken.token, - })) + }))) } GrantType::RefreshToken => { let rtoken = match &form.refresh_token { @@ -110,13 +110,13 @@ pub async fn token( .tap_err(|e| warn!("{e}")) .map_err(|_| OAuth2ErrorKind::ServerError)?; - Ok(web::Json(Response { + Ok(Uncached::new(web::Json(Response { access_token: atoken.token, token_type: "bearer".to_string(), expires_in: atoken.expires_at - OffsetDateTime::now_utc().unix_timestamp(), scope: atoken.scopes.unwrap_or_default(), refresh_token: rtoken.token, - })) + }))) } } } diff --git a/server/wilford/src/routes/v1/auth/authorize.rs b/server/wilford/src/routes/v1/auth/authorize.rs index 9c4b5b9..ef024d5 100644 --- a/server/wilford/src/routes/v1/auth/authorize.rs +++ b/server/wilford/src/routes/v1/auth/authorize.rs @@ -1,7 +1,7 @@ use crate::routes::appdata::WDatabase; use crate::routes::error::{WebError, WebResult}; use crate::routes::oauth::{OAuth2AuthorizationResponse, OAuth2Error, OAuth2ErrorKind}; -use crate::routes::redirect::Redirect; +use crate::response_types::Redirect; use actix_web::cookie::time::OffsetDateTime; use actix_web::web; use database::oauth2_client::{ diff --git a/server/wilford/src/routes/v1/cat/add.rs b/server/wilford/src/routes/v1/cat/add.rs index 15ed3ae..8f8c73a 100644 --- a/server/wilford/src/routes/v1/cat/add.rs +++ b/server/wilford/src/routes/v1/cat/add.rs @@ -1,6 +1,6 @@ use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::empty::Empty; +use crate::response_types::Empty; use crate::routes::error::{WebError, WebResult}; use crate::routes::v1::MANAGE_SCOPE; use actix_web::web; diff --git a/server/wilford/src/routes/v1/cat/remove.rs b/server/wilford/src/routes/v1/cat/remove.rs index f30cb30..9ed897d 100644 --- a/server/wilford/src/routes/v1/cat/remove.rs +++ b/server/wilford/src/routes/v1/cat/remove.rs @@ -1,6 +1,6 @@ use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::empty::Empty; +use crate::response_types::Empty; use crate::routes::error::{WebError, WebResult}; use crate::routes::v1::MANAGE_SCOPE; use actix_web::web; diff --git a/server/wilford/src/routes/v1/clients/add.rs b/server/wilford/src/routes/v1/clients/add.rs index 0140df5..64e78d8 100644 --- a/server/wilford/src/routes/v1/clients/add.rs +++ b/server/wilford/src/routes/v1/clients/add.rs @@ -1,6 +1,6 @@ use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::empty::Empty; +use crate::response_types::Empty; use crate::routes::error::{WebError, WebResult}; use crate::routes::v1::MANAGE_SCOPE; use actix_web::web; diff --git a/server/wilford/src/routes/v1/clients/remove.rs b/server/wilford/src/routes/v1/clients/remove.rs index e9dbec6..760d862 100644 --- a/server/wilford/src/routes/v1/clients/remove.rs +++ b/server/wilford/src/routes/v1/clients/remove.rs @@ -1,6 +1,6 @@ use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::empty::Empty; +use crate::response_types::Empty; use crate::routes::error::{WebError, WebResult}; use crate::routes::v1::MANAGE_SCOPE; use actix_web::web; diff --git a/server/wilford/src/routes/v1/user/permitted_scopes/add.rs b/server/wilford/src/routes/v1/user/permitted_scopes/add.rs index 6aa09fc..24c8b9f 100644 --- a/server/wilford/src/routes/v1/user/permitted_scopes/add.rs +++ b/server/wilford/src/routes/v1/user/permitted_scopes/add.rs @@ -1,6 +1,6 @@ use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::empty::Empty; +use crate::response_types::Empty; use crate::routes::error::{WebError, WebResult}; use crate::routes::v1::MANAGE_SCOPE; use actix_web::web; diff --git a/server/wilford/src/routes/v1/user/permitted_scopes/remove.rs b/server/wilford/src/routes/v1/user/permitted_scopes/remove.rs index 663c8e8..2dfad02 100644 --- a/server/wilford/src/routes/v1/user/permitted_scopes/remove.rs +++ b/server/wilford/src/routes/v1/user/permitted_scopes/remove.rs @@ -1,6 +1,6 @@ use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::empty::Empty; +use crate::response_types::Empty; use crate::routes::error::{WebError, WebResult}; use crate::routes::v1::MANAGE_SCOPE; use actix_web::web; From 6a9f1b0fe8847552739b084f2829bed19618a380 Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Wed, 15 May 2024 21:29:54 +0200 Subject: [PATCH 02/17] OpenID Connect is mostly implemented --- Makefile | 12 +- README.md | 18 +- docker-compose.yml | 4 +- docs/src/api/clients/internal.md | 1 - docs/src/api/user/index.md | 2 +- docs/src/introduction.md | 11 +- docs/src/oauth2/authorization.md | 44 ++- docs/src/oauth2/introspect.md | 2 +- oauth2_proxy.docker-compose.yml | 14 + sample_config.json | 41 +- server/Cargo.lock | 362 ++++++++++++++++-- server/database/Cargo.toml | 4 +- server/database/migrations/1_initial.sql | 2 + server/database/src/oauth2_client.rs | 100 ++++- server/test_oidc_key.pem | 28 ++ server/test_oidc_key.pem.pub | 9 + server/wilford/Cargo.toml | 1 + server/wilford/src/config.rs | 27 ++ server/wilford/src/main.rs | 14 +- server/wilford/src/response_types/uncached.rs | 9 +- server/wilford/src/routes/appdata.rs | 6 + server/wilford/src/routes/error.rs | 3 + server/wilford/src/routes/mod.rs | 5 +- server/wilford/src/routes/oauth/authorize.rs | 64 ++-- server/wilford/src/routes/oauth/token.rs | 34 +- .../wilford/src/routes/v1/auth/authorize.rs | 119 ++++-- server/wilford/src/routes/v1/cat/add.rs | 2 +- server/wilford/src/routes/v1/cat/remove.rs | 2 +- server/wilford/src/routes/v1/clients/add.rs | 2 +- .../wilford/src/routes/v1/clients/remove.rs | 2 +- .../routes/v1/user/permitted_scopes/add.rs | 2 +- .../routes/v1/user/permitted_scopes/remove.rs | 2 +- server/wilford/src/routes/well_known/jwks.rs | 27 ++ server/wilford/src/routes/well_known/mod.rs | 21 + .../routes/well_known/openid_configuration.rs | 28 ++ test_oidc_key.pem | 51 +++ test_oidc_key.pem.pub | 1 + ui/src/main.ts | 8 +- ui/src/router/index.ts | 2 +- 39 files changed, 932 insertions(+), 154 deletions(-) create mode 100644 oauth2_proxy.docker-compose.yml create mode 100644 server/test_oidc_key.pem create mode 100644 server/test_oidc_key.pem.pub create mode 100644 server/wilford/src/routes/well_known/jwks.rs create mode 100644 server/wilford/src/routes/well_known/mod.rs create mode 100644 server/wilford/src/routes/well_known/openid_configuration.rs create mode 100644 test_oidc_key.pem create mode 100644 test_oidc_key.pem.pub diff --git a/Makefile b/Makefile index 3229f3f..5b7f15d 100644 --- a/Makefile +++ b/Makefile @@ -11,8 +11,14 @@ help: echo "- upload-docs : Build and upload the docs Docker image" echo "- upload-ui : Build and upload the ui Docker image" +test_oidc_key.pem: + ssh-keygen -t rsa -b 4096 -m PEM -q -N "" -f ./test_oidc_key.pem + +config.json: + cp sample_config.json config.json + .PHONY: up -up: +up: test_oidc_key.pem config.json docker compose up -d echo "Wilford UI available at http://localhost:2522" echo "Wilford Docs available at http://localhost:2523" @@ -45,6 +51,6 @@ build-docs: .PHONY: build-ui build-ui: # Patch for production - sed -i "s|createWebHistory('/')|createWebHistory('/wilford')|" ui/src/router/index.ts + #sed -i "s|createWebHistory('/')|createWebHistory('/wilford')|" ui/src/router/index.ts - docker build -t registry.mrfriendly.uk/wilford-ui ui/ \ No newline at end of file + docker build -t registry.mrfriendly.uk/wilford-ui ui/ diff --git a/README.md b/README.md index 195d2c7..79ddfc3 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,14 @@ Your applications will then authenticate with Wilford, and your users can continue using their EspoCRM login credentials. ## Development -- Copy the sample config to `config.json` -- Start all docker containers with docker-compose: -``` -docker compose up -d +- Start everything with +```bash +make up ``` +This will: +- Create an OIDC signing key if it doesn't exist +- Copy `sample_config.json` to `config.json +- Start all containers The following services will be available: - The backend, on port [2521](http://localhost:2512) @@ -32,7 +35,12 @@ After starting, you should configure an API-client in EspoCRM: 10. Select the role you just created under `Roles` 11. Set `Authentication method` to `HMAC` and select `Save` 12. Copy the `API Key` and `Secret Key` to `config.json` -13. Hit Ctrl+C and run `docker compose up` again. +13. Restart Wilford +```bash +docker-compose down +make up +``` + # License MIT or Apache-2.0, at your option diff --git a/docker-compose.yml b/docker-compose.yml index a96dbca..a01d8d7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,11 +25,13 @@ services: context: server dockerfile: Dockerfile ports: - - "2521:8080" + - "2521:2521" environment: - "CONFIG_PATH=/config.json" + - "RUST_LOG=DEBUG" volumes: - "./config.json:/config.json" + - "./test_oidc_key.pem:/oidc.pem" depends_on: - "mariadb-wilford" diff --git a/docs/src/api/clients/internal.md b/docs/src/api/clients/internal.md index 5324d73..99b64b2 100644 --- a/docs/src/api/clients/internal.md +++ b/docs/src/api/clients/internal.md @@ -9,7 +9,6 @@ This endpoint provides information about the OAuth2 client { "name": "", "client_id": "", - "client_secret": "", "redirect_uri": "" } ``` \ No newline at end of file diff --git a/docs/src/api/user/index.md b/docs/src/api/user/index.md index dfe60da..b44bd50 100644 --- a/docs/src/api/user/index.md +++ b/docs/src/api/user/index.md @@ -1,2 +1,2 @@ # User -User info & user managment \ No newline at end of file +User info & user management \ No newline at end of file diff --git a/docs/src/introduction.md b/docs/src/introduction.md index c88fcb4..1a7207c 100644 --- a/docs/src/introduction.md +++ b/docs/src/introduction.md @@ -3,7 +3,14 @@ Wilford is a OAuth2 Provider using EspoCRM as its credentials provider. ## Sources -Wilford's implementation of OAuth2 is derived from the following documents: +Wilford's implementation of OAuth2 and OpenID Connect is derived from the following documents: - [RFC6749](https://datatracker.ietf.org/doc/html/rfc6749) - [RFC6750](https://datatracker.ietf.org/doc/html/rfc6750) -- [RFC7662](https://datatracker.ietf.org/doc/html/rfc7662) \ No newline at end of file +- [RFC7662](https://datatracker.ietf.org/doc/html/rfc7662) +- [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) + +## TODO +Not everything is implemented 100%. I'd like to add support for: +- [A.4](https://openid.net/specs/openid-connect-core-1_0.html#code-id_tokenExample) (`response_type=code id_token`). +At the moment only `response_type=id_token token` is supported. +- User information in the returned JWTs \ No newline at end of file diff --git a/docs/src/oauth2/authorization.md b/docs/src/oauth2/authorization.md index a3d38ed..cc2da46 100644 --- a/docs/src/oauth2/authorization.md +++ b/docs/src/oauth2/authorization.md @@ -5,15 +5,17 @@ Logging in using Wilford OAuth2 authentication. The following steps give a global outline of the OAuth2 login process 1. Your application redirects the resource owner to Wilford (Authorization step) 2. Resource owner logs in with Wilford -2. Resourec owner is redirected to your client -3. Your client exchanges the authorization code for an access token and refresh token +3. Resource owner is redirected to your client +4. Your client exchanges the authorization code for an access token and refresh token ## Authorization Supported OAuth2 flows: 1. Authorization Code 2. Implicit -### Authorization Code +Furthermore, Wilford supports the [OpenID Connect](https://openid.net/specs/openid-connect-core-1_0.html) flow. + +### Authorization Code Flow Redirect the resource owner to `/api/oauth/authorize` with the following query parameters (`application/x-www-form-urlencoded`): ``` response_type: code @@ -22,12 +24,13 @@ redirect_uri: scope: state: ``` -The state parameter will be given back to your after the authorization, unmodified. + +The state parameter will be given back to you after the authorization, unmodified. #### Success 1. The resource owner will be redirected to Wilford's login page, where they must log in using their EspoCRM credentials. 2. The resource owner will be asked to grant your client access -3. The resource owner will be redirected to your `redirect_uri`. The `scope` parameter will contain the the scopes that were authorized. The `state` parameter contains the `state` you provided earlier (Optional). The authorization code is provided in the `code` query parameter. +3. The resource owner will be redirected to your `redirect_uri`. The `scope` parameter will contain the scopes that were authorized. The `state` parameter contains the `state` you provided earlier (Optional). The authorization code is provided in the `code` query parameter. You should now exchange the `code` for an access- and refresh token using the Token endpoint. @@ -35,7 +38,7 @@ You should now exchange the `code` for an access- and refresh token using the To 1. The resource owner will be redirected to your `redirect_uri`. The `error` query parameter will contain the error. The `error` parameter will contain a value as described per [RFC6749 Section 4.1.2.1](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1) -### Implicit +### Implicit Flow Redirect the resource owner to `/api/oauth/authorize` with the following query parameters (`application/x-www-form-urlencoded`): ``` response_type: token @@ -44,7 +47,7 @@ redirect_uri: scope: state: ``` -The state parameter will be given back to your after the authorization, unmodified. +The state parameter will be given back to you after the authorization, unmodified. #### Success 1. The resource owner will be redirected to Wilford's login page, where they must log in using their EspoCRM credentials. @@ -57,10 +60,33 @@ This is the end of the implicit flow. 1. The resource owner will be redirected to your `redirect_uri`. The `error` query parameter will contain the error. The `error` parameter will contain a value as described per [RFC6749 Section 4.1.2.1](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1) +### OpenID Flow +Redirect the resource owner to `/api/oauth/authorize` with the following query parameters (`application/x-www-form-urlencoded`): +``` +response_type: id_token token +client_id: +redirect_uri: +scope: +state: +nonce: +``` + +The state parameter will be given back to you after the authorization, unmodified. The nonce +parameter will be passed to the JWT token unmodified if OpenID is used. + +#### Success +1. The resource owner will be redirected to Wilford's login page, where they must log in using their EspoCRM credentials. +2. The resource owner will be asked to grant your client access +3. The resource owner will be redirected to your `redirect_uri`. +The `scope` parameter will contain the scopes that were authorized. +The `state` parameter contains the `state` you provided earlier (Optional). +The `id_token` parameter contains the JWT. +The `access_token` parameter contains the OAuth2 access token. + ## Token exchange >Note: This endpoint is only useful if you used the Authorization Code flows -If the previous step went successfull, your client can now exchange the authorization grant for an access- and refresh token. The `code` is contained in the query parameters, as described in the previous step. +If the previous step went successful, your client can now exchange the authorization grant for an access- and refresh token. The `code` is contained in the query parameters, as described in the previous step. Your client should send a `POST` request to `/api/oauth/token` with the following body (`application/x-www-form-urlencoded`): ``` @@ -78,7 +104,7 @@ client_secret: "token_type": "bearer", "expires_in": 3600, "refresh_token": "", - "scope": "", + "scope": "" } ``` Your application can now use the `access_token` to communicate with resource servers. diff --git a/docs/src/oauth2/introspect.md b/docs/src/oauth2/introspect.md index 5c90774..41cd8a6 100644 --- a/docs/src/oauth2/introspect.md +++ b/docs/src/oauth2/introspect.md @@ -1,4 +1,4 @@ -# Instrospect +# Introspect Token introspection endpoint. See also: [RFC7662](https://datatracker.ietf.org/doc/html/rfc7662) diff --git a/oauth2_proxy.docker-compose.yml b/oauth2_proxy.docker-compose.yml new file mode 100644 index 0000000..cde990e --- /dev/null +++ b/oauth2_proxy.docker-compose.yml @@ -0,0 +1,14 @@ +version: '3.2' +services: + oauth2_proxy: + image: quay.io/oauth2-proxy/oauth2-proxy + environment: + - "OAUTH2_PROXY_COOKIE_SECRET=VsZqXqHQzwdPUcEUDgNxmQvTRZ46DtlQr8q-HtomkL8=" + - "OAUTH2_PROXY_CLIENT_ID=Vy1l9P2X7RFf4jHfo3OiLHtShllSZKDa" + - "OAUTH2_PROXY_CLIENT_SECRET=d4HtCZLeZvRvldGOxQd18l5a46hZEl6WWoocL2IbkfcozLH6" + - "OAUTH2_PROXY_OIDC_ISSUER_URL=http://localhost:2521" + - "OAUTH2_PROXY_REDIRECT_URL=http://127.0.0.1:4180/oauth2/callback" + - "OAUTH2_PROXY_PROVIDER=oidc" + - "OAUTH2_PROXY_EMAIL_DOMAINS=*" + - "OAUTH2_PROXY_OIDC_JWKS_URL=http://localhost:2521/.well-known/jwks.json" + network_mode: "host" \ No newline at end of file diff --git a/sample_config.json b/sample_config.json index f147ca7..e54988f 100644 --- a/sample_config.json +++ b/sample_config.json @@ -1,19 +1,24 @@ { - "http": { - "ui_login_path": "http://localhost:2522/login" - }, - "database": { - "user": "wilford", - "password": "wilford", - "host": "mariadb-wilford", - "database": "wilford" - }, - "espo": { - "host": "http://espocrm", - "api_key": "", - "secret_key": "" - }, - "default_client": { - "redirect_uri": "http://localhost:2522/login-ok" - } - } \ No newline at end of file + "http": { + "ui_login_path": "http://localhost:2522/login", + "authorization_endpoint": "http://localhost:2521/api/oauth/authorize", + "token_endpoint": "http://localhost:2521/api/oauth/token", + "jwks_uri_endpoint": "https://localhost:2521/.well-known/jwks.json" + }, + "database": { + "user": "wilford", + "password": "wilford", + "host": "mariadb-wilford", + "database": "wilford" + }, + "espo": { + "host": "http://espocrm", + "api_key": "", + "secret_key": "" + }, + "default_client": { + "redirect_uri": "http://localhost:2522/login-ok" + }, + "oidc_signing_key": "/oidc.pem", + "oidc_issuer": "http://localhost:2521" +} \ No newline at end of file diff --git a/server/Cargo.lock b/server/Cargo.lock index 791deb0..10df5e2 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -80,7 +80,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.43", + "syn 2.0.63", ] [[package]] @@ -218,7 +218,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.63", ] [[package]] @@ -279,6 +279,24 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +[[package]] +name = "anyhow" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "atoi" version = "2.0.0" @@ -319,6 +337,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.13.1" @@ -331,12 +355,24 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + [[package]] name = "base64ct" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "binstring" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e0d60973d9320722cb1206f412740e162a33b8547ea8d6be75d7cff237c7a85" + [[package]] name = "bitflags" version = "1.3.2" @@ -352,6 +388,17 @@ dependencies = [ "serde", ] +[[package]] +name = "blake2b_simd" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -425,6 +472,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "coarsetime" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b3839cf01bb7960114be3ccf2340f541b6d0c81f8690b007b2b39f750f7e5d" +dependencies = [ + "libc", + "wasix", + "wasm-bindgen", +] + [[package]] name = "color-eyre" version = "0.6.2" @@ -458,6 +516,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + [[package]] name = "convert_case" version = "0.4.0" @@ -543,6 +607,18 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -553,11 +629,19 @@ dependencies = [ "typenum", ] +[[package]] +name = "ct-codecs" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df" + [[package]] name = "database" version = "0.1.0" dependencies = [ + "jwt-simple", "rand", + "serde", "sqlx", "thiserror", "time", @@ -615,6 +699,30 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519-compact" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b3460f44bea8cd47f45a0c70892f1eff856d97cd55358b2f73f663789f6190" +dependencies = [ + "ct-codecs", + "getrandom", +] + [[package]] name = "either" version = "1.9.0" @@ -624,6 +732,27 @@ dependencies = [ "serde", ] +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "encoding_rs" version = "0.8.33" @@ -706,6 +835,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "finl_unicode" version = "1.2.0" @@ -800,7 +939,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.63", ] [[package]] @@ -840,6 +979,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -849,8 +989,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -859,6 +1001,17 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "h2" version = "0.3.22" @@ -936,6 +1089,30 @@ dependencies = [ "digest", ] +[[package]] +name = "hmac-sha1-compact" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9d405ec732fa3fcde87264e54a32a84956a377b3e3107de96e59b798c84a7" + +[[package]] +name = "hmac-sha256" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3688e69b38018fec1557254f64c8dc2cc8ec502890182f395dbb0aa997aa5735" +dependencies = [ + "digest", +] + +[[package]] +name = "hmac-sha512" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ce1f4656bae589a3fab938f9f09bf58645b7ed01a2c5f8a3c238e01a4ef78a" +dependencies = [ + "digest", +] + [[package]] name = "home" version = "0.5.9" @@ -1082,6 +1259,46 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jwt-simple" +version = "0.12.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "094661f5aad510abe2658bff20409e89046b753d9dc2d4007f5c100b6d982ba0" +dependencies = [ + "anyhow", + "binstring", + "blake2b_simd", + "coarsetime", + "ct-codecs", + "ed25519-compact", + "hmac-sha1-compact", + "hmac-sha256", + "hmac-sha512", + "k256", + "p256", + "p384", + "rand", + "serde", + "serde_json", + "superboring", + "thiserror", + "zeroize", +] + +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + [[package]] name = "language-tags" version = "0.3.2" @@ -1350,6 +1567,30 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -1379,6 +1620,16 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.0", + "serde", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -1411,7 +1662,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.63", ] [[package]] @@ -1465,11 +1716,20 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" -version = "1.0.71" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -1485,9 +1745,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -1615,6 +1875,16 @@ dependencies = [ "winreg", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "ring" version = "0.17.7" @@ -1643,6 +1913,7 @@ dependencies = [ "pkcs1", "pkcs8", "rand_core", + "sha2", "signature", "spki", "subtle", @@ -1730,6 +2001,20 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "semver" version = "1.0.20" @@ -1738,29 +2023,29 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.63", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -2117,6 +2402,19 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "superboring" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbde97f499e51ef384f585dc8f8fb6a9c3a71b274b8d12469b516758e6540607" +dependencies = [ + "getrandom", + "hmac-sha256", + "hmac-sha512", + "rand", + "rsa", +] + [[package]] name = "syn" version = "1.0.109" @@ -2130,9 +2428,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.43" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", @@ -2181,22 +2479,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.63", ] [[package]] @@ -2280,7 +2578,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.63", ] [[package]] @@ -2357,7 +2655,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.63", ] [[package]] @@ -2519,6 +2817,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasix" +version = "0.12.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" +dependencies = [ + "wasi", +] + [[package]] name = "wasm-bindgen" version = "0.2.89" @@ -2540,7 +2847,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.63", "wasm-bindgen-shared", ] @@ -2574,7 +2881,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.63", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2621,6 +2928,7 @@ dependencies = [ "envy", "espocrm-rs", "noiseless-tracing-actix-web", + "pem", "reqwest", "serde", "serde_json", @@ -2814,7 +3122,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.63", ] [[package]] diff --git a/server/database/Cargo.toml b/server/database/Cargo.toml index 2d27c16..d7ece18 100644 --- a/server/database/Cargo.toml +++ b/server/database/Cargo.toml @@ -3,11 +3,11 @@ name = "database" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] rand = "0.8.5" sqlx = { version = "0.7.3", features = ["mysql", "migrate", "runtime-tokio-rustls"] } thiserror = "1.0.51" time = "0.3.31" tracing = "0.1.40" +jwt-simple = { version = "0.12.9", default-features = false, features = ["pure-rust"]} +serde = { version = "1.0.193", features = ["derive"] } \ No newline at end of file diff --git a/server/database/migrations/1_initial.sql b/server/database/migrations/1_initial.sql index 2a59903..a889ce6 100644 --- a/server/database/migrations/1_initial.sql +++ b/server/database/migrations/1_initial.sql @@ -14,6 +14,7 @@ CREATE TABLE oauth2_pending_authorizations ( state TEXT DEFAULT NULL, espo_user_id TEXT DEFAULT NULL, ty TEXT NOT NULL, + nonce TEXT DEFAULT NULL, PRIMARY KEY (id) ); @@ -41,6 +42,7 @@ CREATE TABLE oauth2_authorization_codes ( expires_at BIGINT NOT NULL, scopes TEXT DEFAULT NULL, espo_user_id TEXT NOT NULL, + nonce TEXT DEFAULT NULL, PRIMARY KEY (code) ); diff --git a/server/database/src/oauth2_client.rs b/server/database/src/oauth2_client.rs index 764594d..fc9b768 100644 --- a/server/database/src/oauth2_client.rs +++ b/server/database/src/oauth2_client.rs @@ -1,9 +1,13 @@ use crate::driver::Database; use crate::{generate_string, impl_enum_type}; +use jwt_simple::algorithms::{RS256KeyPair, RSAKeyPairLike}; +use jwt_simple::claims::Claims; +use serde::{Deserialize, Serialize}; use sqlx::{Decode, Encode, FromRow, Result}; use std::collections::HashSet; use thiserror::Error; use time::{Duration, OffsetDateTime}; +use tracing::info; #[derive(Debug, Clone, FromRow)] pub struct OAuth2Client { @@ -55,6 +59,13 @@ impl OAuth2PendingAuthorization { Self::EspoUnauthorized(v) => &v.scopes, } } + + pub fn nonce(&self) -> &Option { + match self { + Self::EspoAuthorized(v) => &v.nonce, + Self::EspoUnauthorized(v) => &v.nonce, + } + } } #[derive(Debug, Clone)] @@ -64,6 +75,7 @@ pub struct OAuth2PendingAuthorizationUnauthorized { scopes: Option, state: Option, ty: AuthorizationType, + nonce: Option, } #[derive(Debug, Clone)] @@ -74,6 +86,7 @@ pub struct OAuth2PendingAuthorizationAuthorized { state: Option, espo_user_id: String, ty: AuthorizationType, + nonce: Option, } #[derive(FromRow)] @@ -84,6 +97,7 @@ struct _OAuth2PendingAuthorization { state: Option, espo_user_id: Option, ty: AuthorizationType, + nonce: Option, } #[derive(FromRow)] @@ -93,6 +107,7 @@ pub struct OAuth2AuthorizationCode { pub expires_at: i64, pub scopes: Option, pub espo_user_id: String, + pub nonce: Option, } #[derive(Clone, Debug, FromRow)] @@ -133,6 +148,7 @@ pub enum OAuth2PendingAuthorizationSetEspoIdError { pub enum AuthorizationType { AuthorizationCode, Implicit, + IdToken, } impl_enum_type!(AuthorizationType); @@ -226,14 +242,16 @@ impl OAuth2Client { scopes: Option, state: Option, ty: AuthorizationType, + nonce: Option, ) -> Result { let id = Self::generate_pending_authorization_id(); - sqlx::query("INSERT INTO oauth2_pending_authorizations (id, client_id, scopes, state, ty) VALUES (?, ?, ?, ?, ?)") + sqlx::query("INSERT INTO oauth2_pending_authorizations (id, client_id, scopes, state, ty, nonce) VALUES (?, ?, ?, ?, ?, ?)") .bind(&id) .bind(&self.client_id) .bind(&scopes) .bind(&state) .bind(&ty) + .bind(&nonce) .execute(&**driver) .await?; @@ -244,6 +262,7 @@ impl OAuth2Client { scopes, state, ty, + nonce, }, )) } @@ -265,12 +284,13 @@ impl OAuth2Client { let mut tx = driver.begin().await?; - sqlx::query("INSERT INTO oauth2_authorization_codes (client_id, code, expires_at, scopes, espo_user_id) VALUES (?, ?, ?, ?, ?)") + sqlx::query("INSERT INTO oauth2_authorization_codes (client_id, code, expires_at, scopes, espo_user_id, nonce) VALUES (?, ?, ?, ?, ?, ?)") .bind(&self.client_id) .bind(&code) .bind(expires_at) .bind(&pending.scopes) .bind(&pending.espo_user_id) + .bind(&pending.nonce) .execute(&mut *tx) .await?; @@ -287,6 +307,7 @@ impl OAuth2Client { scopes: pending.scopes.clone(), expires_at, espo_user_id: pending.espo_user_id, + nonce: pending.nonce, }) } @@ -512,6 +533,7 @@ impl OAuth2PendingAuthorization { state: v.state, scopes: v.scopes, ty: v.ty, + nonce: v.nonce, }) } Self::EspoAuthorized(_) => unreachable!(), @@ -542,6 +564,7 @@ impl From<_OAuth2PendingAuthorization> for OAuth2PendingAuthorization { state: value.state, espo_user_id, ty: value.ty, + nonce: value.nonce, }) } else { Self::EspoUnauthorized(OAuth2PendingAuthorizationUnauthorized { @@ -550,7 +573,80 @@ impl From<_OAuth2PendingAuthorization> for OAuth2PendingAuthorization { scopes: value.scopes, state: value.state, ty: value.ty, + nonce: value.nonce, }) } } } + +#[derive(Serialize, Deserialize)] +pub struct IdTokenClaims { + /// Issuer Identifier for the Issuer of the response. The iss value is a case-sensitive URL using the https scheme that contains scheme, host, and optionally, port number and path components and no query or fragment components. + iss: String, + /// Subject Identifier. + /// A locally unique and never reassigned identifier within the Issuer for the End-User, which is intended to be consumed by the Client, e.g., 24400320 or AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4. + /// It MUST NOT exceed 255 ASCII [RFC20] characters in length. The sub value is a case-sensitive string. + sub: String, + /// Audience(s) that this ID Token is intended for. It MUST contain the OAuth 2.0 client_id of the Relying Party as an audience value. It MAY also contain identifiers for other audiences. + /// In the general case, the aud value is an array of case-sensitive strings. In the common special case when there is one audience, the aud value MAY be a single case-sensitive string. + aud: String, + /// Expiration time on or after which the ID Token MUST NOT be accepted anymore. + exp: i64, + /// Time at which the JWT was issued. Its value is a JSON number representing the number of seconds from 1970-01-01T00:00:00Z as measured in UTC until the date/time. + iat: i64, + /// String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token + nonce: Option, + /// Authorized party - the party to which the ID Token was issued. If present, it MUST contain the OAuth 2.0 Client ID of this party. + azp: String, +} + +pub enum JwtSigningAlgorithm { + RS256, +} + +#[derive(Debug, Error)] +pub enum IdTokenCreationError { + #[error("Invalid keypair: {0}")] + Keypair(String), + #[error("Signing failed: {0}")] + Signing(String), +} + +pub fn create_id_token( + issuer: String, + client: &OAuth2Client, + end_user_id: String, + oidc_signing_key_pem: &str, + access_token: &AccessToken, + nonce: Option, + jwt_signing_algorithm: JwtSigningAlgorithm, +) -> std::result::Result { + let iat = OffsetDateTime::now_utc(); + + let id_claims = IdTokenClaims { + iss: issuer, + sub: end_user_id, + aud: client.client_id.clone(), + exp: access_token.expires_at, + iat: iat.unix_timestamp(), + nonce, + azp: client.client_id.clone(), + }; + + match jwt_signing_algorithm { + JwtSigningAlgorithm::RS256 => { + let key = RS256KeyPair::from_pem(oidc_signing_key_pem) + .map_err(|e| IdTokenCreationError::Keypair(e.to_string()))?; + + let claims = Claims::with_custom_claims( + id_claims, + jwt_simple::reexports::coarsetime::Duration::from_secs( + (access_token.expires_at - iat.unix_timestamp()) as u64, + ), + ); + Ok(key + .sign(claims) + .map_err(|e| IdTokenCreationError::Signing(e.to_string()))?) + } + } +} diff --git a/server/test_oidc_key.pem b/server/test_oidc_key.pem new file mode 100644 index 0000000..293fc1d --- /dev/null +++ b/server/test_oidc_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDFvqxeV+Tk/V/u +bzPuRrhjDpIZKaXz2G7zJUKqbIKXcbEqSX3uHRiTx9AsRZgnjHRM7bA0QmDM8Jd1 +axFpYnN4EKfUWC1PN/g6Su6cukGSPElP3wiD3X4oqSNPwtvziEyvVBAyeW+n0n7N +ruM2sqi0JbzpO3E9OX2LoZJ4xENjIcigcM6j5Cm6yRoZIADWlxSsUbKpA1p5mzEh +iXsxjGCHo7oDURem7Um0DV9eiTCVhdH5CLN9H5Ef25MVIZ7BrU9FP1bTC9+wBeQb +5+hShO/ghFq3tsmM8fP0th5OcI31x5+vifQPjBJEckaERk1LAu/LsZTeIajYh39/ +CJRweLI7AgMBAAECggEAQsLDEv79qknYz3j/wVxi+B59OL2U56SUoMZdoYgM9vBN +bgdt2qw8FfiJ0ciVPBTxVt+Qc7RNViOMHvpM4JV6gnitEMXMVkiBJ70Qnh1L8gEk +hzC3gT2QUE36O/1/oNLNgQwSg9f32GL6qJp434il6FwtUVjePFtihXfBKcCpN5gi +ZWBB3jR3ygwHh6Sqy0gH26GPVkl/ZdjV6711kaAUEXojiJPDTUs9QMtd+5/NVW12 +/BegDRGfX9fHf0G0Onlys1zdkjydBoH8HTdKmSsydIZs4HVnXvgz+nM9V/6pjqk9 +hctSg6jn57BDzoRye3MBP8FAOzDM8I8jlggpPthFcQKBgQDhkyHoBACWpbora1WZ +rkh+3ioM5TujzFILzYtUD+T/1C9lLJQsDzKvEbk95WYWgzZRAj8ZD8v+A/Ktj4Lp +Rx+90GnBVwIAhkZNOyq7s0oLs4vaF1LnxqqE46plcDKnEvKirVbbajK/yGdaIVz4 +yxK3uTW58AyTqE7TYfYiwvsxLQKBgQDgapoXlXjUMVxjMYxzm4MxW4xXqxpfvyuf +IaWacxyEV4/y+HwJXKVPCOD55FkfhLTaYgMa3slgUo21bLULjuWgTZIG4cK/TyeH +JtxSuoPDnuE1CliLkq5rftT5jciEURAKO5nECLJNId0E4f6VmCYgqy4gn4hXs2EM +31BUTRgCBwKBgDqRcSREpcJ3lTbgz4Dvd3M5S8G9YxbMXiEiZSKWXebuuL+UK4Sl +DYvXFjmz7iW6O8gcxTVEnLp/W+skb+ERznQCQPMqHCL2uhojWp0RmBAhthYSKDx1 +oKCQXBWqY/xtNwJ17y9ZC4QwDGWic/lFAL8jH4f/g8Ry1sSbksTDy2ShAoGAFvhS +VV+jQnTa8/w6WAru3rp0HvUQegRjPqJ/BNQLczBTN5dfesXU1cYe4PUSejtnF4M4 +tiNfkVBh2NQLo+c4cbP+4l1xRYugjCDaJE2a9wboHjm03nRhDfepbPGqOIDjl2iK +UJpjRanVqY5INJ+iJ23eBtDNb8yYfmzNGBPcBTkCgYEAykREjRxjaPUDd5zqOkwq +vHNmgn0OD/9Jp9YKHG/D42HaLbbZ7ImQGNoR3P3zKpqvihXMOEU2GZjRJe89AjkM +vrof1tIY0vtFBC5wyt8Ye5xtF6x2SW+64aFRm/WElPtEz6rn7mH1alSzHmOJfQ3q +X09T48Qia7sp1g1DmQ1CkRw= +-----END PRIVATE KEY----- diff --git a/server/test_oidc_key.pem.pub b/server/test_oidc_key.pem.pub new file mode 100644 index 0000000..12c7195 --- /dev/null +++ b/server/test_oidc_key.pem.pub @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxb6sXlfk5P1f7m8z7ka4 +Yw6SGSml89hu8yVCqmyCl3GxKkl97h0Yk8fQLEWYJ4x0TO2wNEJgzPCXdWsRaWJz +eBCn1FgtTzf4OkrunLpBkjxJT98Ig91+KKkjT8Lb84hMr1QQMnlvp9J+za7jNrKo +tCW86TtxPTl9i6GSeMRDYyHIoHDOo+QpuskaGSAA1pcUrFGyqQNaeZsxIYl7MYxg +h6O6A1EXpu1JtA1fXokwlYXR+QizfR+RH9uTFSGewa1PRT9W0wvfsAXkG+foUoTv +4IRat7bJjPHz9LYeTnCN9cefr4n0D4wSRHJGhEZNSwLvy7GU3iGo2Id/fwiUcHiy +OwIDAQAB +-----END PUBLIC KEY----- diff --git a/server/wilford/Cargo.toml b/server/wilford/Cargo.toml index e87da5f..a341e7b 100644 --- a/server/wilford/Cargo.toml +++ b/server/wilford/Cargo.toml @@ -24,3 +24,4 @@ tokio = { version = "1.35.1", features = ["full"] } tracing = "0.1.40" tracing-actix-web = "0.7.9" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +pem = "3.0.4" \ No newline at end of file diff --git a/server/wilford/src/config.rs b/server/wilford/src/config.rs index da99c46..9923c4e 100644 --- a/server/wilford/src/config.rs +++ b/server/wilford/src/config.rs @@ -15,11 +15,17 @@ pub struct Config { pub database: DatabaseConfig, pub espo: EspoConfig, pub default_client: DefaultClientConfig, + pub oidc_signing_key: PathBuf, + pub oidc_public_key: PathBuf, + pub oidc_issuer: String, } #[derive(Debug, Deserialize)] pub struct HttpConfig { pub ui_login_path: String, + pub authorization_endpoint: String, + pub token_endpoint: String, + pub jwks_uri_endpoint: String, } #[derive(Debug, Deserialize)] @@ -56,6 +62,27 @@ impl Config { Ok(serde_json::from_slice(&buf)?) } + + pub async fn read_oidc_signing_key(&self) -> Result { + Self::read_pem(&self.oidc_signing_key).await + } + + pub async fn read_oidc_public_key(&self) -> Result { + Self::file_read_string(&self.oidc_public_key).await + } + + async fn read_pem(p: &Path) -> Result { + let contents = Self::file_read_string(p).await?; + let pem = pem::parse(contents.as_bytes())?; + Ok(pem.to_string()) + } + + async fn file_read_string(p: &Path) -> Result { + let mut f = fs::File::open(p).await?; + let mut buf = String::new(); + f.read_to_string(&mut buf).await?; + Ok(buf) + } } pub async fn get_config() -> Result { diff --git a/server/wilford/src/main.rs b/server/wilford/src/main.rs index 3b9393d..0648d9c 100644 --- a/server/wilford/src/main.rs +++ b/server/wilford/src/main.rs @@ -1,4 +1,5 @@ use crate::config::{get_config, DefaultClientConfig}; +use crate::routes::{OidcPublicKey, OidcSigningKey, WOidcPublicKey, WOidcSigningKey}; use actix_cors::Cors; use actix_route_config::Routable; use actix_web::{web, App, HttpServer}; @@ -16,8 +17,8 @@ use tracing_subscriber::EnvFilter; mod config; mod espo; -mod routes; mod response_types; +mod routes; #[tokio::main] async fn main() -> Result<()> { @@ -25,6 +26,7 @@ async fn main() -> Result<()> { install_tracing(); let config = get_config().await?; + let database = Database::new( &config.database.user, &config.database.password, @@ -40,8 +42,12 @@ async fn main() -> Result<()> { ensure_internal_oauth_client_exists(&database, &config.default_client).await?; let w_database = web::Data::new(database); - let w_config = web::Data::new(config); let w_espo = web::Data::new(espo_client); + let w_oidc_signing_key = + WOidcSigningKey::new(OidcSigningKey(config.read_oidc_signing_key().await?)); + let w_oidc_public_key = + WOidcPublicKey::new(OidcPublicKey(config.read_oidc_public_key().await?)); + let w_config = web::Data::new(config); HttpServer::new(move || { App::new() @@ -50,9 +56,11 @@ async fn main() -> Result<()> { .app_data(w_database.clone()) .app_data(w_config.clone()) .app_data(w_espo.clone()) + .app_data(w_oidc_signing_key.clone()) + .app_data(w_oidc_public_key.clone()) .configure(routes::Router::configure) }) - .bind("0.0.0.0:8080")? + .bind("0.0.0.0:2521")? .run() .await?; diff --git a/server/wilford/src/response_types/uncached.rs b/server/wilford/src/response_types/uncached.rs index a07585f..3ecb352 100644 --- a/server/wilford/src/response_types/uncached.rs +++ b/server/wilford/src/response_types/uncached.rs @@ -1,5 +1,5 @@ -use actix_web::{HttpRequest, HttpResponse, Responder}; use actix_web::http::header::HeaderValue; +use actix_web::{HttpRequest, HttpResponse, Responder}; use reqwest::header::HeaderName; pub struct Uncached(T); @@ -16,8 +16,11 @@ impl Responder for Uncached { fn respond_to(self, req: &HttpRequest) -> HttpResponse { let mut response = self.0.respond_to(req); let headers = response.headers_mut(); - headers.insert(HeaderName::from_static("cache-control"), HeaderValue::from_static("no-store")); + headers.insert( + HeaderName::from_static("cache-control"), + HeaderValue::from_static("no-store"), + ); response } -} \ No newline at end of file +} diff --git a/server/wilford/src/routes/appdata.rs b/server/wilford/src/routes/appdata.rs index c28b6f7..c4f2b84 100644 --- a/server/wilford/src/routes/appdata.rs +++ b/server/wilford/src/routes/appdata.rs @@ -6,3 +6,9 @@ use espocrm_rs::EspoApiClient; pub type WDatabase = web::Data; pub type WConfig = web::Data; pub type WEspo = web::Data; + +pub type WOidcSigningKey = web::Data; +pub type WOidcPublicKey = web::Data; + +pub struct OidcSigningKey(pub String); +pub struct OidcPublicKey(pub String); diff --git a/server/wilford/src/routes/error.rs b/server/wilford/src/routes/error.rs index 7bbe760..8c2475d 100644 --- a/server/wilford/src/routes/error.rs +++ b/server/wilford/src/routes/error.rs @@ -20,6 +20,8 @@ pub enum WebError { Database(#[from] database::driver::Error), #[error("EspoCRM error: {0}")] Espo(reqwest::Error), + #[error("Internal server error")] + InternalServerError, } impl ResponseError for WebError { @@ -32,6 +34,7 @@ impl ResponseError for WebError { Self::InvalidInternalState => StatusCode::INTERNAL_SERVER_ERROR, Self::Database(_) => StatusCode::INTERNAL_SERVER_ERROR, Self::Espo(_) => StatusCode::BAD_GATEWAY, + Self::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR, } } } diff --git a/server/wilford/src/routes/mod.rs b/server/wilford/src/routes/mod.rs index 340b300..918d972 100644 --- a/server/wilford/src/routes/mod.rs +++ b/server/wilford/src/routes/mod.rs @@ -7,12 +7,15 @@ mod auth; mod error; mod oauth; mod v1; +mod well_known; + +pub use appdata::*; pub struct Router; impl Routable for Router { fn configure(config: &mut ServiceConfig) { - config.service( + config.configure(well_known::Router::configure).service( web::scope("/api") .configure(v1::Router::configure) .configure(oauth::Router::configure), diff --git a/server/wilford/src/routes/oauth/authorize.rs b/server/wilford/src/routes/oauth/authorize.rs index 822452b..8785810 100644 --- a/server/wilford/src/routes/oauth/authorize.rs +++ b/server/wilford/src/routes/oauth/authorize.rs @@ -1,6 +1,6 @@ +use crate::response_types::{Redirect, Uncached}; use crate::routes::appdata::{WConfig, WDatabase}; use crate::routes::oauth::{OAuth2AuthorizationResponse, OAuth2Error, OAuth2ErrorKind}; -use crate::response_types::Redirect; use actix_web::web; use database::oauth2_client::{AuthorizationType, OAuth2Client}; use serde::Deserialize; @@ -13,9 +13,10 @@ pub struct Query { redirect_uri: String, scope: Option, state: Option, + nonce: Option, } -#[derive(Debug, Deserialize)] +#[derive(Debug, PartialEq, Eq, Deserialize)] pub enum ResponseType { #[serde(rename(deserialize = "code"))] /// Authorization Code flow @@ -25,13 +26,25 @@ pub enum ResponseType { /// Implicit Flow /// [RFC6749 Section 4.2](https://datatracker.ietf.org/doc/html/rfc6749#section-4.2) Token, + #[serde(rename(deserialize = "id_token token"))] + IdToken, } pub async fn authorize( database: WDatabase, config: WConfig, query: web::Query, -) -> OAuth2AuthorizationResponse { +) -> OAuth2AuthorizationResponse> { + // Required for the `id_token` response type + // OpenID Connect Core specification, section 3.2.2.1 + if query.response_type.eq(&ResponseType::IdToken) && query.nonce.is_none() { + return OAuth2AuthorizationResponse::Err(OAuth2Error::new( + OAuth2ErrorKind::InvalidRequest, + &query.redirect_uri, + query.state.as_deref(), + )); + } + // Get the OAuth2 client let client = match OAuth2Client::get_by_client_id(&database, &query.client_id).await { Ok(Some(c)) => c, @@ -61,30 +74,15 @@ pub async fn authorize( )); } - let pending_authorization = match query.response_type { - ResponseType::Code => { - // Create authorization - client - .new_pending_authorization( - &database, - query.scope.clone(), - query.state.clone(), - AuthorizationType::AuthorizationCode, - ) - .await - } - ResponseType::Token => { - // Create authorization - client - .new_pending_authorization( - &database, - query.scope.clone(), - query.state.clone(), - AuthorizationType::Implicit, - ) - .await - } - }; + let pending_authorization = client + .new_pending_authorization( + &database, + query.scope.clone(), + query.state.clone(), + rt_to_at(&query.response_type), + query.nonce.clone(), + ) + .await; let pending_authorization = match pending_authorization { Ok(pa) => pa, @@ -99,9 +97,17 @@ pub async fn authorize( }; // Redirect to login page - OAuth2AuthorizationResponse::Ok(Redirect::new(format!( + OAuth2AuthorizationResponse::Ok(Uncached::new(Redirect::new(format!( "{}?authorization={}", config.http.ui_login_path, pending_authorization.id(), - ))) + )))) +} + +fn rt_to_at(rt: &ResponseType) -> AuthorizationType { + match rt { + ResponseType::Code => AuthorizationType::AuthorizationCode, + ResponseType::Token => AuthorizationType::Implicit, + ResponseType::IdToken => AuthorizationType::IdToken, + } } diff --git a/server/wilford/src/routes/oauth/token.rs b/server/wilford/src/routes/oauth/token.rs index aff41a5..7c8f543 100644 --- a/server/wilford/src/routes/oauth/token.rs +++ b/server/wilford/src/routes/oauth/token.rs @@ -1,12 +1,15 @@ -use crate::routes::appdata::WDatabase; +use crate::response_types::Uncached; +use crate::routes::appdata::{WConfig, WDatabase}; use crate::routes::oauth::OAuth2ErrorKind; +use crate::routes::WOidcSigningKey; use actix_web::cookie::time::OffsetDateTime; use actix_web::web; -use database::oauth2_client::{OAuth2AuthorizationCode, OAuth2Client, RefreshToken}; +use database::oauth2_client::{ + create_id_token, JwtSigningAlgorithm, OAuth2AuthorizationCode, OAuth2Client, RefreshToken, +}; use serde::{Deserialize, Serialize}; use tap::TapFallible; use tracing::warn; -use crate::response_types::Uncached; #[derive(Deserialize)] pub struct Form { @@ -33,11 +36,14 @@ pub struct Response { expires_in: i64, refresh_token: String, scope: String, + id_token: String, } pub async fn token( database: WDatabase, form: web::Form, + config: WConfig, + oidc_signing_key: WOidcSigningKey, ) -> Result>, OAuth2ErrorKind> { let client = OAuth2Client::get_by_client_id(&database, &form.client_id) .await @@ -74,6 +80,8 @@ pub async fn token( return Err(OAuth2ErrorKind::InvalidGrant); } + let authorization_nonce = authorization.nonce.clone(); + let (atoken, rtoken) = client .new_token_pair(&database, authorization) .await @@ -81,6 +89,16 @@ pub async fn token( .map_err(|_| OAuth2ErrorKind::ServerError)?; Ok(Uncached::new(web::Json(Response { + id_token: create_id_token( + config.oidc_issuer.clone(), + &client, + rtoken.espo_user_id, + &oidc_signing_key.0, + &atoken, + authorization_nonce, + JwtSigningAlgorithm::RS256, + ) + .map_err(|_| OAuth2ErrorKind::ServerError)?, access_token: atoken.token, token_type: "bearer".to_string(), scope: atoken.scopes.unwrap_or_default(), @@ -111,6 +129,16 @@ pub async fn token( .map_err(|_| OAuth2ErrorKind::ServerError)?; Ok(Uncached::new(web::Json(Response { + id_token: create_id_token( + config.oidc_issuer.clone(), + &client, + rtoken.espo_user_id, + &oidc_signing_key.0, + &atoken, + None, + JwtSigningAlgorithm::RS256, + ) + .map_err(|_| OAuth2ErrorKind::ServerError)?, access_token: atoken.token, token_type: "bearer".to_string(), expires_in: atoken.expires_at - OffsetDateTime::now_utc().unix_timestamp(), diff --git a/server/wilford/src/routes/v1/auth/authorize.rs b/server/wilford/src/routes/v1/auth/authorize.rs index ef024d5..925456c 100644 --- a/server/wilford/src/routes/v1/auth/authorize.rs +++ b/server/wilford/src/routes/v1/auth/authorize.rs @@ -1,14 +1,20 @@ -use crate::routes::appdata::WDatabase; -use crate::routes::error::{WebError, WebResult}; -use crate::routes::oauth::{OAuth2AuthorizationResponse, OAuth2Error, OAuth2ErrorKind}; -use crate::response_types::Redirect; use actix_web::cookie::time::OffsetDateTime; use actix_web::web; +use database::driver::Database; +use serde::{Deserialize, Serialize}; +use tap::TapFallible; +use tracing::{instrument, warn}; + use database::oauth2_client::{ - AuthorizationType, OAuth2AuthorizationCodeCreationError, OAuth2Client, - OAuth2PendingAuthorization, + create_id_token, AccessToken, AuthorizationType, JwtSigningAlgorithm, + OAuth2AuthorizationCodeCreationError, OAuth2Client, OAuth2PendingAuthorization, }; -use serde::{Deserialize, Serialize}; + +use crate::response_types::Redirect; +use crate::routes::appdata::{WConfig, WDatabase}; +use crate::routes::error::{WebError, WebResult}; +use crate::routes::oauth::{OAuth2AuthorizationResponse, OAuth2Error, OAuth2ErrorKind}; +use crate::routes::WOidcSigningKey; #[derive(Deserialize)] pub struct Query { @@ -16,8 +22,11 @@ pub struct Query { grant: bool, } +#[instrument(skip_all)] pub async fn authorize( database: WDatabase, + oidc_signing_key: WOidcSigningKey, + config: WConfig, query: web::Query, ) -> WebResult> { let pending_authorization = @@ -68,39 +77,85 @@ pub async fn authorize( ) } AuthorizationType::Implicit => { - let access_token = client - .new_access_token(&database, pending_authorization) - .await - .map_err(|e| match e { - OAuth2AuthorizationCodeCreationError::Sqlx(e) => WebError::Database(e), - OAuth2AuthorizationCodeCreationError::Unauthorized => { - WebError::InvalidInternalState - } - })?; + let access_token = new_access_token(&client, pending_authorization, &database).await?; - #[derive(Serialize)] - struct RedirectFragment { - access_token: String, - #[serde(skip_serializing_if = "Option::is_none")] - state: Option, - token_type: &'static str, - expires_in: i64, - } + format!( + "{}#{}", + client.redirect_uri, + create_implicit_fragment(None, access_token, state), + ) + } + AuthorizationType::IdToken => { + let nonce = pending_authorization.nonce().clone(); + let access_token = new_access_token(&client, pending_authorization, &database).await?; format!( "{}#{}", client.redirect_uri, - serde_qs::to_string(&RedirectFragment { - access_token: access_token.token, - token_type: "bearer", - expires_in: access_token.expires_at - - OffsetDateTime::now_utc().unix_timestamp(), - state, - }) - .expect("Serializing query string"), + create_implicit_fragment( + Some( + create_id_token( + config.oidc_issuer.clone(), + &client, + access_token.espo_user_id.clone(), + &oidc_signing_key.0, + &access_token, + nonce, + JwtSigningAlgorithm::RS256, + ) + .tap_err(|e| warn!("Failed to create ID token: {e}")) + .map_err(|_| WebError::InternalServerError)? + ), + access_token, + state + ) ) } }; Ok(OAuth2AuthorizationResponse::Ok(Redirect::new(redirect_uri))) } + +/// Create a new OAuth2 access token. +/// Useful for the OAuth2 Implicit flow and the OpenID Connect IdToken flow. +async fn new_access_token( + client: &OAuth2Client, + pending_authorization: OAuth2PendingAuthorization, + database: &Database, +) -> WebResult { + Ok(client + .new_access_token(&database, pending_authorization) + .await + .map_err(|e| match e { + OAuth2AuthorizationCodeCreationError::Sqlx(e) => WebError::Database(e), + OAuth2AuthorizationCodeCreationError::Unauthorized => WebError::InvalidInternalState, + })?) +} + +#[derive(Serialize)] +struct RedirectFragment { + access_token: String, + #[serde(skip_serializing_if = "Option::is_none")] + state: Option, + token_type: &'static str, + expires_in: i64, + #[serde(skip_serializing_if = "Option::is_none")] + id_token: Option, +} + +/// Create the fragment string used for the OAuth2 implicit flow and the OpenID Connect IdToken flow. +/// The `id_token` string should only be supplied for the OpenID Connect IdToken flow. +fn create_implicit_fragment( + id_token: Option, + access_token: AccessToken, + state: Option, +) -> String { + serde_qs::to_string(&RedirectFragment { + access_token: access_token.token, + token_type: "bearer", + expires_in: access_token.expires_at - OffsetDateTime::now_utc().unix_timestamp(), + state, + id_token, + }) + .expect("Serializing query string") +} diff --git a/server/wilford/src/routes/v1/cat/add.rs b/server/wilford/src/routes/v1/cat/add.rs index 8f8c73a..0f64cff 100644 --- a/server/wilford/src/routes/v1/cat/add.rs +++ b/server/wilford/src/routes/v1/cat/add.rs @@ -1,6 +1,6 @@ +use crate::response_types::Empty; use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::response_types::Empty; use crate::routes::error::{WebError, WebResult}; use crate::routes::v1::MANAGE_SCOPE; use actix_web::web; diff --git a/server/wilford/src/routes/v1/cat/remove.rs b/server/wilford/src/routes/v1/cat/remove.rs index 9ed897d..65ff491 100644 --- a/server/wilford/src/routes/v1/cat/remove.rs +++ b/server/wilford/src/routes/v1/cat/remove.rs @@ -1,6 +1,6 @@ +use crate::response_types::Empty; use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::response_types::Empty; use crate::routes::error::{WebError, WebResult}; use crate::routes::v1::MANAGE_SCOPE; use actix_web::web; diff --git a/server/wilford/src/routes/v1/clients/add.rs b/server/wilford/src/routes/v1/clients/add.rs index 64e78d8..6a1648f 100644 --- a/server/wilford/src/routes/v1/clients/add.rs +++ b/server/wilford/src/routes/v1/clients/add.rs @@ -1,6 +1,6 @@ +use crate::response_types::Empty; use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::response_types::Empty; use crate::routes::error::{WebError, WebResult}; use crate::routes::v1::MANAGE_SCOPE; use actix_web::web; diff --git a/server/wilford/src/routes/v1/clients/remove.rs b/server/wilford/src/routes/v1/clients/remove.rs index 760d862..f988756 100644 --- a/server/wilford/src/routes/v1/clients/remove.rs +++ b/server/wilford/src/routes/v1/clients/remove.rs @@ -1,6 +1,6 @@ +use crate::response_types::Empty; use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::response_types::Empty; use crate::routes::error::{WebError, WebResult}; use crate::routes::v1::MANAGE_SCOPE; use actix_web::web; diff --git a/server/wilford/src/routes/v1/user/permitted_scopes/add.rs b/server/wilford/src/routes/v1/user/permitted_scopes/add.rs index 24c8b9f..d72d0f1 100644 --- a/server/wilford/src/routes/v1/user/permitted_scopes/add.rs +++ b/server/wilford/src/routes/v1/user/permitted_scopes/add.rs @@ -1,6 +1,6 @@ +use crate::response_types::Empty; use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::response_types::Empty; use crate::routes::error::{WebError, WebResult}; use crate::routes::v1::MANAGE_SCOPE; use actix_web::web; diff --git a/server/wilford/src/routes/v1/user/permitted_scopes/remove.rs b/server/wilford/src/routes/v1/user/permitted_scopes/remove.rs index 2dfad02..8c2d61c 100644 --- a/server/wilford/src/routes/v1/user/permitted_scopes/remove.rs +++ b/server/wilford/src/routes/v1/user/permitted_scopes/remove.rs @@ -1,6 +1,6 @@ +use crate::response_types::Empty; use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::response_types::Empty; use crate::routes::error::{WebError, WebResult}; use crate::routes::v1::MANAGE_SCOPE; use actix_web::web; diff --git a/server/wilford/src/routes/well_known/jwks.rs b/server/wilford/src/routes/well_known/jwks.rs new file mode 100644 index 0000000..bc16e49 --- /dev/null +++ b/server/wilford/src/routes/well_known/jwks.rs @@ -0,0 +1,27 @@ +use crate::routes::appdata::WOidcPublicKey; +use actix_web::web; +use serde::Serialize; + +#[derive(Serialize)] +pub struct Jwks { + keys: Vec, +} + +#[derive(Serialize)] +pub struct Key { + kty: String, + r#use: String, + alg: String, + k: String, +} + +pub async fn jwks(oidc_public_key: WOidcPublicKey) -> web::Json { + web::Json(Jwks { + keys: vec![Key { + kty: "RSA".to_string(), + r#use: "verify".to_string(), + alg: "RS256".to_string(), + k: (**oidc_public_key).0.clone(), + }], + }) +} diff --git a/server/wilford/src/routes/well_known/mod.rs b/server/wilford/src/routes/well_known/mod.rs new file mode 100644 index 0000000..5be1274 --- /dev/null +++ b/server/wilford/src/routes/well_known/mod.rs @@ -0,0 +1,21 @@ +mod jwks; +mod openid_configuration; + +use actix_route_config::Routable; +use actix_web::web; +use actix_web::web::ServiceConfig; + +pub struct Router; + +impl Routable for Router { + fn configure(config: &mut ServiceConfig) { + config.service( + web::scope("/.well-known") + .route( + "/openid-configuration", + web::get().to(openid_configuration::openid_configuration), + ) + .route("/jwks.json", web::get().to(jwks::jwks)), + ); + } +} diff --git a/server/wilford/src/routes/well_known/openid_configuration.rs b/server/wilford/src/routes/well_known/openid_configuration.rs new file mode 100644 index 0000000..210db9f --- /dev/null +++ b/server/wilford/src/routes/well_known/openid_configuration.rs @@ -0,0 +1,28 @@ +use crate::routes::appdata::WConfig; +use actix_web::web; +use serde::Serialize; + +#[derive(Serialize)] +pub struct OpenidConfiguration { + issuer: String, + authorization_endpoint: String, + token_endpoint: String, + response_types_supported: Vec, + grant_types_supported: Vec, + id_token_signing_alg_values_supported: Vec, +} + +pub async fn openid_configuration(config: WConfig) -> web::Json { + web::Json(OpenidConfiguration { + issuer: config.oidc_issuer.clone(), + authorization_endpoint: config.http.authorization_endpoint.clone(), + token_endpoint: config.http.token_endpoint.clone(), + response_types_supported: vec![ + "code".to_string(), + "id_token token".to_string(), + "token".to_string(), + ], + grant_types_supported: vec!["authorization_code".to_string(), "implicit".to_string()], + id_token_signing_alg_values_supported: vec!["RS256".to_string()], + }) +} diff --git a/test_oidc_key.pem b/test_oidc_key.pem new file mode 100644 index 0000000..a2a153e --- /dev/null +++ b/test_oidc_key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/test_oidc_key.pem.pub b/test_oidc_key.pem.pub new file mode 100644 index 0000000..6581061 --- /dev/null +++ b/test_oidc_key.pem.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCla6LsWXmoO6UTF0C1lJ2erWwQoq+5M3jfBvqmUAutZVjvcmHebfdbqCfHwCahF50IN3rkL5zQyd/mXdZs1bRvoRzN03MprDqenxuyHwtiGJYstvdzz8XJp/Taxc9C3WSRbSdHVpTg9AzaQxyakWoOm7b9eqhhJ5Ga3oHvLTCS0dUl9gyWmgncRAXzZZOWFcnp8PcbwUBrejc8837Dpk02HASdt9zB+dwJ59fTOBASduQ/Mr1m63NAqgi4yVRTjmX6QFNjZT32wHrUbQQ9CNIARQMOc4slIqi2WKWmcu6qJNMFs9Ztg2FAoDO0BmkPJXBGvv2MYfMEyaV/D9qf2dUz1wLwlvUKyPOFh6D7cC4i2AQm26KSUM7VcwLpW8frKao9q9Zw5WEUE/9riM+HErxOkbifeOT4m/PLPQS0dnYM+Abda9bJ037pLpaWXxWaHuteOdCM9XVKUzNWW3EpGl0FDQBfVtiutDefUuD5a7bNUHbm/LVv0vzayENEdWXYLyQfJ4468Jb4nRONv5wYH3Td0HKThV70f0O764OSFKIhhwX2zQIceOj9fu7nCkfnCUzw0J2KEFEDTGdTejpKcTEcixw5uaFLdqQW9QJcJ4MQcSJb6KwGK1eNwxcbuIpvX8HoQJoVBp728svD7fSvqkMi9FlLZlk2wELNepWORijiVQ== tobias@tobias-desktop diff --git a/ui/src/main.ts b/ui/src/main.ts index 43ef859..9f0b6dc 100644 --- a/ui/src/main.ts +++ b/ui/src/main.ts @@ -7,10 +7,10 @@ // Plugins import { registerPlugins } from '@/plugins' -export const server = -window.location.host.includes("localhost") ? "http://localhost:2521" // Dev, in docker -: window.location.host.includes("127.0.0.1") ? "http://localhost:8080" // Dev, not docker -: "/wilford"; // Production +const isLocalhost = window.location.host.includes('localhost'); +export const server = isLocalhost + ? "http://localhost:2521" + : "/api"; // Components import App from './App.vue' diff --git a/ui/src/router/index.ts b/ui/src/router/index.ts index 6015f11..6f94cc4 100644 --- a/ui/src/router/index.ts +++ b/ui/src/router/index.ts @@ -52,7 +52,7 @@ const routes = [ ] const router = createRouter({ - history: createWebHistory('/wilford'), + history: createWebHistory(), routes, }) From fdc109c85bbfa0aae164bdedd51b6c5394be275f Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Thu, 16 May 2024 13:44:45 +0200 Subject: [PATCH 03/17] Cargo CI --- .github/workflows/cargo.yml | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/cargo.yml diff --git a/.github/workflows/cargo.yml b/.github/workflows/cargo.yml new file mode 100644 index 0000000..98cd65e --- /dev/null +++ b/.github/workflows/cargo.yml @@ -0,0 +1,41 @@ +name: Cargo CI + +on: + push: + pull_request: + +env: + CARGO_TERM_COLOR: always + +jobs: + fmt: + name: Cargo fmt + runs-on: ubuntu-latest + strategy: + matrix: + toolchains: + - stable + + steps: + - uses: actions/checkout@v4 + - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} + - run: rustup component add --toolchain ${{ matrix.toolchain }} rustfmt + - run: cargo fmt --all --check + + build: + name: Cargo build + runs-on: ubuntu-latest + strategy: + matrix: + toolchains: + - stable + - nightly + targets: + - x86_64-unknown-linux-gnu + - x86_64-unknown-linux-musl + + steps: + - uses: actions/checkout@v4 + - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} + - run: rustup target add --toolchain ${{ matrix.toolchain}} ${{ matrix.target }} + - run: cargo build --target ${{ matrix.target }} \ No newline at end of file From 83b0e107dd723f39dbd0a754cc4463dd2173a71d Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Thu, 16 May 2024 13:46:02 +0200 Subject: [PATCH 04/17] Fix typos in CI --- .github/workflows/cargo.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cargo.yml b/.github/workflows/cargo.yml index 98cd65e..9f2a5d7 100644 --- a/.github/workflows/cargo.yml +++ b/.github/workflows/cargo.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - toolchains: + toolchain: - stable steps: @@ -27,15 +27,15 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - toolchains: + toolchain: - stable - nightly - targets: + target: - x86_64-unknown-linux-gnu - x86_64-unknown-linux-musl steps: - uses: actions/checkout@v4 - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} - - run: rustup target add --toolchain ${{ matrix.toolchain}} ${{ matrix.target }} + - run: rustup target add --toolchain ${{ matrix.toolchain }} ${{ matrix.target }} - run: cargo build --target ${{ matrix.target }} \ No newline at end of file From c4d8f2baebb2a1a343c2204d2b4ba6e835a9258f Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Thu, 16 May 2024 13:46:45 +0200 Subject: [PATCH 05/17] Include CD to correct dir --- .github/workflows/cargo.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cargo.yml b/.github/workflows/cargo.yml index 9f2a5d7..56e6625 100644 --- a/.github/workflows/cargo.yml +++ b/.github/workflows/cargo.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} - run: rustup component add --toolchain ${{ matrix.toolchain }} rustfmt - - run: cargo fmt --all --check + - run: cd server && cargo fmt --all --check build: name: Cargo build @@ -38,4 +38,4 @@ jobs: - uses: actions/checkout@v4 - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} - run: rustup target add --toolchain ${{ matrix.toolchain }} ${{ matrix.target }} - - run: cargo build --target ${{ matrix.target }} \ No newline at end of file + - run: cd server && cargo build --target ${{ matrix.target }} \ No newline at end of file From fd3a6b82743238ecc1382394d07baef1dd74128f Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Thu, 16 May 2024 14:14:40 +0200 Subject: [PATCH 06/17] Update cargo.lock Signed-off-by: Tobias de Bruijn --- server/Cargo.lock | 570 +++++++++++++++++++++++----------------------- 1 file changed, 291 insertions(+), 279 deletions(-) diff --git a/server/Cargo.lock b/server/Cargo.lock index 10df5e2..2ce01e9 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -4,11 +4,11 @@ version = 3 [[package]] name = "actix-codec" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "bytes", "futures-core", "futures-sink", @@ -36,17 +36,17 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.5.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "129d4c88e98860e1758c5de288d1632b07970a16d59bdf7b8d66053d582bb71f" +checksum = "d223b13fd481fc0d1f83bb12659ae774d9e3601814c68a0bc539731698cca743" dependencies = [ "actix-codec", "actix-rt", "actix-service", "actix-utils", "ahash", - "base64 0.21.5", - "bitflags 2.4.1", + "base64 0.21.7", + "bitflags 2.5.0", "brotli", "bytes", "bytestring", @@ -171,9 +171,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.4.1" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e43428f3bf11dee6d166b00ec2df4e3aa8cc1606aaa0b7433c146852e2f4e03b" +checksum = "43a6556ddebb638c2358714d853257ed226ece6023ef9364f23f0c70737ea984" dependencies = [ "actix-codec", "actix-http", @@ -238,9 +238,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "getrandom", @@ -251,9 +251,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -275,9 +275,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "anyhow" @@ -306,27 +306,17 @@ dependencies = [ "num-traits", ] -[[package]] -name = "atomic-write-file" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcdbedc2236483ab103a53415653d6b4442ea6141baf1ffa85df29635e88436" -dependencies = [ - "nix", - "rand", -] - [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -351,15 +341,15 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" @@ -381,9 +371,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" dependencies = [ "serde", ] @@ -410,9 +400,9 @@ dependencies = [ [[package]] name = "brotli" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -431,9 +421,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byteorder" @@ -443,9 +433,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "bytestring" @@ -458,12 +448,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -485,9 +476,9 @@ dependencies = [ [[package]] name = "color-eyre" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" dependencies = [ "backtrace", "color-spantrace", @@ -557,18 +548,18 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc" -version = "3.0.1" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" dependencies = [ "crc-catalog", ] @@ -581,31 +572,27 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-queue" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc6598521bb5a83d491e8c1fe51db7296019d2ca3cb93cc6c2a20369a4d78a2" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-bigint" @@ -650,9 +637,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "pem-rfc7468", @@ -661,9 +648,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", ] @@ -725,9 +712,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" dependencies = [ "serde", ] @@ -755,9 +742,9 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -779,9 +766,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -821,9 +808,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "eyre" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -831,9 +818,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "ff" @@ -853,9 +840,9 @@ checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -984,9 +971,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -1014,9 +1001,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.22" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -1033,9 +1020,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -1061,9 +1048,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -1124,9 +1111,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1212,9 +1199,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", @@ -1228,33 +1215,33 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "itertools" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -1316,9 +1303,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.151" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libm" @@ -1339,9 +1326,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "local-channel" @@ -1362,9 +1349,9 @@ checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1372,9 +1359,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "matchers" @@ -1397,9 +1384,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "mime" @@ -1415,18 +1402,18 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -1440,17 +1427,6 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d02c0b00610773bb7fc61d85e13d86c7858cbdf00e1a120bfc41bc055dbaa0e" -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.4.1", - "cfg-if", - "libc", -] - [[package]] name = "noiseless-tracing-actix-web" version = "0.1.0" @@ -1499,21 +1475,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -1522,9 +1503,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -1593,9 +1574,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -1603,22 +1584,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pem" @@ -1626,7 +1607,7 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "serde", ] @@ -1647,18 +1628,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", @@ -1667,9 +1648,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -1700,9 +1681,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "powerfmt" @@ -1791,16 +1772,25 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "regex" -version = "1.10.2" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", - "regex-syntax 0.8.2", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", ] [[package]] @@ -1814,13 +1804,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -1831,17 +1821,17 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", @@ -1863,6 +1853,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-rustls", @@ -1887,16 +1878,17 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if", "getrandom", "libc", "spin 0.9.8", "untrusted", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1922,9 +1914,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc_version" @@ -1937,11 +1929,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", @@ -1950,9 +1942,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", @@ -1966,7 +1958,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", ] [[package]] @@ -1981,9 +1973,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scopeguard" @@ -2017,9 +2009,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" @@ -2108,9 +2100,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -2136,18 +2128,18 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2188,9 +2180,9 @@ dependencies = [ [[package]] name = "sqlx" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba03c279da73694ef99763320dea58b51095dfe87d001b1d4b5fe78ba8763cf" +checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" dependencies = [ "sqlx-core", "sqlx-macros", @@ -2201,9 +2193,9 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd" +checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" dependencies = [ "ahash", "atoi", @@ -2211,7 +2203,6 @@ dependencies = [ "bytes", "crc", "crossbeam-queue", - "dotenvy", "either", "event-listener", "futures-channel", @@ -2244,9 +2235,9 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89961c00dc4d7dffb7aee214964b065072bff69e36ddb9e2c107541f75e4f2a5" +checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" dependencies = [ "proc-macro2", "quote", @@ -2257,11 +2248,10 @@ dependencies = [ [[package]] name = "sqlx-macros-core" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0bd4519486723648186a08785143599760f7cc81c52334a55d6a83ea1e20841" +checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" dependencies = [ - "atomic-write-file", "dotenvy", "either", "heck", @@ -2283,13 +2273,13 @@ dependencies = [ [[package]] name = "sqlx-mysql" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37195395df71fd068f6e2082247891bc11e3289624bbc776a0cdfa1ca7f1ea4" +checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" dependencies = [ "atoi", - "base64 0.21.5", - "bitflags 2.4.1", + "base64 0.21.7", + "bitflags 2.5.0", "byteorder", "bytes", "crc", @@ -2325,13 +2315,13 @@ dependencies = [ [[package]] name = "sqlx-postgres" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ac0ac3b7ccd10cc96c7ab29791a7dd236bd94021f31eec7ba3d46a74aa1c24" +checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" dependencies = [ "atoi", - "base64 0.21.5", - "bitflags 2.4.1", + "base64 0.21.7", + "bitflags 2.5.0", "byteorder", "crc", "dotenvy", @@ -2352,7 +2342,6 @@ dependencies = [ "rand", "serde", "serde_json", - "sha1", "sha2", "smallvec", "sqlx-core", @@ -2364,9 +2353,9 @@ dependencies = [ [[package]] name = "sqlx-sqlite" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490" +checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" dependencies = [ "atoi", "flume", @@ -2437,6 +2426,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-configuration" version = "0.5.1" @@ -2466,15 +2461,14 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.8.1" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2499,9 +2493,9 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -2509,12 +2503,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -2529,10 +2524,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -2553,9 +2549,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -2593,9 +2589,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -2604,16 +2600,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -2636,9 +2631,9 @@ dependencies = [ [[package]] name = "tracing-actix-web" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe0d5feac3f4ca21ba33496bcb1ccab58cca6412b1405ae80f0581541e0ca78" +checksum = "fa069bd1503dd526ee793bb3fce408895136c95fc86d2edb2acf1c646d7f0684" dependencies = [ "actix-web", "mutually_exclusive_features", @@ -2721,9 +2716,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -2733,18 +2728,18 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode_categories" @@ -2777,9 +2772,9 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "uuid" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom", ] @@ -2817,6 +2812,12 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasix" version = "0.12.21" @@ -2828,9 +2829,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2838,9 +2839,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", @@ -2853,9 +2854,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.39" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -2865,9 +2866,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2875,9 +2876,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", @@ -2888,15 +2889,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -2904,15 +2905,19 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "whoami" -version = "1.4.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +dependencies = [ + "redox_syscall 0.4.1", + "wasite", +] [[package]] name = "wilford" @@ -2922,7 +2927,7 @@ dependencies = [ "actix-multiresponse", "actix-route-config", "actix-web", - "base64 0.21.5", + "base64 0.21.7", "color-eyre", "database", "envy", @@ -2978,7 +2983,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -2998,17 +3003,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -3019,9 +3025,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -3031,9 +3037,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -3043,9 +3049,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -3055,9 +3067,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -3067,9 +3079,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -3079,9 +3091,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -3091,9 +3103,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winreg" @@ -3107,18 +3119,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", @@ -3133,27 +3145,27 @@ checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" [[package]] name = "zstd" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.0.0" +version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.10+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" dependencies = [ "cc", "pkg-config", From b6fa108b597b59fbbe1a72a4737b7292401d2898 Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Thu, 16 May 2024 14:16:40 +0200 Subject: [PATCH 07/17] Add apt install for musl-gcc --- .github/workflows/cargo.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cargo.yml b/.github/workflows/cargo.yml index 56e6625..bf3e0c9 100644 --- a/.github/workflows/cargo.yml +++ b/.github/workflows/cargo.yml @@ -36,6 +36,7 @@ jobs: steps: - uses: actions/checkout@v4 + - run: sudo apt install -y musl-gcc - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} - run: rustup target add --toolchain ${{ matrix.toolchain }} ${{ matrix.target }} - run: cd server && cargo build --target ${{ matrix.target }} \ No newline at end of file From a7ffa24a361014418a9cbd3c82738247c5962cec Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Thu, 16 May 2024 14:17:57 +0200 Subject: [PATCH 08/17] Fix pkg name --- .github/workflows/cargo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cargo.yml b/.github/workflows/cargo.yml index bf3e0c9..f8d9f6d 100644 --- a/.github/workflows/cargo.yml +++ b/.github/workflows/cargo.yml @@ -36,7 +36,7 @@ jobs: steps: - uses: actions/checkout@v4 - - run: sudo apt install -y musl-gcc + - run: sudo apt install -y musl-tools - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} - run: rustup target add --toolchain ${{ matrix.toolchain }} ${{ matrix.target }} - run: cd server && cargo build --target ${{ matrix.target }} \ No newline at end of file From ab62be9d1b5cbc0e40a7b1ccd65784c3f55975a3 Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Thu, 16 May 2024 14:36:18 +0200 Subject: [PATCH 09/17] release CI --- .github/workflows/release.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..4673d1f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,18 @@ +name: Release + +on: + tag: + +env: + CARGO_TERM_COLOR: always + +jobs: + fmt: + name: Server build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: make build-server + - run: docker tag registry.mrfriendly.uk/wilford:latest registry.mrfriendly.uk/wilford:${{ github.ref_name }} + - run: docker push registry.mrfriendly.uk/wilford:latest + - run: docker push registry.mrfriendly.uk/wilford:${{ github.ref_name }} \ No newline at end of file From a75dad13646d9056b3e4ac52a8bd461ffe68b328 Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Thu, 16 May 2024 14:38:00 +0200 Subject: [PATCH 10/17] Release CI --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4673d1f..9ce191e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,7 +1,8 @@ name: Release on: - tag: + push: + tags: env: CARGO_TERM_COLOR: always From 925ddf100570f6c66de23a7382fb68e3fe164ebb Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Thu, 16 May 2024 14:41:48 +0200 Subject: [PATCH 11/17] Release CI --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9ce191e..d1f6d88 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,6 @@ jobs: steps: - uses: actions/checkout@v4 - run: make build-server - - run: docker tag registry.mrfriendly.uk/wilford:latest registry.mrfriendly.uk/wilford:${{ github.ref_name }} - - run: docker push registry.mrfriendly.uk/wilford:latest - - run: docker push registry.mrfriendly.uk/wilford:${{ github.ref_name }} \ No newline at end of file + - run: docker tag registry.mrfriendly.uk/wilford-server:latest registry.mrfriendly.uk/wilford-server:${{ github.ref_name }} + - run: docker push registry.mrfriendly.uk/wilford-server:latest + - run: docker push registry.mrfriendly.uk/wilford-server:${{ github.ref_name }} \ No newline at end of file From e2dfde2dddb8f4fe680df34a68ad2c2b5772ac17 Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Fri, 17 May 2024 10:24:04 +0200 Subject: [PATCH 12/17] Release CI --- .github/workflows/release.yml | 36 +++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d1f6d88..6a0d0cd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,14 +6,42 @@ on: env: CARGO_TERM_COLOR: always + DOCK_REG: registry.mrfriendly.uk jobs: - fmt: + server: name: Server build runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: make build-server - - run: docker tag registry.mrfriendly.uk/wilford-server:latest registry.mrfriendly.uk/wilford-server:${{ github.ref_name }} - - run: docker push registry.mrfriendly.uk/wilford-server:latest - - run: docker push registry.mrfriendly.uk/wilford-server:${{ github.ref_name }} \ No newline at end of file + - run: docker tag $DOCK_REG/wilford-server:latest $DOCK_REG/wilford-server:${{ github.ref_name }} + - run: docker push $DOCK_REG/wilford-server:latest + - run: docker push $DOCK_REG/wilford-server:${{ github.ref_name }} + + ui: + name: UI build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '21' + - run: (cd ui; yarn install) + + - run: make build-ui + - run: docker tag $DOCK_REG/wilford-ui:latest $DOCK_REG/wilford-ui:${{ github.ref_name }} + - run: docker push $DOCK_REG/wilford-ui:latest + - run: docker push $DOCK_REG/wilford-ui:${{ github.ref_name }} + + docs: + name: Docs build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: cargo install mdbook --locked + + - run: make build-ui + - run: docker tag $DOCK_REG/wilford-docs:latest $DOCK_REG/wilford-docs:${{ github.ref_name }} + - run: docker push $DOCK_REG/wilford-docs:latest + - run: docker push $DOCK_REG/wilford-docs:${{ github.ref_name }} \ No newline at end of file From e08c46e4dd004ed4c291af3b56caa8532cfa202f Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Fri, 17 May 2024 10:29:24 +0200 Subject: [PATCH 13/17] Release CI --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6a0d0cd..1f30c98 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,6 +3,7 @@ name: Release on: push: tags: + - '*' env: CARGO_TERM_COLOR: always @@ -41,7 +42,7 @@ jobs: - uses: actions/checkout@v4 - run: cargo install mdbook --locked - - run: make build-ui + - run: make build-docs - run: docker tag $DOCK_REG/wilford-docs:latest $DOCK_REG/wilford-docs:${{ github.ref_name }} - run: docker push $DOCK_REG/wilford-docs:latest - run: docker push $DOCK_REG/wilford-docs:${{ github.ref_name }} \ No newline at end of file From 7c14deb2f866a21376beebe1b98f39cc30f3f999 Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Fri, 17 May 2024 11:00:28 +0200 Subject: [PATCH 14/17] Cargo CI Cache --- .github/workflows/cargo.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/cargo.yml b/.github/workflows/cargo.yml index f8d9f6d..401b7c4 100644 --- a/.github/workflows/cargo.yml +++ b/.github/workflows/cargo.yml @@ -36,6 +36,15 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Cache + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + server/target + key: ${{ runner.os }}-${{ matrix.toolchain }}-${{ matrix.target }}-cargo-${{ hashFiles('server/**/Cargo.lock') }} + - run: sudo apt install -y musl-tools - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} - run: rustup target add --toolchain ${{ matrix.toolchain }} ${{ matrix.target }} From dedeb00500564d006944b424b1aaa321028267f6 Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Fri, 17 May 2024 11:35:53 +0200 Subject: [PATCH 15/17] Add email, admin status and name to returned JWT --- Makefile | 12 +- README.md | 10 +- docker-compose.yml | 3 +- docs/src/introduction.md | 3 +- sample_config.json | 3 +- server/database/src/oauth2_client.rs | 131 ++++++++++-------- server/database/src/user.rs | 36 ++--- server/test_oidc_key.pem | 28 ---- server/test_oidc_key.pem.pub | 9 -- server/wilford/src/espo/user.rs | 1 + server/wilford/src/routes/auth.rs | 2 +- server/wilford/src/routes/oauth/introspect.rs | 4 +- server/wilford/src/routes/oauth/token.rs | 9 +- .../src/routes/v1/auth/authorization_info.rs | 4 +- .../wilford/src/routes/v1/auth/authorize.rs | 4 +- server/wilford/src/routes/v1/auth/login.rs | 7 +- server/wilford/src/routes/v1/user/list.rs | 4 +- test_oidc_key.pem | 103 +++++++------- test_oidc_key.pem.pub | 15 +- 19 files changed, 199 insertions(+), 189 deletions(-) delete mode 100644 server/test_oidc_key.pem delete mode 100644 server/test_oidc_key.pem.pub diff --git a/Makefile b/Makefile index 5b7f15d..9163a28 100644 --- a/Makefile +++ b/Makefile @@ -12,13 +12,16 @@ help: echo "- upload-ui : Build and upload the ui Docker image" test_oidc_key.pem: - ssh-keygen -t rsa -b 4096 -m PEM -q -N "" -f ./test_oidc_key.pem + openssl genrsa -out ./test_oidc_key.pem 4096 + +test_oidc_key.pem.pub: test_oidc_key.pem + openssl rsa -in ./test_oidc_key.pem -pubout -outform PEM -out ./test_oidc_key.pem.pub -config.json: +config.json: sample_config.json cp sample_config.json config.json .PHONY: up -up: test_oidc_key.pem config.json +up: test_oidc_key.pem test_oidc_key.pem.pub config.json docker compose up -d echo "Wilford UI available at http://localhost:2522" echo "Wilford Docs available at http://localhost:2523" @@ -50,7 +53,4 @@ build-docs: .PHONY: build-ui build-ui: - # Patch for production - #sed -i "s|createWebHistory('/')|createWebHistory('/wilford')|" ui/src/router/index.ts - docker build -t registry.mrfriendly.uk/wilford-ui ui/ diff --git a/README.md b/README.md index 79ddfc3..ef455a7 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ make up ``` This will: - Create an OIDC signing key if it doesn't exist -- Copy `sample_config.json` to `config.json +- Copy `sample_config.json` to `config.json` - Start all containers The following services will be available: @@ -41,6 +41,14 @@ docker-compose down make up ``` +## Generate OIDC Key +```bash +# Private key +openssl genrsa -out ./oidc.pem 4096 + +# Public key +openssl rsa -in ./oidc.pem -pubout -outform PEM -out ./oidc.pem.pub +``` # License MIT or Apache-2.0, at your option diff --git a/docker-compose.yml b/docker-compose.yml index a01d8d7..91190c0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,7 +31,8 @@ services: - "RUST_LOG=DEBUG" volumes: - "./config.json:/config.json" - - "./test_oidc_key.pem:/oidc.pem" + - "./test_oidc_key.pem:/test_oidc_key.pem" + - "./test_oidc_key.pem.pub:/test_oidc_key.pem.pub" depends_on: - "mariadb-wilford" diff --git a/docs/src/introduction.md b/docs/src/introduction.md index 1a7207c..9479eb2 100644 --- a/docs/src/introduction.md +++ b/docs/src/introduction.md @@ -12,5 +12,4 @@ Wilford's implementation of OAuth2 and OpenID Connect is derived from the follow ## TODO Not everything is implemented 100%. I'd like to add support for: - [A.4](https://openid.net/specs/openid-connect-core-1_0.html#code-id_tokenExample) (`response_type=code id_token`). -At the moment only `response_type=id_token token` is supported. -- User information in the returned JWTs \ No newline at end of file +At the moment only `response_type=id_token token` is supported. \ No newline at end of file diff --git a/sample_config.json b/sample_config.json index e54988f..bac7abf 100644 --- a/sample_config.json +++ b/sample_config.json @@ -19,6 +19,7 @@ "default_client": { "redirect_uri": "http://localhost:2522/login-ok" }, - "oidc_signing_key": "/oidc.pem", + "oidc_signing_key": "/test_oidc_key.pem", + "oidc_public_key": "/test_oidc_key.pem.pub", "oidc_issuer": "http://localhost:2521" } \ No newline at end of file diff --git a/server/database/src/oauth2_client.rs b/server/database/src/oauth2_client.rs index fc9b768..5551178 100644 --- a/server/database/src/oauth2_client.rs +++ b/server/database/src/oauth2_client.rs @@ -7,7 +7,7 @@ use sqlx::{Decode, Encode, FromRow, Result}; use std::collections::HashSet; use thiserror::Error; use time::{Duration, OffsetDateTime}; -use tracing::info; +use crate::user::User; #[derive(Debug, Clone, FromRow)] pub struct OAuth2Client { @@ -20,50 +20,50 @@ pub struct OAuth2Client { #[derive(Debug, Clone)] pub enum OAuth2PendingAuthorization { - EspoUnauthorized(OAuth2PendingAuthorizationUnauthorized), - EspoAuthorized(OAuth2PendingAuthorizationAuthorized), + Unauthorized(OAuth2PendingAuthorizationUnauthorized), + Authorized(OAuth2PendingAuthorizationAuthorized), } impl OAuth2PendingAuthorization { pub fn ty(&self) -> &AuthorizationType { match self { - Self::EspoUnauthorized(v) => &v.ty, - Self::EspoAuthorized(v) => &v.ty, + Self::Unauthorized(v) => &v.ty, + Self::Authorized(v) => &v.ty, } } pub fn id(&self) -> &String { match self { - Self::EspoAuthorized(v) => &v.id, - Self::EspoUnauthorized(v) => &v.id, + Self::Authorized(v) => &v.id, + Self::Unauthorized(v) => &v.id, } } pub fn client_id(&self) -> &String { match self { - Self::EspoAuthorized(v) => &v.client_id, - Self::EspoUnauthorized(v) => &v.client_id, + Self::Authorized(v) => &v.client_id, + Self::Unauthorized(v) => &v.client_id, } } pub fn state(&self) -> &Option { match self { - Self::EspoAuthorized(v) => &v.state, - Self::EspoUnauthorized(v) => &v.state, + Self::Authorized(v) => &v.state, + Self::Unauthorized(v) => &v.state, } } pub fn scopes(&self) -> &Option { match self { - Self::EspoAuthorized(v) => &v.scopes, - Self::EspoUnauthorized(v) => &v.scopes, + Self::Authorized(v) => &v.scopes, + Self::Unauthorized(v) => &v.scopes, } } pub fn nonce(&self) -> &Option { match self { - Self::EspoAuthorized(v) => &v.nonce, - Self::EspoUnauthorized(v) => &v.nonce, + Self::Authorized(v) => &v.nonce, + Self::Unauthorized(v) => &v.nonce, } } } @@ -84,7 +84,7 @@ pub struct OAuth2PendingAuthorizationAuthorized { client_id: String, scopes: Option, state: Option, - espo_user_id: String, + user_id: String, ty: AuthorizationType, nonce: Option, } @@ -95,7 +95,7 @@ struct _OAuth2PendingAuthorization { client_id: String, scopes: Option, state: Option, - espo_user_id: Option, + user_id: Option, ty: AuthorizationType, nonce: Option, } @@ -106,7 +106,7 @@ pub struct OAuth2AuthorizationCode { pub client_id: String, pub expires_at: i64, pub scopes: Option, - pub espo_user_id: String, + pub user_id: String, pub nonce: Option, } @@ -116,7 +116,7 @@ pub struct AccessToken { pub client_id: String, pub expires_at: i64, pub issued_at: i64, - pub espo_user_id: String, + pub user_id: String, pub scopes: Option, } @@ -124,7 +124,7 @@ pub struct AccessToken { pub struct RefreshToken { pub token: String, pub client_id: String, - pub espo_user_id: String, + pub user_id: String, pub scopes: Option, } @@ -132,12 +132,12 @@ pub struct RefreshToken { pub enum OAuth2AuthorizationCodeCreationError { #[error("{0}")] Sqlx(#[from] sqlx::Error), - #[error("Not authorized with EspoCRM yet")] + #[error("Not authorized yet")] Unauthorized, } #[derive(Debug, Error)] -pub enum OAuth2PendingAuthorizationSetEspoIdError { +pub enum OAuth2PendingAuthorizationSetUserIdError { #[error("{0}")] Sqlx(#[from] sqlx::Error), #[error("Cannot overwrite existing authorization")] @@ -255,7 +255,7 @@ impl OAuth2Client { .execute(&**driver) .await?; - Ok(OAuth2PendingAuthorization::EspoUnauthorized( + Ok(OAuth2PendingAuthorization::Unauthorized( OAuth2PendingAuthorizationUnauthorized { id, client_id: self.client_id.clone(), @@ -273,8 +273,8 @@ impl OAuth2Client { pending: OAuth2PendingAuthorization, ) -> std::result::Result { let pending = match pending { - OAuth2PendingAuthorization::EspoAuthorized(v) => v, - OAuth2PendingAuthorization::EspoUnauthorized(_) => { + OAuth2PendingAuthorization::Authorized(v) => v, + OAuth2PendingAuthorization::Unauthorized(_) => { return Err(OAuth2AuthorizationCodeCreationError::Unauthorized) } }; @@ -284,12 +284,12 @@ impl OAuth2Client { let mut tx = driver.begin().await?; - sqlx::query("INSERT INTO oauth2_authorization_codes (client_id, code, expires_at, scopes, espo_user_id, nonce) VALUES (?, ?, ?, ?, ?, ?)") + sqlx::query("INSERT INTO oauth2_authorization_codes (client_id, code, expires_at, scopes, user_id, nonce) VALUES (?, ?, ?, ?, ?, ?)") .bind(&self.client_id) .bind(&code) .bind(expires_at) .bind(&pending.scopes) - .bind(&pending.espo_user_id) + .bind(&pending.user_id) .bind(&pending.nonce) .execute(&mut *tx) .await?; @@ -306,7 +306,7 @@ impl OAuth2Client { code, scopes: pending.scopes.clone(), expires_at, - espo_user_id: pending.espo_user_id, + user_id: pending.user_id, nonce: pending.nonce, }) } @@ -317,8 +317,8 @@ impl OAuth2Client { authorization: OAuth2PendingAuthorization, ) -> std::result::Result { let authorization = match authorization { - OAuth2PendingAuthorization::EspoAuthorized(v) => v, - OAuth2PendingAuthorization::EspoUnauthorized(_) => { + OAuth2PendingAuthorization::Authorized(v) => v, + OAuth2PendingAuthorization::Unauthorized(_) => { return Err(OAuth2AuthorizationCodeCreationError::Unauthorized) } }; @@ -329,12 +329,12 @@ impl OAuth2Client { let mut tx = driver.begin().await?; - sqlx::query("INSERT INTO oauth2_access_tokens (token, client_id, expires_at, issued_at, espo_user_id, scopes) VALUES (?, ?, ?, ?, ?, ?)") + sqlx::query("INSERT INTO oauth2_access_tokens (token, client_id, expires_at, issued_at, user_id, scopes) VALUES (?, ?, ?, ?, ?, ?)") .bind(&atoken) .bind(&self.client_id) .bind(expires_at) .bind(issued_at) - .bind(&authorization.espo_user_id) + .bind(&authorization.user_id) .bind(&authorization.scopes) .execute(&mut *tx) .await?; @@ -350,7 +350,7 @@ impl OAuth2Client { token: atoken, issued_at, expires_at, - espo_user_id: authorization.espo_user_id, + user_id: authorization.user_id, scopes: authorization.scopes, client_id: self.client_id.clone(), }) @@ -369,21 +369,21 @@ impl OAuth2Client { let mut tx = driver.begin().await?; // Access token - sqlx::query("INSERT INTO oauth2_access_tokens (token, client_id, expires_at, issued_at, espo_user_id, scopes) VALUES (?, ?, ?, ?, ?, ?)") + sqlx::query("INSERT INTO oauth2_access_tokens (token, client_id, expires_at, issued_at, user_id, scopes) VALUES (?, ?, ?, ?, ?, ?)") .bind(&atoken) .bind(&self.client_id) .bind(expires_at) .bind(issued_at) - .bind(&authorization.espo_user_id) + .bind(&authorization.user_id) .bind(&authorization.scopes) .execute(&mut *tx) .await?; // Refresh token - sqlx::query("INSERT INTO oauth2_refresh_tokens (token, client_id, espo_user_id, scopes) VALUES (?, ?, ?, ?)") + sqlx::query("INSERT INTO oauth2_refresh_tokens (token, client_id, user_id, scopes) VALUES (?, ?, ?, ?)") .bind(&rtoken) .bind(&self.client_id) - .bind(&authorization.espo_user_id) + .bind(&authorization.user_id) .bind(&authorization.scopes) .execute(&mut *tx) .await?; @@ -402,13 +402,13 @@ impl OAuth2Client { client_id: self.client_id.clone(), expires_at, issued_at, - espo_user_id: authorization.espo_user_id.clone(), + user_id: authorization.user_id.clone(), scopes: authorization.scopes.clone(), }, RefreshToken { token: rtoken, client_id: self.client_id.clone(), - espo_user_id: authorization.espo_user_id.clone(), + user_id: authorization.user_id.clone(), scopes: authorization.scopes.clone(), }, )) @@ -423,11 +423,11 @@ impl OAuth2Client { let expires_at = Self::generate_access_token_expiry(); let issued_at = OffsetDateTime::now_utc().unix_timestamp(); - sqlx::query("INSERT INTO oauth2_access_tokens (token, client_id, expires_at, issued_at, espo_user_id, scopes) VALUES (?, ?, ?, ?, ?, ?)") + sqlx::query("INSERT INTO oauth2_access_tokens (token, client_id, expires_at, issued_at, user_id, scopes) VALUES (?, ?, ?, ?, ?, ?)") .bind(&atoken) .bind(&self.client_id) .bind(expires_at) - .bind(&refresh_token.espo_user_id) + .bind(&refresh_token.user_id) .bind(&refresh_token.scopes) .execute(&**driver) .await?; @@ -438,7 +438,7 @@ impl OAuth2Client { scopes: refresh_token.scopes.clone(), issued_at, expires_at, - espo_user_id: refresh_token.espo_user_id.clone(), + user_id: refresh_token.user_id.clone(), }) } } @@ -506,37 +506,37 @@ impl OAuth2PendingAuthorization { ) } - pub async fn set_espo_user_id( + pub async fn set_user_id( self, driver: &Database, - espo_user_id: &str, - ) -> std::result::Result { + user_id: &str, + ) -> std::result::Result { let id = match &self { - Self::EspoUnauthorized(v) => &v.id, - Self::EspoAuthorized(_) => { - return Err(OAuth2PendingAuthorizationSetEspoIdError::AlreadyAuthorized) + Self::Unauthorized(v) => &v.id, + Self::Authorized(_) => { + return Err(OAuth2PendingAuthorizationSetUserIdError::AlreadyAuthorized) } }; - sqlx::query("UPDATE oauth2_pending_authorizations SET espo_user_id = ? WHERE id = ?") - .bind(espo_user_id) + sqlx::query("UPDATE oauth2_pending_authorizations SET user_id = ? WHERE id = ?") + .bind(user_id) .bind(&id) .execute(&**driver) .await?; let new_self = match self { - Self::EspoUnauthorized(v) => { - Self::EspoAuthorized(OAuth2PendingAuthorizationAuthorized { + Self::Unauthorized(v) => { + Self::Authorized(OAuth2PendingAuthorizationAuthorized { id: v.id, client_id: v.client_id, - espo_user_id: espo_user_id.to_string(), + user_id: user_id.to_string(), state: v.state, scopes: v.scopes, ty: v.ty, nonce: v.nonce, }) } - Self::EspoAuthorized(_) => unreachable!(), + Self::Authorized(_) => unreachable!(), }; Ok(new_self) @@ -556,18 +556,18 @@ impl OAuth2AuthorizationCode { impl From<_OAuth2PendingAuthorization> for OAuth2PendingAuthorization { fn from(value: _OAuth2PendingAuthorization) -> Self { - if let Some(espo_user_id) = value.espo_user_id { - Self::EspoAuthorized(OAuth2PendingAuthorizationAuthorized { + if let Some(user_id) = value.user_id { + Self::Authorized(OAuth2PendingAuthorizationAuthorized { id: value.id, client_id: value.client_id, scopes: value.scopes, state: value.state, - espo_user_id, + user_id, ty: value.ty, nonce: value.nonce, }) } else { - Self::EspoUnauthorized(OAuth2PendingAuthorizationUnauthorized { + Self::Unauthorized(OAuth2PendingAuthorizationUnauthorized { id: value.id, client_id: value.client_id, scopes: value.scopes, @@ -598,6 +598,12 @@ pub struct IdTokenClaims { nonce: Option, /// Authorized party - the party to which the ID Token was issued. If present, it MUST contain the OAuth 2.0 Client ID of this party. azp: String, + + // We also have some custom claims, this is allowed by the JWT spec + + sub_email: String, + sub_name: String, + sub_is_admin: bool, } pub enum JwtSigningAlgorithm { @@ -615,7 +621,7 @@ pub enum IdTokenCreationError { pub fn create_id_token( issuer: String, client: &OAuth2Client, - end_user_id: String, + user: &User, oidc_signing_key_pem: &str, access_token: &AccessToken, nonce: Option, @@ -624,13 +630,18 @@ pub fn create_id_token( let iat = OffsetDateTime::now_utc(); let id_claims = IdTokenClaims { + // Standard claims iss: issuer, - sub: end_user_id, + sub: user.user_id.clone(), aud: client.client_id.clone(), exp: access_token.expires_at, iat: iat.unix_timestamp(), nonce, azp: client.client_id.clone(), + // Custom claims + sub_name: user.name.clone(), + sub_is_admin: user.is_admin, + sub_email: user.email.clone(), }; match jwt_signing_algorithm { diff --git a/server/database/src/user.rs b/server/database/src/user.rs index 3d385c3..c1ec448 100644 --- a/server/database/src/user.rs +++ b/server/database/src/user.rs @@ -3,34 +3,38 @@ use sqlx::{FromRow, Result}; #[derive(Debug, FromRow)] pub struct User { - pub espo_user_id: String, + pub user_id: String, pub name: String, - pub is_espo_admin: bool, + pub email: String, + pub is_admin: bool, } impl User { pub async fn new( driver: &Database, - espo_user_id: String, + user_id: String, name: String, - is_espo_admin: bool, + email: String, + is_admin: bool, ) -> Result { - sqlx::query("INSERT INTO users (espo_user_id, name, is_espo_admin) VALUES (?, ?, ?)") - .bind(&espo_user_id) + sqlx::query("INSERT INTO users (user_id, name, email, is_admin) VALUES (?, ?, ?, ?)") + .bind(&user_id) .bind(&name) - .bind(is_espo_admin) + .bind(&email) + .bind(is_admin) .execute(&**driver) .await?; Ok(Self { name, - espo_user_id, - is_espo_admin, + user_id, + email, + is_admin, }) } pub async fn get_by_id(driver: &Database, id: &str) -> Result> { - Ok(sqlx::query_as("SELECT * FROM users WHERE espo_user_id = ?") + Ok(sqlx::query_as("SELECT * FROM users WHERE user_id = ?") .bind(id) .fetch_optional(&**driver) .await?) @@ -44,16 +48,16 @@ impl User { pub async fn list_permitted_scopes(&self, driver: &Database) -> Result> { Ok( - sqlx::query_scalar("SELECT scope FROM user_permitted_scopes WHERE espo_user_id = ?") - .bind(&self.espo_user_id) + sqlx::query_scalar("SELECT scope FROM user_permitted_scopes WHERE user_id = ?") + .bind(&self.user_id) .fetch_all(&**driver) .await?, ) } pub async fn remove_permitted_scope(&self, driver: &Database, scope: &str) -> Result<()> { - sqlx::query("DELETE FROM user_permitted_scopes WHERE espo_user_id = ? AND scope = ?") - .bind(&self.espo_user_id) + sqlx::query("DELETE FROM user_permitted_scopes WHERE user_id = ? AND scope = ?") + .bind(&self.user_id) .bind(scope) .execute(&**driver) .await?; @@ -62,8 +66,8 @@ impl User { } pub async fn grant_permitted_scope(&self, driver: &Database, scope: &str) -> Result<()> { - sqlx::query("INSERT INTO user_permitted_scopes (espo_user_id, scope) VALUES (?, ?)") - .bind(&self.espo_user_id) + sqlx::query("INSERT INTO user_permitted_scopes (user_id, scope) VALUES (?, ?)") + .bind(&self.user_id) .bind(scope) .execute(&**driver) .await?; diff --git a/server/test_oidc_key.pem b/server/test_oidc_key.pem deleted file mode 100644 index 293fc1d..0000000 --- a/server/test_oidc_key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDFvqxeV+Tk/V/u -bzPuRrhjDpIZKaXz2G7zJUKqbIKXcbEqSX3uHRiTx9AsRZgnjHRM7bA0QmDM8Jd1 -axFpYnN4EKfUWC1PN/g6Su6cukGSPElP3wiD3X4oqSNPwtvziEyvVBAyeW+n0n7N -ruM2sqi0JbzpO3E9OX2LoZJ4xENjIcigcM6j5Cm6yRoZIADWlxSsUbKpA1p5mzEh -iXsxjGCHo7oDURem7Um0DV9eiTCVhdH5CLN9H5Ef25MVIZ7BrU9FP1bTC9+wBeQb -5+hShO/ghFq3tsmM8fP0th5OcI31x5+vifQPjBJEckaERk1LAu/LsZTeIajYh39/ -CJRweLI7AgMBAAECggEAQsLDEv79qknYz3j/wVxi+B59OL2U56SUoMZdoYgM9vBN -bgdt2qw8FfiJ0ciVPBTxVt+Qc7RNViOMHvpM4JV6gnitEMXMVkiBJ70Qnh1L8gEk -hzC3gT2QUE36O/1/oNLNgQwSg9f32GL6qJp434il6FwtUVjePFtihXfBKcCpN5gi -ZWBB3jR3ygwHh6Sqy0gH26GPVkl/ZdjV6711kaAUEXojiJPDTUs9QMtd+5/NVW12 -/BegDRGfX9fHf0G0Onlys1zdkjydBoH8HTdKmSsydIZs4HVnXvgz+nM9V/6pjqk9 -hctSg6jn57BDzoRye3MBP8FAOzDM8I8jlggpPthFcQKBgQDhkyHoBACWpbora1WZ -rkh+3ioM5TujzFILzYtUD+T/1C9lLJQsDzKvEbk95WYWgzZRAj8ZD8v+A/Ktj4Lp -Rx+90GnBVwIAhkZNOyq7s0oLs4vaF1LnxqqE46plcDKnEvKirVbbajK/yGdaIVz4 -yxK3uTW58AyTqE7TYfYiwvsxLQKBgQDgapoXlXjUMVxjMYxzm4MxW4xXqxpfvyuf -IaWacxyEV4/y+HwJXKVPCOD55FkfhLTaYgMa3slgUo21bLULjuWgTZIG4cK/TyeH -JtxSuoPDnuE1CliLkq5rftT5jciEURAKO5nECLJNId0E4f6VmCYgqy4gn4hXs2EM -31BUTRgCBwKBgDqRcSREpcJ3lTbgz4Dvd3M5S8G9YxbMXiEiZSKWXebuuL+UK4Sl -DYvXFjmz7iW6O8gcxTVEnLp/W+skb+ERznQCQPMqHCL2uhojWp0RmBAhthYSKDx1 -oKCQXBWqY/xtNwJ17y9ZC4QwDGWic/lFAL8jH4f/g8Ry1sSbksTDy2ShAoGAFvhS -VV+jQnTa8/w6WAru3rp0HvUQegRjPqJ/BNQLczBTN5dfesXU1cYe4PUSejtnF4M4 -tiNfkVBh2NQLo+c4cbP+4l1xRYugjCDaJE2a9wboHjm03nRhDfepbPGqOIDjl2iK -UJpjRanVqY5INJ+iJ23eBtDNb8yYfmzNGBPcBTkCgYEAykREjRxjaPUDd5zqOkwq -vHNmgn0OD/9Jp9YKHG/D42HaLbbZ7ImQGNoR3P3zKpqvihXMOEU2GZjRJe89AjkM -vrof1tIY0vtFBC5wyt8Ye5xtF6x2SW+64aFRm/WElPtEz6rn7mH1alSzHmOJfQ3q -X09T48Qia7sp1g1DmQ1CkRw= ------END PRIVATE KEY----- diff --git a/server/test_oidc_key.pem.pub b/server/test_oidc_key.pem.pub deleted file mode 100644 index 12c7195..0000000 --- a/server/test_oidc_key.pem.pub +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxb6sXlfk5P1f7m8z7ka4 -Yw6SGSml89hu8yVCqmyCl3GxKkl97h0Yk8fQLEWYJ4x0TO2wNEJgzPCXdWsRaWJz -eBCn1FgtTzf4OkrunLpBkjxJT98Ig91+KKkjT8Lb84hMr1QQMnlvp9J+za7jNrKo -tCW86TtxPTl9i6GSeMRDYyHIoHDOo+QpuskaGSAA1pcUrFGyqQNaeZsxIYl7MYxg -h6O6A1EXpu1JtA1fXokwlYXR+QizfR+RH9uTFSGewa1PRT9W0wvfsAXkG+foUoTv -4IRat7bJjPHz9LYeTnCN9cefr4n0D4wSRHJGhEZNSwLvy7GU3iGo2Id/fwiUcHiy -OwIDAQAB ------END PUBLIC KEY----- diff --git a/server/wilford/src/espo/user.rs b/server/wilford/src/espo/user.rs index 051dd29..6f28979 100644 --- a/server/wilford/src/espo/user.rs +++ b/server/wilford/src/espo/user.rs @@ -9,6 +9,7 @@ use tracing::warn; pub struct EspoUser { pub id: String, pub name: String, + pub email_address: String, pub is_active: bool, #[serde(rename(deserialize = "type"))] pub user_type: String, diff --git a/server/wilford/src/routes/auth.rs b/server/wilford/src/routes/auth.rs index 8a393fd..16091e7 100644 --- a/server/wilford/src/routes/auth.rs +++ b/server/wilford/src/routes/auth.rs @@ -47,7 +47,7 @@ impl FromRequest for Auth { None => return Err(WebError::Unauthorized), }; - let espo_user = EspoUser::get_by_id(&espo_client, &token_info.espo_user_id) + let espo_user = EspoUser::get_by_id(&espo_client, &token_info.user_id) .await .map_err(|e| WebError::Espo(e))?; diff --git a/server/wilford/src/routes/oauth/introspect.rs b/server/wilford/src/routes/oauth/introspect.rs index c360f33..67a82c1 100644 --- a/server/wilford/src/routes/oauth/introspect.rs +++ b/server/wilford/src/routes/oauth/introspect.rs @@ -88,7 +88,7 @@ pub async fn introspect( } } - let user = User::get_by_id(&database, &token.espo_user_id) + let user = User::get_by_id(&database, &token.user_id) .await? .ok_or(IntrospectError::Internal)?; @@ -101,6 +101,6 @@ pub async fn introspect( exp: token.expires_at, iat: token.issued_at, nbf: token.issued_at, - sub: token.espo_user_id, + sub: token.user_id, })) } diff --git a/server/wilford/src/routes/oauth/token.rs b/server/wilford/src/routes/oauth/token.rs index 7c8f543..2b5afa6 100644 --- a/server/wilford/src/routes/oauth/token.rs +++ b/server/wilford/src/routes/oauth/token.rs @@ -10,6 +10,7 @@ use database::oauth2_client::{ use serde::{Deserialize, Serialize}; use tap::TapFallible; use tracing::warn; +use database::user::User; #[derive(Deserialize)] pub struct Form { @@ -92,7 +93,9 @@ pub async fn token( id_token: create_id_token( config.oidc_issuer.clone(), &client, - rtoken.espo_user_id, + &User::get_by_id(&database, &rtoken.user_id).await + .map_err(|_| OAuth2ErrorKind::ServerError)? + .ok_or(OAuth2ErrorKind::ServerError)?, &oidc_signing_key.0, &atoken, authorization_nonce, @@ -132,7 +135,9 @@ pub async fn token( id_token: create_id_token( config.oidc_issuer.clone(), &client, - rtoken.espo_user_id, + &User::get_by_id(&database, &rtoken.user_id).await + .map_err(|_| OAuth2ErrorKind::ServerError)? + .ok_or(OAuth2ErrorKind::ServerError)?, &oidc_signing_key.0, &atoken, None, diff --git a/server/wilford/src/routes/v1/auth/authorization_info.rs b/server/wilford/src/routes/v1/auth/authorization_info.rs index 0eb7699..a77de1e 100644 --- a/server/wilford/src/routes/v1/auth/authorization_info.rs +++ b/server/wilford/src/routes/v1/auth/authorization_info.rs @@ -24,8 +24,8 @@ pub async fn authorization_info( .ok_or(WebError::NotFound)?; match &authorization { - OAuth2PendingAuthorization::EspoAuthorized(_) => {} - OAuth2PendingAuthorization::EspoUnauthorized(_) => return Err(WebError::Unauthorized), + OAuth2PendingAuthorization::Authorized(_) => {} + OAuth2PendingAuthorization::Unauthorized(_) => return Err(WebError::Unauthorized), } let client = OAuth2Client::get_by_client_id(&database, &authorization.client_id()) diff --git a/server/wilford/src/routes/v1/auth/authorize.rs b/server/wilford/src/routes/v1/auth/authorize.rs index 925456c..3fa5fbb 100644 --- a/server/wilford/src/routes/v1/auth/authorize.rs +++ b/server/wilford/src/routes/v1/auth/authorize.rs @@ -9,6 +9,7 @@ use database::oauth2_client::{ create_id_token, AccessToken, AuthorizationType, JwtSigningAlgorithm, OAuth2AuthorizationCodeCreationError, OAuth2Client, OAuth2PendingAuthorization, }; +use database::user::User; use crate::response_types::Redirect; use crate::routes::appdata::{WConfig, WDatabase}; @@ -97,7 +98,8 @@ pub async fn authorize( create_id_token( config.oidc_issuer.clone(), &client, - access_token.espo_user_id.clone(), + &User::get_by_id(&database, &access_token.user_id).await? + .ok_or(WebError::InternalServerError)?, &oidc_signing_key.0, &access_token, nonce, diff --git a/server/wilford/src/routes/v1/auth/login.rs b/server/wilford/src/routes/v1/auth/login.rs index f7d4a24..f25def6 100644 --- a/server/wilford/src/routes/v1/auth/login.rs +++ b/server/wilford/src/routes/v1/auth/login.rs @@ -55,7 +55,7 @@ pub async fn login( // Only exceptions to this are Espo admins, they may have all scopes, // and the OIDC scopes match User::get_by_id(&database, &id).await? { - Some(user) if user.is_espo_admin => {} + Some(user) if user.is_admin => {} Some(user) => { let permitted_scopes = HashSet::from_iter(user.list_permitted_scopes(&database).await?); @@ -83,12 +83,13 @@ pub async fn login( &database, id.clone(), espo_user.name, + espo_user.email_address, espo_user.user_type.eq("admin"), ) .await?; // No permitted scopes are granted yet - if !user.is_espo_admin { + if !user.is_admin { // Remove the OIDC scopes let oidc_scopes = oidc_scopes(); let disallowed_scopes = @@ -102,7 +103,7 @@ pub async fn login( } authorization - .set_espo_user_id(&database, &id) + .set_user_id(&database, &id) .await .map_err(|_| WebError::BadRequest)?; diff --git a/server/wilford/src/routes/v1/user/list.rs b/server/wilford/src/routes/v1/user/list.rs index 0726d2a..8f8b88a 100644 --- a/server/wilford/src/routes/v1/user/list.rs +++ b/server/wilford/src/routes/v1/user/list.rs @@ -27,8 +27,8 @@ pub async fn list(database: WDatabase, auth: Auth) -> WebResult>(); diff --git a/test_oidc_key.pem b/test_oidc_key.pem index a2a153e..17ba90d 100644 --- a/test_oidc_key.pem +++ b/test_oidc_key.pem @@ -1,51 +1,52 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +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 +gZn/+8gFf14XcyWxmqj+55AyOfsRsw== +-----END PRIVATE KEY----- diff --git a/test_oidc_key.pem.pub b/test_oidc_key.pem.pub index 6581061..59c5bb0 100644 --- a/test_oidc_key.pem.pub +++ b/test_oidc_key.pem.pub @@ -1 +1,14 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCla6LsWXmoO6UTF0C1lJ2erWwQoq+5M3jfBvqmUAutZVjvcmHebfdbqCfHwCahF50IN3rkL5zQyd/mXdZs1bRvoRzN03MprDqenxuyHwtiGJYstvdzz8XJp/Taxc9C3WSRbSdHVpTg9AzaQxyakWoOm7b9eqhhJ5Ga3oHvLTCS0dUl9gyWmgncRAXzZZOWFcnp8PcbwUBrejc8837Dpk02HASdt9zB+dwJ59fTOBASduQ/Mr1m63NAqgi4yVRTjmX6QFNjZT32wHrUbQQ9CNIARQMOc4slIqi2WKWmcu6qJNMFs9Ztg2FAoDO0BmkPJXBGvv2MYfMEyaV/D9qf2dUz1wLwlvUKyPOFh6D7cC4i2AQm26KSUM7VcwLpW8frKao9q9Zw5WEUE/9riM+HErxOkbifeOT4m/PLPQS0dnYM+Abda9bJ037pLpaWXxWaHuteOdCM9XVKUzNWW3EpGl0FDQBfVtiutDefUuD5a7bNUHbm/LVv0vzayENEdWXYLyQfJ4468Jb4nRONv5wYH3Td0HKThV70f0O764OSFKIhhwX2zQIceOj9fu7nCkfnCUzw0J2KEFEDTGdTejpKcTEcixw5uaFLdqQW9QJcJ4MQcSJb6KwGK1eNwxcbuIpvX8HoQJoVBp728svD7fSvqkMi9FlLZlk2wELNepWORijiVQ== tobias@tobias-desktop +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmHD+RiKhimWW9Tpjr6/l +UtbNmdA0pQF/oif4IZGEKzBXTdQ2YSLq8/bqi0TO0/zB/GdQhCMn9JuH4HpPTa+u +aJ1GOEvo8EIWB0HpKJCrguQP3x53eDRU/mLPzTqQ2js4YHozCFJipdZ7reb83M8v +lMvBYcNW6IZO8DpvkNphM43qlqyyiyFZVJab+0jGFpi7+IVwybjl31LQdB13ycPz +WrGbKDTW9OrOl8P/AIzNM1H05lzZ1GKdmIOYB2bDmdVbrRg31VVU8Sj7NWItkN/e +IeC2CLUf3RnpeQ21Zlkg94VZAhbEEEm6nWj0JUjOB/5pHybOZYIM48TGYCGwoFyg +Zh6T3wG9Cmb9EwZFQTWC0FxkWzqFlJU6QrSU2WeOngNtNExojY+6Lug3yqCP8uYP +lk6fGQ/12KOUerwyxHuoNlTWerPW3IWwamjqO9i8XWsSJ8a4yPRdI/PYMZz3bnUG +0zRgpkJNAf0B1sCQl6LBySadpGJT/g0hrK0f/ZYX33RblY87Wf+j/kND/oj5wWl0 +6k5NeaqfPgLB4UM5wgF9FmEkBsTCjGpK8RbvYHP0jzpKwe1mbiES/CGMFS7PDsqX +5XwRulEXvdVBXlllPcAD6gm498hi/0rfEu9SNpCoQyU+elwgg40Hh8cG2tNgMAdX +5YpgL4GZjDoQ+/EoL/+NM1UCAwEAAQ== +-----END PUBLIC KEY----- From 7a607bac5e78f85b1cfa2e24fac8388492ce245a Mon Sep 17 00:00:00 2001 From: Tobias de Bruijn Date: Sun, 30 Jun 2024 17:33:27 +0200 Subject: [PATCH 16/17] Got it work with oauth2proxy --- docker-compose.yml | 8 ++ docs/src/SUMMARY.md | 3 + docs/src/deploy/configuration.md | 19 ++++ docs/src/deploy/index.md | 1 + docs/src/deploy/oauth2_proxy.md | 96 +++++++++++++++++++ localhost-key.pem | 28 ++++++ localhost.pem | 26 +++++ nginx.conf | 39 ++++++++ oauth2_proxy.docker-compose.yml | 21 ++-- server/Cargo.lock | 2 + server/database/migrations/1_initial.sql | 19 ++-- server/database/src/constant_access_tokens.rs | 5 + server/database/src/oauth2_client.rs | 46 ++++++--- server/database/src/user.rs | 7 ++ server/wilford/Cargo.toml | 4 +- server/wilford/src/config.rs | 2 + server/wilford/src/espo/user.rs | 20 +++- server/wilford/src/main.rs | 17 +++- server/wilford/src/routes/auth.rs | 12 +-- server/wilford/src/routes/error.rs | 56 +++++++++-- server/wilford/src/routes/oauth/token.rs | 8 +- .../src/routes/v1/auth/authorization_info.rs | 16 ++-- .../wilford/src/routes/v1/auth/authorize.rs | 23 +++-- server/wilford/src/routes/v1/auth/login.rs | 18 ++-- server/wilford/src/routes/v1/cat/add.rs | 16 ++-- server/wilford/src/routes/v1/cat/list.rs | 12 ++- server/wilford/src/routes/v1/cat/remove.rs | 14 +-- server/wilford/src/routes/v1/clients/add.rs | 16 ++-- .../wilford/src/routes/v1/clients/internal.rs | 10 +- server/wilford/src/routes/v1/clients/list.rs | 12 ++- .../wilford/src/routes/v1/clients/remove.rs | 14 +-- server/wilford/src/routes/v1/user/list.rs | 9 +- .../routes/v1/user/permitted_scopes/add.rs | 16 ++-- .../routes/v1/user/permitted_scopes/list.rs | 14 +-- .../routes/v1/user/permitted_scopes/remove.rs | 16 ++-- server/wilford/src/routes/well_known/jwks.rs | 46 +++++++-- .../routes/well_known/openid_configuration.rs | 2 + 37 files changed, 541 insertions(+), 152 deletions(-) create mode 100644 docs/src/deploy/configuration.md create mode 100644 docs/src/deploy/index.md create mode 100644 docs/src/deploy/oauth2_proxy.md create mode 100644 localhost-key.pem create mode 100644 localhost.pem create mode 100644 nginx.conf diff --git a/docker-compose.yml b/docker-compose.yml index 91190c0..a46dd20 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -66,3 +66,11 @@ services: - "./tmp/espocrm:/var/www/html" depends_on: - mariadb-espocrm + + nginx: + image: nginx + network_mode: "host" + volumes: + - "./localhost.pem:/etc/ssl/certs/ssl-cert-snakeoil.pem" + - "./localhost-key.pem:/etc/ssl/private/ssl-cert-snakeoil.key" + - "./nginx.conf:/etc/nginx/conf.d/default.conf:ro" \ No newline at end of file diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 28f0e18..b79c4d5 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -1,5 +1,8 @@ # Summary - [Introduction](introduction.md) +- [Deploying](./deploy/index.md) + - [Configuration](./deploy/configuration.md) + - [OAuth2 Proxy](./deploy/oauth2_proxy.md) - [OAuth2](oauth2/index.md) - [Authorization](oauth2/authorization.md) - [API](api/index.md) diff --git a/docs/src/deploy/configuration.md b/docs/src/deploy/configuration.md new file mode 100644 index 0000000..8980f78 --- /dev/null +++ b/docs/src/deploy/configuration.md @@ -0,0 +1,19 @@ +# Configuration + + +## Default config file +```json +{{#include ../../../sample_config.json}} +``` + +## Environmental variables +``` +CONFIG_PATH= +``` + +## Available options +The following Rust structs define the layout of the configuration. +An example of how this translates to JSON can be found in the [sample config](#default-config-file) +```rust,noplayground +{{#include ../../../server/wilford/src/config.rs:config}} +``` \ No newline at end of file diff --git a/docs/src/deploy/index.md b/docs/src/deploy/index.md new file mode 100644 index 0000000..5ccaf93 --- /dev/null +++ b/docs/src/deploy/index.md @@ -0,0 +1 @@ +# Deployment Documentation \ No newline at end of file diff --git a/docs/src/deploy/oauth2_proxy.md b/docs/src/deploy/oauth2_proxy.md new file mode 100644 index 0000000..b26b1c7 --- /dev/null +++ b/docs/src/deploy/oauth2_proxy.md @@ -0,0 +1,96 @@ +# OAuth2 Proxy + +Wilford supports running with [oauth2-proxy](https://oauth2-proxy.github.io/oauth2-proxy/). +Using oaut2-proxy together with Wilford and nginx, you can protect static resources without needing to modify them. + +## OAuth2 Config +Sample docker-compose file for running oauth2-proxy. +Replace the `CLIENT_ID` and `CLIENT_SECRET` with the ID and Secret generated by Wilford. Use `REDIRECT_URL` as the Redirect URL in Wilford. +The `COOKIE_SECRET` should be a securely generated random string. +```yml +version: '3.2' +services: + oauth2_proxy: + image: quay.io/oauth2-proxy/oauth2-proxy + environment: + - "OAUTH2_PROXY_COOKIE_SECRET=VsZqXqHQzwdPUcEUDgNxmQvTRZ46DtlQr8q-HtomkL8=" + - "OAUTH2_PROXY_COOKIE_SECURE=true" + - "OAUTH2_PROXY_COOKIE_DOMAIN=localhost" + - "OAUTH2_PROXY_CLIENT_ID=NuWrxroZbOuhBL2ufHx9zj0qKT6XXQRg" + - "OAUTH2_PROXY_CLIENT_SECRET=vwn0MqNbD9qAnvCbGns9sNtikWC7eTM2V7DIz85vcimtxm12" + - "OAUTH2_PROXY_OIDC_ISSUER_URL=https://localhost:8443" + - "OAUTH2_PROXY_REDIRECT_URL=https://localhost:8443/oauth2/callback" + - "OAUTH2_PROXY_PROVIDER=oidc" + - "OAUTH2_PROXY_EMAIL_DOMAINS=*" + - "OAUTH2_PROXY_OIDC_EMAIL_CLAIM=sub_email" + - "OAUTH2_PROXY_PROVIDER_DISPLAY_NAME=Koala" + - "OAUTH2_PROXY_CUSTOM_SIGN_IN_LOGO=-" + - "OAUTH2_PROXY_BANNER=" + - "OAUTH2_PROXY_FOOTER=-" + network_mode: "host" +``` + +## Nginx auth_directive +Using this setup, you can use the nginx auth directive very easily: +```conf + location /secure { + auth_request /oauth2/auth; + error_page 401 =403 /oauth2/sign_in; + proxy_pass http://my-secure-backend; + } +``` + +## Localhost +The JWKS specification requires it be served over https. When working locally, this can be a bit of a pain. + +### Generate certificates for localhost +Install the required tools: +```bash +sudo apt install -y libnss3-tools mkcert +``` +Generate a CA cert: +```bash +mkcert --install +``` + +Generate SSL certificate for `localhost`, from the repository root: +```bash +mkcert localhost +``` + +### Oauth2-proxy +Add the following to the docker-compose file: +```yml +volumes: + - "/usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro" + - "/etc/ssl/certs:/etc/ssl/certs:ro" +``` + +### nginx +Use the following block for Wilford: +```conf +server { + listen 8443 ssl default_server; + server_name _; + + ssl_certificate /etc/ssl/certs/ssl-cert-localhost.pem; + ssl_certificate_key /etc/ssl/private/ssl-cert-localhost.key; + + location / { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://localhost:2521; + } +} +``` +docker-compose: +```yml + nginx: + image: nginx + network_mode: "host" + volumes: + - "./localhost.pem:/etc/ssl/certs/ssl-cert-localhost.pem" + - "./localhost-key.pem:/etc/ssl/private/ssl-cert-localhost.key" + - "./nginx.conf:/etc/nginx/conf.d/default.conf:ro" +``` \ No newline at end of file diff --git a/localhost-key.pem b/localhost-key.pem new file mode 100644 index 0000000..e616212 --- /dev/null +++ b/localhost-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDsNbwWcAH2Dss+ +GPSLZkO1gYb8SOFTbjSw/pXCVudZNiLbCPNWrALSLpgwLiXJQyQLwG40lL4jOOl1 ++p4nKGYl/6zRpPLolvngQH+GVbUJbODiYLY/MqjVdMT3PX97qIKGpqb2zzcPHm2E +ntlTfh7RIHzlvej9zPpccG62a4w9C67oj7Bh+E274HQY96pPSw3eFcictXekPoxS +lIdVnOh+3uP4TI/MnKDrcn346FJqnQZD9AnwQ5B7x4oB+d9BrmMyzWyQqF1jNxbV +CTzoTeKoyqsdJzCuecLeCHN+soSuK2aM9d2Wx5Pb16VUYD8XKMjEYg72tYNrGx7a +whMQ189rAgMBAAECggEAWUmSmJSsWRuMfiOmxM7aR1D3+oN+ETB2YHVLnNOGzfUl +xdAjU57fzh1oz8WR6PslNAAAaIXVPbE0prEeeUTPIAv+gpysaXkwaTFYQypArZhn +hYrzOP5oTY+/KIopl0/CTy3NrTv03xUsZtY45lOlSH3UWG+qE84Y0Tp6zx/mOehn +qVyWC+klhbcYbJDh2bRcddJbYv7TbtHMLKpvIiM2As5mYeDJChDX1oBPRirabALs +gxfvY+NWw/DmL+Nn7QRL6Il6Mr5U5GGLYiRyRlnQHRGPaZ2OjNiRmmxsVYkKcFQ2 +7XWkNl4kKKgA+plqryGKl47blRG0p2fNhADjTax9iQKBgQDt0L9QFEE5YlFWS9/o +wOg2DKKTkDUUnBBKlw0nbx58ygLb0VZaPCoWp/Mukxd7Lry6PBdSMXsIPIZnY+BP +52A3o1a0sy/ssmNL97CJmTyQbDNKoXWzY8W/Ra0m0R6nbnV/CoFhbHmQoZnB4TeP +41t/EIC+1u8CBUIoZOpo4Y+EhwKBgQD+RY8tAvqYpR0XaxgKhxPxSgbtXAH/6dvd +fVVhsUqtfdE4gFZbJmHXdC2vf1bKyHQ7D/g4rqpUe2aauDUSD57WObBfl1w7i/uR +3dAyUFEwme4KsJz5nS0fNQxZ/6+0MFEVtywe3w96Jr15RhltwKZSZxhZehxcnlvH +jpvESDr6/QKBgEFU23nQVqrBC789UOHMPP68Md1//FURGpijLoXqzOFTTb29oI9h +f96BfRkKZ6T7jfVLlMyLs1Tr67Bzi6fn1FL0mFlD8KKBzy2LegATDMRQNTcHbCJA +Ao8tQQgs4tL0UWr5I9nzxuGow2izymPI/dXGXtgOi9JuR2J5drwhWx/5AoGABdPW +SjPNRn5SQl0j+enKnTcTHZGEQjc74MGkmU6U5ZECoIbgc8pXZ7az7Ve/x3n8n/Xn +vHTUVodVfKpIHRfajhJYZnhzlrHInDk3MlAA7Fo6yGfv0RC3HgX7OHzRrBGHajX+ +ft6h3izRHtxqbMeDiFPwjOxthfnjJJmyHDeDkokCgYEA68cGwARZoX1lyzcXGbfb +TwOxJc79TU+zhdQVH/aTI6hzMeTdoc2qT+yPU8qOgJkVoCBM/TaRrIhrpGy+Tf1o +P4Gw1qcJWNMRdg1ssdyKCueCIx+M7DdNPA578UhD5RgdiCUmu0ufLWdC5ZPVxv8c +fFo8K4Bc9wBn4W3UBsnBROc= +-----END PRIVATE KEY----- diff --git a/localhost.pem b/localhost.pem new file mode 100644 index 0000000..927aec2 --- /dev/null +++ b/localhost.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEUjCCArqgAwIBAgIQAKAUPbumOzgi7b+DtrstqDANBgkqhkiG9w0BAQsFADCB +jTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTEwLwYDVQQLDCh0b2Jp +YXNAdG9iaWFzLWRlc2t0b3AgKFRvYmlhcyBkZSBCcnVpam4pMTgwNgYDVQQDDC9t +a2NlcnQgdG9iaWFzQHRvYmlhcy1kZXNrdG9wIChUb2JpYXMgZGUgQnJ1aWpuKTAe +Fw0yNDA2MzAxMzQ3MzBaFw0yNjA5MzAxMzQ3MzBaMFwxJzAlBgNVBAoTHm1rY2Vy +dCBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTExMC8GA1UECwwodG9iaWFzQHRvYmlh +cy1kZXNrdG9wIChUb2JpYXMgZGUgQnJ1aWpuKTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAOw1vBZwAfYOyz4Y9ItmQ7WBhvxI4VNuNLD+lcJW51k2ItsI +81asAtIumDAuJclDJAvAbjSUviM46XX6nicoZiX/rNGk8uiW+eBAf4ZVtQls4OJg +tj8yqNV0xPc9f3uogoampvbPNw8ebYSe2VN+HtEgfOW96P3M+lxwbrZrjD0LruiP +sGH4TbvgdBj3qk9LDd4VyJy1d6Q+jFKUh1Wc6H7e4/hMj8ycoOtyffjoUmqdBkP0 +CfBDkHvHigH530GuYzLNbJCoXWM3FtUJPOhN4qjKqx0nMK55wt4Ic36yhK4rZoz1 +3ZbHk9vXpVRgPxcoyMRiDva1g2sbHtrCExDXz2sCAwEAAaNeMFwwDgYDVR0PAQH/ +BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB8GA1UdIwQYMBaAFNFs6uYdWwV2 +bmxT5oolJDjqSqCcMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsF +AAOCAYEAKMi6fznFFhcxHKl94lIW8WBuw8SwbtRCRmugl1UFjT9zHYijLdwSTcPs +1FTIBSskK8xZFWI4b+UQJDe6jZEhuYUHRownx9OYznPy2daBK2Mnh3u7Ni07d7R5 +EomqHurTxVTxC0+hnV487zLpXDZr3Xz0Q9YORuHOYj2ayMUOrahp6aR2ppBWFTAB +bOgioBj9qcavx0YWC5P3WzljG/+G1x2KQQ5Q1zrxy2E4bxwAWpxXjIMvTWjDq/NQ +wD6NCAPbRVvBEM1rgBJ3chKAQqp5VQ3oebxPXyQDoUG/WCTVgBfmO9t6mUT7eONT +pbKRxSP6CGnIdhPFCscODs9CffcfNBznUFj886q2+3vwOnFa1uZCLdQ2i35wurxl +PffKLJS+oJyUpP9qXPY7BNzmodw6hQwLhk4Mv3IGUWJcgfuHkIlsLYkUv41G4BMJ +wHGKflYVCTU+GVeT/M4zP/W5yLM0k1h56s+qgd5d8vlXIvIBnEmV9Y9jcgCT8+Wz +kYkapOB2 +-----END CERTIFICATE----- diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..063689d --- /dev/null +++ b/nginx.conf @@ -0,0 +1,39 @@ +server { + listen 8443 ssl default_server; + server_name _; + + ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem; + ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key; + + location /oauth2/ { + proxy_pass http://127.0.0.1:4180; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Auth-Request-Redirect $request_uri; + # or, if you are handling multiple domains: + # proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri; + } + location = /oauth2/auth { + proxy_pass http://127.0.0.1:4180; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Uri $request_uri; + # nginx auth_request includes headers but not body + proxy_set_header Content-Length ""; + proxy_pass_request_body off; + } + + location /docs { + auth_request /oauth2/auth; + error_page 401 =403 /oauth2/sign_in; + proxy_pass https://google.com; + } + + + location / { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://localhost:2521; + } +} \ No newline at end of file diff --git a/oauth2_proxy.docker-compose.yml b/oauth2_proxy.docker-compose.yml index cde990e..148b0ed 100644 --- a/oauth2_proxy.docker-compose.yml +++ b/oauth2_proxy.docker-compose.yml @@ -2,13 +2,22 @@ version: '3.2' services: oauth2_proxy: image: quay.io/oauth2-proxy/oauth2-proxy + volumes: + - "/usr/local/share/ca-certificates:/usr/local/share/ca-certificates:ro" + - "/etc/ssl/certs:/etc/ssl/certs:ro" environment: - "OAUTH2_PROXY_COOKIE_SECRET=VsZqXqHQzwdPUcEUDgNxmQvTRZ46DtlQr8q-HtomkL8=" - - "OAUTH2_PROXY_CLIENT_ID=Vy1l9P2X7RFf4jHfo3OiLHtShllSZKDa" - - "OAUTH2_PROXY_CLIENT_SECRET=d4HtCZLeZvRvldGOxQd18l5a46hZEl6WWoocL2IbkfcozLH6" - - "OAUTH2_PROXY_OIDC_ISSUER_URL=http://localhost:2521" - - "OAUTH2_PROXY_REDIRECT_URL=http://127.0.0.1:4180/oauth2/callback" + - "OAUTH2_PROXY_COOKIE_SECURE=true" + - "OAUTH2_PROXY_COOKIE_DOMAIN=localhost" + - "OAUTH2_PROXY_CLIENT_ID=NuWrxroZbOuhBL2ufHx9zj0qKT6XXQRg" + - "OAUTH2_PROXY_CLIENT_SECRET=vwn0MqNbD9qAnvCbGns9sNtikWC7eTM2V7DIz85vcimtxm12" + - "OAUTH2_PROXY_OIDC_ISSUER_URL=https://localhost:8443" + - "OAUTH2_PROXY_REDIRECT_URL=https://localhost:8443/oauth2/callback" - "OAUTH2_PROXY_PROVIDER=oidc" - "OAUTH2_PROXY_EMAIL_DOMAINS=*" - - "OAUTH2_PROXY_OIDC_JWKS_URL=http://localhost:2521/.well-known/jwks.json" - network_mode: "host" \ No newline at end of file + - "OAUTH2_PROXY_OIDC_EMAIL_CLAIM=sub_email" + - "OAUTH2_PROXY_PROVIDER_DISPLAY_NAME=Koala" + - "OAUTH2_PROXY_CUSTOM_SIGN_IN_LOGO=-" + - "OAUTH2_PROXY_BANNER=" + - "OAUTH2_PROXY_FOOTER=-" + network_mode: "host" diff --git a/server/Cargo.lock b/server/Cargo.lock index 2ce01e9..e743239 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -2935,6 +2935,7 @@ dependencies = [ "noiseless-tracing-actix-web", "pem", "reqwest", + "rsa", "serde", "serde_json", "serde_qs", @@ -2943,6 +2944,7 @@ dependencies = [ "tokio", "tracing", "tracing-actix-web", + "tracing-error", "tracing-subscriber", ] diff --git a/server/database/migrations/1_initial.sql b/server/database/migrations/1_initial.sql index a889ce6..d154051 100644 --- a/server/database/migrations/1_initial.sql +++ b/server/database/migrations/1_initial.sql @@ -12,7 +12,7 @@ CREATE TABLE oauth2_pending_authorizations ( client_id VARCHAR(32) NOT NULL, scopes TEXT DEFAULT NULL, state TEXT DEFAULT NULL, - espo_user_id TEXT DEFAULT NULL, + user_id TEXT DEFAULT NULL, ty TEXT NOT NULL, nonce TEXT DEFAULT NULL, PRIMARY KEY (id) @@ -23,7 +23,7 @@ CREATE TABLE oauth2_access_tokens ( client_id VARCHAR(32) NOT NULL, expires_at BIGINT NOT NULL, issued_at BIGINT NOT NULL, - espo_user_id VARCHAR(64) NOT NULL, + user_id VARCHAR(64) NOT NULL, scopes TEXT DEFAULT NULL, PRIMARY KEY (token) ); @@ -31,7 +31,7 @@ CREATE TABLE oauth2_access_tokens ( CREATE TABLE oauth2_refresh_tokens ( token VARCHAR(32) NOT NULL, client_id VARCHAR(32) NOT NULL, - espo_user_id VARCHAR(64) NOT NULL, + user_id VARCHAR(64) NOT NULL, scopes TEXT DEFAULT NULL, PRIMARY KEY (token) ); @@ -41,22 +41,23 @@ CREATE TABLE oauth2_authorization_codes ( code VARCHAR(32) NOT NULL, expires_at BIGINT NOT NULL, scopes TEXT DEFAULT NULL, - espo_user_id TEXT NOT NULL, + user_id TEXT NOT NULL, nonce TEXT DEFAULT NULL, PRIMARY KEY (code) ); CREATE TABLE users ( - espo_user_id VARCHAR(64) NOT NULL, + user_id VARCHAR(64) NOT NULL, name TEXT NOT NULL, - is_espo_admin BOOL, - PRIMARY KEY (espo_user_id) + email TEXT NOT NULL, + is_admin BOOL, + PRIMARY KEY (user_id) ); CREATE TABLE user_permitted_scopes ( - espo_user_id VARCHAR(64) NOT NULL, + user_id VARCHAR(64) NOT NULL, scope VARCHAR(64) NOT NULL, - PRIMARY KEY (espo_user_id, scope) + PRIMARY KEY (user_id, scope) ); CREATE TABLE constant_access_tokens ( diff --git a/server/database/src/constant_access_tokens.rs b/server/database/src/constant_access_tokens.rs index f7d3420..251d469 100644 --- a/server/database/src/constant_access_tokens.rs +++ b/server/database/src/constant_access_tokens.rs @@ -2,6 +2,7 @@ use crate::driver::Database; use crate::generate_string; use sqlx::FromRow; use sqlx::Result; +use tracing::instrument; #[derive(Debug, Clone, FromRow)] pub struct ConstantAccessToken { @@ -14,6 +15,7 @@ impl ConstantAccessToken { generate_string(32) } + #[instrument] pub async fn new(driver: &Database, name: String) -> Result { let token = Self::generate_token(); @@ -26,12 +28,14 @@ impl ConstantAccessToken { Ok(Self { name, token }) } + #[instrument] pub async fn list(driver: &Database) -> Result> { Ok(sqlx::query_as("SELECT * FROM constant_access_tokens") .fetch_all(&**driver) .await?) } + #[instrument] pub async fn get_by_token(driver: &Database, token: &str) -> Result> { Ok( sqlx::query_as("SELECT * FROM constant_access_tokens WHERE token = ?") @@ -41,6 +45,7 @@ impl ConstantAccessToken { ) } + #[instrument] pub async fn revoke(self, driver: &Database) -> Result<()> { sqlx::query("DELETE FROM constant_access_tokens WHERE token = ?") .bind(self.token) diff --git a/server/database/src/oauth2_client.rs b/server/database/src/oauth2_client.rs index 5551178..14a77bb 100644 --- a/server/database/src/oauth2_client.rs +++ b/server/database/src/oauth2_client.rs @@ -1,4 +1,5 @@ use crate::driver::Database; +use crate::user::User; use crate::{generate_string, impl_enum_type}; use jwt_simple::algorithms::{RS256KeyPair, RSAKeyPairLike}; use jwt_simple::claims::Claims; @@ -7,7 +8,7 @@ use sqlx::{Decode, Encode, FromRow, Result}; use std::collections::HashSet; use thiserror::Error; use time::{Duration, OffsetDateTime}; -use crate::user::User; +use tracing::instrument; #[derive(Debug, Clone, FromRow)] pub struct OAuth2Client { @@ -100,7 +101,7 @@ struct _OAuth2PendingAuthorization { nonce: Option, } -#[derive(FromRow)] +#[derive(Debug, FromRow)] pub struct OAuth2AuthorizationCode { pub code: String, pub client_id: String, @@ -120,7 +121,7 @@ pub struct AccessToken { pub scopes: Option, } -#[derive(FromRow)] +#[derive(Debug, FromRow)] pub struct RefreshToken { pub token: String, pub client_id: String, @@ -186,6 +187,7 @@ impl OAuth2Client { (OffsetDateTime::now_utc() + Duration::hours(1)).unix_timestamp() } + #[instrument] pub async fn new( driver: &Database, name: String, @@ -213,12 +215,14 @@ impl OAuth2Client { }) } + #[instrument] pub async fn list(driver: &Database) -> Result> { Ok(sqlx::query_as("SELECT * FROM oauth2_clients") .fetch_all(&**driver) .await?) } + #[instrument] pub async fn delete(self, driver: &Database) -> Result<()> { sqlx::query("DELETE FROM oauth2_clients WHERE client_id = ?") .bind(self.client_id) @@ -227,6 +231,7 @@ impl OAuth2Client { Ok(()) } + #[instrument] pub async fn get_by_client_id(driver: &Database, client_id: &str) -> Result> { Ok( sqlx::query_as("SELECT * FROM oauth2_clients WHERE client_id = ?") @@ -236,6 +241,7 @@ impl OAuth2Client { ) } + #[instrument] pub async fn new_pending_authorization( &self, driver: &Database, @@ -267,6 +273,7 @@ impl OAuth2Client { )) } + #[instrument] pub async fn new_authorization_code( &self, driver: &Database, @@ -311,6 +318,7 @@ impl OAuth2Client { }) } + #[instrument] pub async fn new_access_token( &self, driver: &Database, @@ -356,6 +364,7 @@ impl OAuth2Client { }) } + #[instrument] pub async fn new_token_pair( &self, driver: &Database, @@ -414,6 +423,7 @@ impl OAuth2Client { )) } + #[instrument] pub async fn refresh_access_token( &self, driver: &Database, @@ -444,6 +454,7 @@ impl OAuth2Client { } impl AccessToken { + #[instrument] pub async fn get_by_token(driver: &Database, token: &str) -> Result> { Ok( sqlx::query_as("SELECT * FROM oauth2_access_tokens WHERE token = ?") @@ -453,6 +464,7 @@ impl AccessToken { ) } + #[instrument] pub async fn get_with_validation( driver: &Database, token: &str, @@ -473,6 +485,7 @@ impl AccessToken { ) } + #[instrument] pub fn scopes(&self) -> HashSet { self.scopes .as_ref() @@ -482,6 +495,7 @@ impl AccessToken { } impl RefreshToken { + #[instrument] pub async fn get_by_token(driver: &Database, token: &str) -> Result> { Ok( sqlx::query_as("SELECT * FROM oauth2_refresh_tokens WHERE token = ?") @@ -493,6 +507,7 @@ impl RefreshToken { } impl OAuth2PendingAuthorization { + #[instrument] pub async fn get_by_id( driver: &Database, id: &str, @@ -506,6 +521,7 @@ impl OAuth2PendingAuthorization { ) } + #[instrument] pub async fn set_user_id( self, driver: &Database, @@ -525,17 +541,15 @@ impl OAuth2PendingAuthorization { .await?; let new_self = match self { - Self::Unauthorized(v) => { - Self::Authorized(OAuth2PendingAuthorizationAuthorized { - id: v.id, - client_id: v.client_id, - user_id: user_id.to_string(), - state: v.state, - scopes: v.scopes, - ty: v.ty, - nonce: v.nonce, - }) - } + Self::Unauthorized(v) => Self::Authorized(OAuth2PendingAuthorizationAuthorized { + id: v.id, + client_id: v.client_id, + user_id: user_id.to_string(), + state: v.state, + scopes: v.scopes, + ty: v.ty, + nonce: v.nonce, + }), Self::Authorized(_) => unreachable!(), }; @@ -544,6 +558,7 @@ impl OAuth2PendingAuthorization { } impl OAuth2AuthorizationCode { + #[instrument] pub async fn get_by_code(driver: &Database, code: &str) -> Result> { Ok( sqlx::query_as("SELECT * FROM oauth2_authorization_codes WHERE code = ?") @@ -600,12 +615,12 @@ pub struct IdTokenClaims { azp: String, // We also have some custom claims, this is allowed by the JWT spec - sub_email: String, sub_name: String, sub_is_admin: bool, } +#[derive(Debug)] pub enum JwtSigningAlgorithm { RS256, } @@ -618,6 +633,7 @@ pub enum IdTokenCreationError { Signing(String), } +#[instrument] pub fn create_id_token( issuer: String, client: &OAuth2Client, diff --git a/server/database/src/user.rs b/server/database/src/user.rs index c1ec448..f5dc0b7 100644 --- a/server/database/src/user.rs +++ b/server/database/src/user.rs @@ -1,5 +1,6 @@ use crate::driver::Database; use sqlx::{FromRow, Result}; +use tracing::instrument; #[derive(Debug, FromRow)] pub struct User { @@ -10,6 +11,7 @@ pub struct User { } impl User { + #[instrument] pub async fn new( driver: &Database, user_id: String, @@ -33,6 +35,7 @@ impl User { }) } + #[instrument] pub async fn get_by_id(driver: &Database, id: &str) -> Result> { Ok(sqlx::query_as("SELECT * FROM users WHERE user_id = ?") .bind(id) @@ -40,12 +43,14 @@ impl User { .await?) } + #[instrument] pub async fn list(driver: &Database) -> Result> { Ok(sqlx::query_as("SELECT * FROM users") .fetch_all(&**driver) .await?) } + #[instrument] pub async fn list_permitted_scopes(&self, driver: &Database) -> Result> { Ok( sqlx::query_scalar("SELECT scope FROM user_permitted_scopes WHERE user_id = ?") @@ -55,6 +60,7 @@ impl User { ) } + #[instrument] pub async fn remove_permitted_scope(&self, driver: &Database, scope: &str) -> Result<()> { sqlx::query("DELETE FROM user_permitted_scopes WHERE user_id = ? AND scope = ?") .bind(&self.user_id) @@ -65,6 +71,7 @@ impl User { Ok(()) } + #[instrument] pub async fn grant_permitted_scope(&self, driver: &Database, scope: &str) -> Result<()> { sqlx::query("INSERT INTO user_permitted_scopes (user_id, scope) VALUES (?, ?)") .bind(&self.user_id) diff --git a/server/wilford/Cargo.toml b/server/wilford/Cargo.toml index a341e7b..4437fae 100644 --- a/server/wilford/Cargo.toml +++ b/server/wilford/Cargo.toml @@ -24,4 +24,6 @@ tokio = { version = "1.35.1", features = ["full"] } tracing = "0.1.40" tracing-actix-web = "0.7.9" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } -pem = "3.0.4" \ No newline at end of file +pem = "3.0.4" +tracing-error = "0.2.0" +rsa = "0.9.6" \ No newline at end of file diff --git a/server/wilford/src/config.rs b/server/wilford/src/config.rs index 9923c4e..c367b6c 100644 --- a/server/wilford/src/config.rs +++ b/server/wilford/src/config.rs @@ -9,6 +9,7 @@ struct EnvConfig { config_path: PathBuf, } +/* ANCHOR: config */ #[derive(Debug, Deserialize)] pub struct Config { pub http: HttpConfig, @@ -47,6 +48,7 @@ pub struct DatabaseConfig { pub struct DefaultClientConfig { pub redirect_uri: String, } +/* ANCHOR_END: config */ impl EnvConfig { fn new() -> Result { diff --git a/server/wilford/src/espo/user.rs b/server/wilford/src/espo/user.rs index 6f28979..0243acf 100644 --- a/server/wilford/src/espo/user.rs +++ b/server/wilford/src/espo/user.rs @@ -2,7 +2,7 @@ use base64::Engine; use espocrm_rs::{EspoApiClient, Method}; use reqwest::{Result, StatusCode}; use serde::Deserialize; -use tracing::warn; +use tracing::{instrument, Instrument, trace, warn, warn_span}; #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] @@ -22,15 +22,19 @@ pub enum LoginStatus { } impl EspoUser { + #[instrument(skip(client))] pub async fn get_by_id(client: &EspoApiClient, id: &str) -> Result { Ok(client .request::<(), &str>(Method::Get, &format!("User/{id}"), None, None) + .instrument(warn_span!("user::by_id")) .await? .error_for_status()? .json() + .instrument(warn_span!("user::by_id::json")) .await?) } + #[instrument(skip_all)] pub async fn try_login( host: &str, username: &str, @@ -51,7 +55,9 @@ impl EspoUser { request = request.header("Espo-Authorization-Code", totp); } - let result = request.send().await?; + let result = request.send() + .instrument(warn_span!("try_login::request")) + .await?; match result.status() { StatusCode::OK => { @@ -67,7 +73,10 @@ impl EspoUser { is_active: bool, } - let payload: Response = result.json().await?; + trace!("Deserializing EspoCRM response"); + let payload: Response = result.json() + .instrument(warn_span!("deserialize")) + .await?; if payload.user.is_active { Ok(LoginStatus::Ok(payload.user.id)) } else { @@ -80,7 +89,10 @@ impl EspoUser { message: String, } - let payload: Response = result.json().await?; + trace!("Deserializing EspoCRM response"); + let payload: Response = result.json() + .instrument(warn_span!("deserialize")) + .await?; if payload.message.eq("enterTotpCode") { Ok(LoginStatus::SecondStepRequired) } else { diff --git a/server/wilford/src/main.rs b/server/wilford/src/main.rs index 0648d9c..78649bf 100644 --- a/server/wilford/src/main.rs +++ b/server/wilford/src/main.rs @@ -10,6 +10,7 @@ use espocrm_rs::EspoApiClient; use noiseless_tracing_actix_web::NoiselessRootSpanBuilder; use tracing::info; use tracing_actix_web::TracingLogger; +use tracing_error::ErrorLayer; use tracing_subscriber::fmt::layer; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; @@ -97,7 +98,19 @@ fn install_tracing() { } tracing_subscriber::registry() - .with(EnvFilter::from_default_env()) - .with(layer().compact()) + .with(EnvFilter::from_default_env() + .add_directive("rustls=WARN" + .parse() + .expect("Invalid tracing directive") + ) + .add_directive("rustls=WARN" + .parse() + .expect("Invalid tracing directive") + ) + ) + .with(layer() + .pretty() + ) + .with(ErrorLayer::default()) .init(); } diff --git a/server/wilford/src/routes/auth.rs b/server/wilford/src/routes/auth.rs index 16091e7..0ef385d 100644 --- a/server/wilford/src/routes/auth.rs +++ b/server/wilford/src/routes/auth.rs @@ -1,6 +1,6 @@ use crate::espo::user::EspoUser; use crate::routes::appdata::{WDatabase, WEspo}; -use crate::routes::error::{WebError, WebResult}; +use crate::routes::error::{WebError, WebErrorKind, WebResult}; use actix_web::cookie::time::OffsetDateTime; use actix_web::dev::Payload; use actix_web::{FromRequest, HttpRequest}; @@ -39,17 +39,17 @@ impl FromRequest for Auth { let token_info = match AccessToken::get_by_token(&database, &token).await? { Some(v) => { if v.expires_at < OffsetDateTime::now_utc().unix_timestamp() { - return Err(WebError::Unauthorized); + return Err(WebErrorKind::Unauthorized.into()); } else { v } } - None => return Err(WebError::Unauthorized), + None => return Err(WebErrorKind::Unauthorized.into()), }; let espo_user = EspoUser::get_by_id(&espo_client, &token_info.user_id) .await - .map_err(|e| WebError::Espo(e))?; + .map_err(|e| WebErrorKind::Espo(e))?; Ok(Self { espo_user_id: espo_user.id, @@ -94,7 +94,7 @@ impl FromRequest for ConstantAccessTokenAuth { let token = get_authorization_token(&req)?; let cat = ConstantAccessToken::get_by_token(&database, &token) .await? - .ok_or(WebError::Unauthorized)?; + .ok_or(WebErrorKind::Unauthorized)?; Ok(Self { name: cat.name, @@ -122,5 +122,5 @@ fn get_authorization_token(req: &HttpRequest) -> WebResult { _ => {} } - Err(WebError::Unauthorized) + Err(WebErrorKind::Unauthorized.into()) } diff --git a/server/wilford/src/routes/error.rs b/server/wilford/src/routes/error.rs index 8c2475d..41b2fd7 100644 --- a/server/wilford/src/routes/error.rs +++ b/server/wilford/src/routes/error.rs @@ -1,11 +1,44 @@ +use std::fmt; +use std::fmt::{Formatter, Write}; use actix_web::http::StatusCode; use actix_web::ResponseError; use thiserror::Error; +use tracing_error::SpanTrace; pub type WebResult = Result; +#[derive(Error)] +#[error("{}", kind)] +pub struct WebError { + kind: WebErrorKind, + context: SpanTrace, +} + +impl fmt::Debug for WebError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.kind.fmt(f)?; + f.write_char('\n')?; + f.write_str(&self.context.to_string())?; + f.write_char('\n')?; + Ok(()) + } +} + +impl From for WebError +where + E: Into, +{ + #[track_caller] + fn from(value: E) -> Self { + Self { + kind: value.into(), + context: SpanTrace::capture(), + } + } +} + #[derive(Debug, Error)] -pub enum WebError { +pub enum WebErrorKind { #[error("Not found")] NotFound, #[error("Bad request")] @@ -22,19 +55,22 @@ pub enum WebError { Espo(reqwest::Error), #[error("Internal server error")] InternalServerError, + #[error("Failed to parse PKCS8 SPKI: {0}")] + RsaPkcs8Spki(#[from] rsa::pkcs8::spki::Error), } impl ResponseError for WebError { fn status_code(&self) -> StatusCode { - match self { - Self::NotFound => StatusCode::NOT_FOUND, - Self::BadRequest => StatusCode::BAD_REQUEST, - Self::Unauthorized => StatusCode::UNAUTHORIZED, - Self::Forbidden => StatusCode::FORBIDDEN, - Self::InvalidInternalState => StatusCode::INTERNAL_SERVER_ERROR, - Self::Database(_) => StatusCode::INTERNAL_SERVER_ERROR, - Self::Espo(_) => StatusCode::BAD_GATEWAY, - Self::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR, + match self.kind { + WebErrorKind::NotFound => StatusCode::NOT_FOUND, + WebErrorKind::BadRequest => StatusCode::BAD_REQUEST, + WebErrorKind::Unauthorized => StatusCode::UNAUTHORIZED, + WebErrorKind::Forbidden => StatusCode::FORBIDDEN, + WebErrorKind::InvalidInternalState => StatusCode::INTERNAL_SERVER_ERROR, + WebErrorKind::Database(_) => StatusCode::INTERNAL_SERVER_ERROR, + WebErrorKind::Espo(_) => StatusCode::BAD_GATEWAY, + WebErrorKind::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR, + WebErrorKind::RsaPkcs8Spki(_) => StatusCode::INTERNAL_SERVER_ERROR, } } } diff --git a/server/wilford/src/routes/oauth/token.rs b/server/wilford/src/routes/oauth/token.rs index 2b5afa6..ebbbb81 100644 --- a/server/wilford/src/routes/oauth/token.rs +++ b/server/wilford/src/routes/oauth/token.rs @@ -7,10 +7,10 @@ use actix_web::web; use database::oauth2_client::{ create_id_token, JwtSigningAlgorithm, OAuth2AuthorizationCode, OAuth2Client, RefreshToken, }; +use database::user::User; use serde::{Deserialize, Serialize}; use tap::TapFallible; use tracing::warn; -use database::user::User; #[derive(Deserialize)] pub struct Form { @@ -93,7 +93,8 @@ pub async fn token( id_token: create_id_token( config.oidc_issuer.clone(), &client, - &User::get_by_id(&database, &rtoken.user_id).await + &User::get_by_id(&database, &rtoken.user_id) + .await .map_err(|_| OAuth2ErrorKind::ServerError)? .ok_or(OAuth2ErrorKind::ServerError)?, &oidc_signing_key.0, @@ -135,7 +136,8 @@ pub async fn token( id_token: create_id_token( config.oidc_issuer.clone(), &client, - &User::get_by_id(&database, &rtoken.user_id).await + &User::get_by_id(&database, &rtoken.user_id) + .await .map_err(|_| OAuth2ErrorKind::ServerError)? .ok_or(OAuth2ErrorKind::ServerError)?, &oidc_signing_key.0, diff --git a/server/wilford/src/routes/v1/auth/authorization_info.rs b/server/wilford/src/routes/v1/auth/authorization_info.rs index a77de1e..b0c21ce 100644 --- a/server/wilford/src/routes/v1/auth/authorization_info.rs +++ b/server/wilford/src/routes/v1/auth/authorization_info.rs @@ -1,9 +1,11 @@ -use crate::routes::appdata::WDatabase; -use crate::routes::error::{WebError, WebResult}; use actix_web::web; -use database::oauth2_client::{OAuth2Client, OAuth2PendingAuthorization}; use serde::{Deserialize, Serialize}; +use database::oauth2_client::{OAuth2Client, OAuth2PendingAuthorization}; + +use crate::routes::appdata::WDatabase; +use crate::routes::error::{WebErrorKind, WebResult}; + #[derive(Deserialize)] pub struct Query { authorization: String, @@ -21,16 +23,18 @@ pub async fn authorization_info( ) -> WebResult> { let authorization = OAuth2PendingAuthorization::get_by_id(&database, &query.authorization) .await? - .ok_or(WebError::NotFound)?; + .ok_or(WebErrorKind::NotFound)?; match &authorization { OAuth2PendingAuthorization::Authorized(_) => {} - OAuth2PendingAuthorization::Unauthorized(_) => return Err(WebError::Unauthorized), + OAuth2PendingAuthorization::Unauthorized(_) => { + return Err(WebErrorKind::Unauthorized.into()) + } } let client = OAuth2Client::get_by_client_id(&database, &authorization.client_id()) .await? - .ok_or(WebError::NotFound)?; + .ok_or(WebErrorKind::NotFound)?; Ok(web::Json(Response { client_name: client.name, diff --git a/server/wilford/src/routes/v1/auth/authorize.rs b/server/wilford/src/routes/v1/auth/authorize.rs index 3fa5fbb..d04f6af 100644 --- a/server/wilford/src/routes/v1/auth/authorize.rs +++ b/server/wilford/src/routes/v1/auth/authorize.rs @@ -13,7 +13,7 @@ use database::user::User; use crate::response_types::Redirect; use crate::routes::appdata::{WConfig, WDatabase}; -use crate::routes::error::{WebError, WebResult}; +use crate::routes::error::{WebErrorKind, WebResult}; use crate::routes::oauth::{OAuth2AuthorizationResponse, OAuth2Error, OAuth2ErrorKind}; use crate::routes::WOidcSigningKey; @@ -33,11 +33,11 @@ pub async fn authorize( let pending_authorization = OAuth2PendingAuthorization::get_by_id(&database, &query.authorization) .await? - .ok_or(WebError::NotFound)?; + .ok_or(WebErrorKind::NotFound)?; let client = OAuth2Client::get_by_client_id(&database, &pending_authorization.client_id()) .await? - .ok_or(WebError::NotFound)?; + .ok_or(WebErrorKind::NotFound)?; if !query.grant { return Ok(OAuth2AuthorizationResponse::Err(OAuth2Error::new( @@ -54,9 +54,9 @@ pub async fn authorize( .new_authorization_code(&database, pending_authorization) .await .map_err(|e| match e { - OAuth2AuthorizationCodeCreationError::Sqlx(e) => WebError::Database(e), + OAuth2AuthorizationCodeCreationError::Sqlx(e) => WebErrorKind::Database(e), OAuth2AuthorizationCodeCreationError::Unauthorized => { - WebError::InvalidInternalState + WebErrorKind::InvalidInternalState } })?; @@ -98,15 +98,16 @@ pub async fn authorize( create_id_token( config.oidc_issuer.clone(), &client, - &User::get_by_id(&database, &access_token.user_id).await? - .ok_or(WebError::InternalServerError)?, + &User::get_by_id(&database, &access_token.user_id) + .await? + .ok_or(WebErrorKind::InternalServerError)?, &oidc_signing_key.0, &access_token, nonce, JwtSigningAlgorithm::RS256, ) .tap_err(|e| warn!("Failed to create ID token: {e}")) - .map_err(|_| WebError::InternalServerError)? + .map_err(|_| WebErrorKind::InternalServerError)? ), access_token, state @@ -129,8 +130,10 @@ async fn new_access_token( .new_access_token(&database, pending_authorization) .await .map_err(|e| match e { - OAuth2AuthorizationCodeCreationError::Sqlx(e) => WebError::Database(e), - OAuth2AuthorizationCodeCreationError::Unauthorized => WebError::InvalidInternalState, + OAuth2AuthorizationCodeCreationError::Sqlx(e) => WebErrorKind::Database(e), + OAuth2AuthorizationCodeCreationError::Unauthorized => { + WebErrorKind::InvalidInternalState + } })?) } diff --git a/server/wilford/src/routes/v1/auth/login.rs b/server/wilford/src/routes/v1/auth/login.rs index f25def6..92d9b24 100644 --- a/server/wilford/src/routes/v1/auth/login.rs +++ b/server/wilford/src/routes/v1/auth/login.rs @@ -1,13 +1,14 @@ use crate::espo::user::{EspoUser, LoginStatus}; use crate::routes::appdata::{WConfig, WDatabase, WEspo}; -use crate::routes::error::{WebError, WebResult}; +use crate::routes::error::{WebErrorKind, WebResult}; use actix_web::web; use database::oauth2_client::OAuth2PendingAuthorization; use database::user::User; use serde::{Deserialize, Serialize}; use std::collections::HashSet; +use tracing::instrument; -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] pub struct Request { authorization: String, username: String, @@ -21,6 +22,7 @@ pub struct Response { totp_required: bool, } +#[instrument(skip_all)] pub async fn login( database: WDatabase, config: WConfig, @@ -29,7 +31,7 @@ pub async fn login( ) -> WebResult> { let authorization = OAuth2PendingAuthorization::get_by_id(&database, &payload.authorization) .await? - .ok_or(WebError::NotFound)?; + .ok_or(WebErrorKind::NotFound)?; let login = EspoUser::try_login( &config.espo.host, @@ -38,7 +40,7 @@ pub async fn login( payload.totp_code.as_deref(), ) .await - .map_err(|e| WebError::Espo(e))?; + .map_err(|e| WebErrorKind::Espo(e))?; // OAuth2 defines `scope` to be all scopes, seperated by a ' ' (space char) // Where duplicates can be ignored. @@ -71,13 +73,13 @@ pub async fn login( .collect::>(); if !disallowed_scopes.is_empty() { - return Err(WebError::Forbidden); + return Err(WebErrorKind::Forbidden.into()); } } None => { let espo_user = EspoUser::get_by_id(&espo, &id) .await - .map_err(|e| WebError::Espo(e))?; + .map_err(|e| WebErrorKind::Espo(e))?; let user = User::new( &database, @@ -96,7 +98,7 @@ pub async fn login( scope_set.difference(&oidc_scopes).collect::>(); if !disallowed_scopes.is_empty() { - return Err(WebError::Forbidden); + return Err(WebErrorKind::Forbidden.into()); } } } @@ -105,7 +107,7 @@ pub async fn login( authorization .set_user_id(&database, &id) .await - .map_err(|_| WebError::BadRequest)?; + .map_err(|_| WebErrorKind::BadRequest)?; Ok(web::Json(Response { status: true, diff --git a/server/wilford/src/routes/v1/cat/add.rs b/server/wilford/src/routes/v1/cat/add.rs index 0f64cff..2b0f30c 100644 --- a/server/wilford/src/routes/v1/cat/add.rs +++ b/server/wilford/src/routes/v1/cat/add.rs @@ -1,11 +1,13 @@ +use actix_web::web; +use serde::Deserialize; + +use database::constant_access_tokens::ConstantAccessToken; + use crate::response_types::Empty; use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::error::{WebError, WebResult}; +use crate::routes::error::{WebErrorKind, WebResult}; use crate::routes::v1::MANAGE_SCOPE; -use actix_web::web; -use database::constant_access_tokens::ConstantAccessToken; -use serde::Deserialize; #[derive(Deserialize)] pub struct Request { @@ -14,7 +16,7 @@ pub struct Request { pub async fn add(database: WDatabase, auth: Auth, payload: web::Json) -> WebResult { if !auth.has_scope(MANAGE_SCOPE) { - return Err(WebError::Forbidden); + return Err(WebErrorKind::Forbidden.into()); } let exists = ConstantAccessToken::list(&database) @@ -24,11 +26,11 @@ pub async fn add(database: WDatabase, auth: Auth, payload: web::Json) - .is_some(); if exists { - return Err(WebError::BadRequest); + return Err(WebErrorKind::BadRequest.into()); } if payload.name.len() > 64 { - return Err(WebError::BadRequest); + return Err(WebErrorKind::BadRequest.into()); } ConstantAccessToken::new(&database, payload.name.clone()).await?; diff --git a/server/wilford/src/routes/v1/cat/list.rs b/server/wilford/src/routes/v1/cat/list.rs index 54f65bf..d637318 100644 --- a/server/wilford/src/routes/v1/cat/list.rs +++ b/server/wilford/src/routes/v1/cat/list.rs @@ -1,10 +1,12 @@ +use actix_web::web; +use serde::Serialize; + +use database::constant_access_tokens::ConstantAccessToken; + use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::error::{WebError, WebResult}; +use crate::routes::error::{WebErrorKind, WebResult}; use crate::routes::v1::MANAGE_SCOPE; -use actix_web::web; -use database::constant_access_tokens::ConstantAccessToken; -use serde::Serialize; #[derive(Serialize)] pub struct Response { @@ -19,7 +21,7 @@ pub struct Cat { pub async fn list(database: WDatabase, auth: Auth) -> WebResult> { if !auth.has_scope(MANAGE_SCOPE) { - return Err(WebError::Forbidden); + return Err(WebErrorKind::Forbidden.into()); } let tokens = ConstantAccessToken::list(&database) diff --git a/server/wilford/src/routes/v1/cat/remove.rs b/server/wilford/src/routes/v1/cat/remove.rs index 65ff491..553f127 100644 --- a/server/wilford/src/routes/v1/cat/remove.rs +++ b/server/wilford/src/routes/v1/cat/remove.rs @@ -1,11 +1,13 @@ +use actix_web::web; +use serde::Deserialize; + +use database::constant_access_tokens::ConstantAccessToken; + use crate::response_types::Empty; use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::error::{WebError, WebResult}; +use crate::routes::error::{WebErrorKind, WebResult}; use crate::routes::v1::MANAGE_SCOPE; -use actix_web::web; -use database::constant_access_tokens::ConstantAccessToken; -use serde::Deserialize; #[derive(Deserialize)] pub struct Request { @@ -18,12 +20,12 @@ pub async fn remove( payload: web::Json, ) -> WebResult { if !auth.has_scope(MANAGE_SCOPE) { - return Err(WebError::Forbidden); + return Err(WebErrorKind::Forbidden.into()); } let cat = ConstantAccessToken::get_by_token(&database, &payload.token) .await? - .ok_or(WebError::NotFound)?; + .ok_or(WebErrorKind::NotFound)?; cat.revoke(&database).await?; Ok(Empty) diff --git a/server/wilford/src/routes/v1/clients/add.rs b/server/wilford/src/routes/v1/clients/add.rs index 6a1648f..1dc5044 100644 --- a/server/wilford/src/routes/v1/clients/add.rs +++ b/server/wilford/src/routes/v1/clients/add.rs @@ -1,11 +1,13 @@ +use actix_web::web; +use serde::Deserialize; + +use database::oauth2_client::OAuth2Client; + use crate::response_types::Empty; use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::error::{WebError, WebResult}; +use crate::routes::error::{WebErrorKind, WebResult}; use crate::routes::v1::MANAGE_SCOPE; -use actix_web::web; -use database::oauth2_client::OAuth2Client; -use serde::Deserialize; #[derive(Deserialize)] pub struct Request { @@ -15,11 +17,11 @@ pub struct Request { pub async fn add(database: WDatabase, auth: Auth, payload: web::Json) -> WebResult { if !auth.has_scope(MANAGE_SCOPE) { - return Err(WebError::Forbidden); + return Err(WebErrorKind::Forbidden.into()); } if payload.name.len() > 64 { - return Err(WebError::BadRequest); + return Err(WebErrorKind::BadRequest.into()); } let exists = OAuth2Client::list(&database) @@ -29,7 +31,7 @@ pub async fn add(database: WDatabase, auth: Auth, payload: web::Json) - .is_some(); if exists { - return Err(WebError::BadRequest); + return Err(WebErrorKind::BadRequest.into()); } OAuth2Client::new( diff --git a/server/wilford/src/routes/v1/clients/internal.rs b/server/wilford/src/routes/v1/clients/internal.rs index 6e9093b..0d21f82 100644 --- a/server/wilford/src/routes/v1/clients/internal.rs +++ b/server/wilford/src/routes/v1/clients/internal.rs @@ -1,9 +1,11 @@ -use crate::routes::appdata::WDatabase; -use crate::routes::error::{WebError, WebResult}; use actix_web::web; -use database::oauth2_client::OAuth2Client; use serde::Serialize; +use database::oauth2_client::OAuth2Client; + +use crate::routes::appdata::WDatabase; +use crate::routes::error::{WebErrorKind, WebResult}; + #[derive(Serialize)] pub struct Response { name: String, @@ -17,7 +19,7 @@ pub async fn internal(database: WDatabase) -> WebResult> { .await? .into_iter() .find(|c| c.is_internal) - .ok_or(WebError::InvalidInternalState)?; + .ok_or(WebErrorKind::InvalidInternalState)?; Ok(web::Json(Response { name: client.name, diff --git a/server/wilford/src/routes/v1/clients/list.rs b/server/wilford/src/routes/v1/clients/list.rs index ce02729..27e3a6a 100644 --- a/server/wilford/src/routes/v1/clients/list.rs +++ b/server/wilford/src/routes/v1/clients/list.rs @@ -1,10 +1,12 @@ +use actix_web::web; +use serde::Serialize; + +use database::oauth2_client::OAuth2Client; + use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::error::{WebError, WebResult}; +use crate::routes::error::{WebErrorKind, WebResult}; use crate::routes::v1::MANAGE_SCOPE; -use actix_web::web; -use database::oauth2_client::OAuth2Client; -use serde::Serialize; #[derive(Serialize)] pub struct Response { @@ -21,7 +23,7 @@ pub struct Client { pub async fn list(database: WDatabase, auth: Auth) -> WebResult> { if !auth.has_scope(MANAGE_SCOPE) { - return Err(WebError::Forbidden); + return Err(WebErrorKind::Forbidden.into()); } let clients = OAuth2Client::list(&database) diff --git a/server/wilford/src/routes/v1/clients/remove.rs b/server/wilford/src/routes/v1/clients/remove.rs index f988756..d31808f 100644 --- a/server/wilford/src/routes/v1/clients/remove.rs +++ b/server/wilford/src/routes/v1/clients/remove.rs @@ -1,11 +1,13 @@ +use actix_web::web; +use serde::Deserialize; + +use database::oauth2_client::OAuth2Client; + use crate::response_types::Empty; use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::error::{WebError, WebResult}; +use crate::routes::error::{WebErrorKind, WebResult}; use crate::routes::v1::MANAGE_SCOPE; -use actix_web::web; -use database::oauth2_client::OAuth2Client; -use serde::Deserialize; #[derive(Deserialize)] pub struct Request { @@ -18,12 +20,12 @@ pub async fn remove( payload: web::Json, ) -> WebResult { if !auth.has_scope(MANAGE_SCOPE) { - return Err(WebError::Forbidden); + return Err(WebErrorKind::Forbidden.into()); } let client = OAuth2Client::get_by_client_id(&database, &payload.client_id) .await? - .ok_or(WebError::NotFound)?; + .ok_or(WebErrorKind::NotFound)?; client.delete(&database).await?; Ok(Empty) diff --git a/server/wilford/src/routes/v1/user/list.rs b/server/wilford/src/routes/v1/user/list.rs index 8f8b88a..5b02d41 100644 --- a/server/wilford/src/routes/v1/user/list.rs +++ b/server/wilford/src/routes/v1/user/list.rs @@ -1,9 +1,10 @@ +use actix_web::web; +use serde::Serialize; + use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::error::{WebError, WebResult}; +use crate::routes::error::{WebErrorKind, WebResult}; use crate::routes::v1::MANAGE_SCOPE; -use actix_web::web; -use serde::Serialize; #[derive(Serialize)] pub struct Response { @@ -19,7 +20,7 @@ pub struct User { pub async fn list(database: WDatabase, auth: Auth) -> WebResult> { if !auth.has_scope(MANAGE_SCOPE) { - return Err(WebError::Forbidden); + return Err(WebErrorKind::Forbidden.into()); } let users = database::user::User::list(&database) diff --git a/server/wilford/src/routes/v1/user/permitted_scopes/add.rs b/server/wilford/src/routes/v1/user/permitted_scopes/add.rs index d72d0f1..ba2d649 100644 --- a/server/wilford/src/routes/v1/user/permitted_scopes/add.rs +++ b/server/wilford/src/routes/v1/user/permitted_scopes/add.rs @@ -1,11 +1,13 @@ +use actix_web::web; +use serde::Deserialize; + +use database::user::User; + use crate::response_types::Empty; use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::error::{WebError, WebResult}; +use crate::routes::error::{WebErrorKind, WebResult}; use crate::routes::v1::MANAGE_SCOPE; -use actix_web::web; -use database::user::User; -use serde::Deserialize; #[derive(Deserialize)] pub struct Payload { @@ -17,16 +19,16 @@ pub struct Payload { pub async fn add(database: WDatabase, auth: Auth, payload: web::Json) -> WebResult { if !auth.has_scope(MANAGE_SCOPE) { - return Err(WebError::Forbidden); + return Err(WebErrorKind::Forbidden.into()); } let user = User::get_by_id(&database, &payload.to) .await? - .ok_or(WebError::NotFound)?; + .ok_or(WebErrorKind::NotFound)?; let current_scopes = user.list_permitted_scopes(&database).await?; if current_scopes.contains(&payload.scope) { - return Err(WebError::BadRequest); + return Err(WebErrorKind::BadRequest.into()); } user.grant_permitted_scope(&database, &payload.scope) diff --git a/server/wilford/src/routes/v1/user/permitted_scopes/list.rs b/server/wilford/src/routes/v1/user/permitted_scopes/list.rs index 5aaf5c4..1e22a29 100644 --- a/server/wilford/src/routes/v1/user/permitted_scopes/list.rs +++ b/server/wilford/src/routes/v1/user/permitted_scopes/list.rs @@ -1,10 +1,12 @@ +use actix_web::web; +use serde::{Deserialize, Serialize}; + +use database::user::User; + use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::error::{WebError, WebResult}; +use crate::routes::error::{WebErrorKind, WebResult}; use crate::routes::v1::MANAGE_SCOPE; -use actix_web::web; -use database::user::User; -use serde::{Deserialize, Serialize}; #[derive(Serialize)] pub struct Response { @@ -23,12 +25,12 @@ pub async fn list( query: web::Query, ) -> WebResult> { if !auth.has_scope(MANAGE_SCOPE) { - return Err(WebError::Forbidden); + return Err(WebErrorKind::Forbidden.into()); } let user = User::get_by_id(&database, &query.user) .await? - .ok_or(WebError::NotFound)?; + .ok_or(WebErrorKind::NotFound)?; let scopes = user.list_permitted_scopes(&database).await?; Ok(web::Json(Response { scopes })) diff --git a/server/wilford/src/routes/v1/user/permitted_scopes/remove.rs b/server/wilford/src/routes/v1/user/permitted_scopes/remove.rs index 8c2d61c..83a3f08 100644 --- a/server/wilford/src/routes/v1/user/permitted_scopes/remove.rs +++ b/server/wilford/src/routes/v1/user/permitted_scopes/remove.rs @@ -1,11 +1,13 @@ +use actix_web::web; +use serde::Deserialize; + +use database::user::User; + use crate::response_types::Empty; use crate::routes::appdata::WDatabase; use crate::routes::auth::Auth; -use crate::routes::error::{WebError, WebResult}; +use crate::routes::error::{WebErrorKind, WebResult}; use crate::routes::v1::MANAGE_SCOPE; -use actix_web::web; -use database::user::User; -use serde::Deserialize; #[derive(Deserialize)] pub struct Request { @@ -21,16 +23,16 @@ pub async fn remove( payload: web::Json, ) -> WebResult { if !auth.has_scope(MANAGE_SCOPE) { - return Err(WebError::Forbidden); + return Err(WebErrorKind::Forbidden.into()); } let user = User::get_by_id(&database, &payload.from) .await? - .ok_or(WebError::NotFound)?; + .ok_or(WebErrorKind::NotFound)?; let current_scops = user.list_permitted_scopes(&database).await?; if !current_scops.contains(&payload.scope) { - return Err(WebError::NotFound); + return Err(WebErrorKind::NotFound.into()); } user.remove_permitted_scope(&database, &payload.scope) diff --git a/server/wilford/src/routes/well_known/jwks.rs b/server/wilford/src/routes/well_known/jwks.rs index bc16e49..3572ef0 100644 --- a/server/wilford/src/routes/well_known/jwks.rs +++ b/server/wilford/src/routes/well_known/jwks.rs @@ -1,6 +1,13 @@ -use crate::routes::appdata::WOidcPublicKey; use actix_web::web; -use serde::Serialize; +use base64::Engine; +use base64::prelude::BASE64_URL_SAFE_NO_PAD; +use rsa::{BigUint, RsaPublicKey}; +use rsa::pkcs8::DecodePublicKey; +use rsa::traits::PublicKeyParts; +use serde::{Serialize, Serializer}; + +use crate::routes::appdata::WOidcPublicKey; +use crate::routes::error::WebResult; #[derive(Serialize)] pub struct Jwks { @@ -12,16 +19,39 @@ pub struct Key { kty: String, r#use: String, alg: String, - k: String, + kid: String, + key_ops: Vec, + // k: String, + #[serde(serialize_with = "serialize")] + n: BigUint, + #[serde(serialize_with = "serialize")] + e: BigUint, } -pub async fn jwks(oidc_public_key: WOidcPublicKey) -> web::Json { - web::Json(Jwks { +pub async fn jwks(oidc_public_key: WOidcPublicKey) -> WebResult> { + let public_key = RsaPublicKey::from_public_key_pem(&oidc_public_key.0)?; + + Ok(web::Json(Jwks { keys: vec![Key { kty: "RSA".to_string(), - r#use: "verify".to_string(), + r#use: "sig".to_string(), alg: "RS256".to_string(), - k: (**oidc_public_key).0.clone(), + kid: "rsa".to_string(), // We dont have a kid + key_ops: vec![ + "verify".to_string(), + ], + // k: (**oidc_public_key).0.clone(), + n: public_key.n().clone(), + e: public_key.e().clone() }], - }) + })) } + +pub fn serialize(value: &BigUint, serializer: S) -> Result +where + S: Serializer, +{ + let bytes = value.to_bytes_be(); + let base64 = BASE64_URL_SAFE_NO_PAD.encode(bytes.as_slice()); + serializer.serialize_str(&base64) +} \ No newline at end of file diff --git a/server/wilford/src/routes/well_known/openid_configuration.rs b/server/wilford/src/routes/well_known/openid_configuration.rs index 210db9f..36fa282 100644 --- a/server/wilford/src/routes/well_known/openid_configuration.rs +++ b/server/wilford/src/routes/well_known/openid_configuration.rs @@ -10,6 +10,7 @@ pub struct OpenidConfiguration { response_types_supported: Vec, grant_types_supported: Vec, id_token_signing_alg_values_supported: Vec, + jwks_uri: String, } pub async fn openid_configuration(config: WConfig) -> web::Json { @@ -24,5 +25,6 @@ pub async fn openid_configuration(config: WConfig) -> web::Json Date: Sun, 3 Nov 2024 20:45:18 +0100 Subject: [PATCH 17/17] Fmt --- server/wilford/src/espo/user.rs | 13 +++++-------- server/wilford/src/main.rs | 17 +++++------------ server/wilford/src/routes/error.rs | 4 ++-- server/wilford/src/routes/oauth/authorize.rs | 4 ++-- server/wilford/src/routes/well_known/jwks.rs | 12 +++++------- 5 files changed, 19 insertions(+), 31 deletions(-) diff --git a/server/wilford/src/espo/user.rs b/server/wilford/src/espo/user.rs index 0243acf..f9c2c22 100644 --- a/server/wilford/src/espo/user.rs +++ b/server/wilford/src/espo/user.rs @@ -2,7 +2,7 @@ use base64::Engine; use espocrm_rs::{EspoApiClient, Method}; use reqwest::{Result, StatusCode}; use serde::Deserialize; -use tracing::{instrument, Instrument, trace, warn, warn_span}; +use tracing::{instrument, trace, warn, warn_span, Instrument}; #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] @@ -55,7 +55,8 @@ impl EspoUser { request = request.header("Espo-Authorization-Code", totp); } - let result = request.send() + let result = request + .send() .instrument(warn_span!("try_login::request")) .await?; @@ -74,9 +75,7 @@ impl EspoUser { } trace!("Deserializing EspoCRM response"); - let payload: Response = result.json() - .instrument(warn_span!("deserialize")) - .await?; + let payload: Response = result.json().instrument(warn_span!("deserialize")).await?; if payload.user.is_active { Ok(LoginStatus::Ok(payload.user.id)) } else { @@ -90,9 +89,7 @@ impl EspoUser { } trace!("Deserializing EspoCRM response"); - let payload: Response = result.json() - .instrument(warn_span!("deserialize")) - .await?; + let payload: Response = result.json().instrument(warn_span!("deserialize")).await?; if payload.message.eq("enterTotpCode") { Ok(LoginStatus::SecondStepRequired) } else { diff --git a/server/wilford/src/main.rs b/server/wilford/src/main.rs index 78649bf..e9a94ad 100644 --- a/server/wilford/src/main.rs +++ b/server/wilford/src/main.rs @@ -98,19 +98,12 @@ fn install_tracing() { } tracing_subscriber::registry() - .with(EnvFilter::from_default_env() - .add_directive("rustls=WARN" - .parse() - .expect("Invalid tracing directive") - ) - .add_directive("rustls=WARN" - .parse() - .expect("Invalid tracing directive") - ) - ) - .with(layer() - .pretty() + .with( + EnvFilter::from_default_env() + .add_directive("rustls=WARN".parse().expect("Invalid tracing directive")) + .add_directive("rustls=WARN".parse().expect("Invalid tracing directive")), ) + .with(layer().pretty()) .with(ErrorLayer::default()) .init(); } diff --git a/server/wilford/src/routes/error.rs b/server/wilford/src/routes/error.rs index 41b2fd7..a4bf216 100644 --- a/server/wilford/src/routes/error.rs +++ b/server/wilford/src/routes/error.rs @@ -1,7 +1,7 @@ -use std::fmt; -use std::fmt::{Formatter, Write}; use actix_web::http::StatusCode; use actix_web::ResponseError; +use std::fmt; +use std::fmt::{Formatter, Write}; use thiserror::Error; use tracing_error::SpanTrace; diff --git a/server/wilford/src/routes/oauth/authorize.rs b/server/wilford/src/routes/oauth/authorize.rs index 8785810..c704396 100644 --- a/server/wilford/src/routes/oauth/authorize.rs +++ b/server/wilford/src/routes/oauth/authorize.rs @@ -79,7 +79,7 @@ pub async fn authorize( &database, query.scope.clone(), query.state.clone(), - rt_to_at(&query.response_type), + response_to_authorization_type(&query.response_type), query.nonce.clone(), ) .await; @@ -104,7 +104,7 @@ pub async fn authorize( )))) } -fn rt_to_at(rt: &ResponseType) -> AuthorizationType { +fn response_to_authorization_type(rt: &ResponseType) -> AuthorizationType { match rt { ResponseType::Code => AuthorizationType::AuthorizationCode, ResponseType::Token => AuthorizationType::Implicit, diff --git a/server/wilford/src/routes/well_known/jwks.rs b/server/wilford/src/routes/well_known/jwks.rs index 3572ef0..45e1ad9 100644 --- a/server/wilford/src/routes/well_known/jwks.rs +++ b/server/wilford/src/routes/well_known/jwks.rs @@ -1,9 +1,9 @@ use actix_web::web; -use base64::Engine; use base64::prelude::BASE64_URL_SAFE_NO_PAD; -use rsa::{BigUint, RsaPublicKey}; +use base64::Engine; use rsa::pkcs8::DecodePublicKey; use rsa::traits::PublicKeyParts; +use rsa::{BigUint, RsaPublicKey}; use serde::{Serialize, Serializer}; use crate::routes::appdata::WOidcPublicKey; @@ -37,12 +37,10 @@ pub async fn jwks(oidc_public_key: WOidcPublicKey) -> WebResult> r#use: "sig".to_string(), alg: "RS256".to_string(), kid: "rsa".to_string(), // We dont have a kid - key_ops: vec![ - "verify".to_string(), - ], + key_ops: vec!["verify".to_string()], // k: (**oidc_public_key).0.clone(), n: public_key.n().clone(), - e: public_key.e().clone() + e: public_key.e().clone(), }], })) } @@ -54,4 +52,4 @@ where let bytes = value.to_bytes_be(); let base64 = BASE64_URL_SAFE_NO_PAD.encode(bytes.as_slice()); serializer.serialize_str(&base64) -} \ No newline at end of file +}