mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
636 consent remembering in oauth2 (#824)
This commit is contained in:
parent
4a1df985b9
commit
9d929b876c
|
@ -57,18 +57,23 @@ pub struct AuthorisationRequestOidc {
|
||||||
pub acr: Option<String>,
|
pub acr: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// We ask our user to consent to this Authorisation Request with the
|
/// When we request to authorise, it can either prompt us for consent,
|
||||||
/// following data.
|
/// or it can immediately be granted due the past grant.
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct ConsentRequest {
|
pub enum AuthorisationResponse {
|
||||||
// A pretty-name of the client
|
ConsentRequested {
|
||||||
pub client_name: String,
|
// A pretty-name of the client
|
||||||
// A list of scopes requested / to be issued.
|
client_name: String,
|
||||||
pub scopes: Vec<String>,
|
// A list of scopes requested / to be issued.
|
||||||
// The users displayname (?)
|
scopes: Vec<String>,
|
||||||
// pub display_name: String,
|
// Extra PII that may be requested
|
||||||
// The token we need to be given back to allow this to proceed
|
pii_scopes: Vec<String>,
|
||||||
pub consent_token: String,
|
// The users displayname (?)
|
||||||
|
// pub display_name: String,
|
||||||
|
// The token we need to be given back to allow this to proceed
|
||||||
|
consent_token: String,
|
||||||
|
},
|
||||||
|
Permitted,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The resource server then contacts the token endpoint with
|
// The resource server then contacts the token endpoint with
|
||||||
|
|
|
@ -11,8 +11,8 @@ LABEL maintainer william@blackhats.net.au
|
||||||
|
|
||||||
RUN zypper install -y \
|
RUN zypper install -y \
|
||||||
cargo \
|
cargo \
|
||||||
rust \
|
rust wasm-pack \
|
||||||
gcc clang lld \
|
clang lld \
|
||||||
make automake autoconf \
|
make automake autoconf \
|
||||||
libopenssl-devel pam-devel \
|
libopenssl-devel pam-devel \
|
||||||
sqlite3-devel \
|
sqlite3-devel \
|
||||||
|
@ -20,7 +20,6 @@ RUN zypper install -y \
|
||||||
zypper clean -a
|
zypper clean -a
|
||||||
|
|
||||||
COPY . /usr/src/kanidm
|
COPY . /usr/src/kanidm
|
||||||
WORKDIR /usr/src/kanidm/kanidmd/daemon
|
|
||||||
|
|
||||||
ARG SCCACHE_REDIS=""
|
ARG SCCACHE_REDIS=""
|
||||||
ARG KANIDM_FEATURES
|
ARG KANIDM_FEATURES
|
||||||
|
@ -31,25 +30,38 @@ RUN mkdir /scratch
|
||||||
RUN echo $KANIDM_BUILD_PROFILE
|
RUN echo $KANIDM_BUILD_PROFILE
|
||||||
RUN echo $KANIDM_FEATURES
|
RUN echo $KANIDM_FEATURES
|
||||||
|
|
||||||
ENV RUSTFLAGS="-Clinker=clang -Clink-arg=-fuse-ld=/usr/bin/ld.lld"
|
|
||||||
ENV CARGO_HOME=/scratch/.cargo
|
ENV CARGO_HOME=/scratch/.cargo
|
||||||
|
ENV RUSTFLAGS="-Clinker=clang"
|
||||||
|
|
||||||
|
WORKDIR /usr/src/kanidm/kanidmd_web_ui
|
||||||
|
RUN if [ "${SCCACHE_REDIS}" != "" ]; \
|
||||||
|
then \
|
||||||
|
export CARGO_INCREMENTAL=false && \
|
||||||
|
export RUSTC_WRAPPER=sccache && \
|
||||||
|
sccache --start-server; \
|
||||||
|
fi && \
|
||||||
|
./build_wasm_dev.sh
|
||||||
|
|
||||||
|
WORKDIR /usr/src/kanidm/kanidmd/daemon
|
||||||
|
|
||||||
|
ENV RUSTFLAGS="-Clinker=clang -Clink-arg=-fuse-ld=/usr/bin/ld.lld"
|
||||||
|
|
||||||
RUN if [ "${SCCACHE_REDIS}" != "" ]; \
|
RUN if [ "${SCCACHE_REDIS}" != "" ]; \
|
||||||
then \
|
then \
|
||||||
export CC="/usr/bin/sccache /usr/bin/clang" && \
|
export CC="/usr/bin/sccache /usr/bin/clang" && \
|
||||||
export CARGO_INCREMENTAL=false && \
|
export CARGO_INCREMENTAL=false && \
|
||||||
export RUSTC_WRAPPER=sccache && \
|
export RUSTC_WRAPPER=sccache && \
|
||||||
sccache --start-server; \
|
sccache --start-server; \
|
||||||
else \
|
else \
|
||||||
export CC="/usr/bin/clang"; \
|
export CC="/usr/bin/clang"; \
|
||||||
fi && \
|
fi && \
|
||||||
cargo build ${KANIDM_BUILD_OPTIONS} \
|
cargo build ${KANIDM_BUILD_OPTIONS} \
|
||||||
--features=${KANIDM_FEATURES} \
|
--features=${KANIDM_FEATURES} \
|
||||||
--target-dir=/usr/src/kanidm/target/ \
|
--target-dir=/usr/src/kanidm/target/ \
|
||||||
--release && \
|
--release && \
|
||||||
if [ "${SCCACHE_REDIS}" != "" ]; \
|
if [ "${SCCACHE_REDIS}" != "" ]; \
|
||||||
then sccache -s; \
|
then sccache -s; \
|
||||||
fi;
|
fi;
|
||||||
|
|
||||||
RUN ls -al /usr/src/kanidm/target/release
|
RUN ls -al /usr/src/kanidm/target/release
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ path = "src/main.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
kanidm = { path = "../idm" }
|
kanidm = { path = "../idm" }
|
||||||
score = { path = "../score" }
|
score = { path = "../score" }
|
||||||
clap = { version = "^3.2", features = ["derive"] }
|
clap = { version = "^3.2", features = ["derive", "env"] }
|
||||||
users = "^0.11.0"
|
users = "^0.11.0"
|
||||||
serde = { version = "^1.0.137", features = ["derive"] }
|
serde = { version = "^1.0.137", features = ["derive"] }
|
||||||
tokio = { version = "^1.19.1", features = ["rt-multi-thread", "macros", "signal"] }
|
tokio = { version = "^1.19.1", features = ["rt-multi-thread", "macros", "signal"] }
|
||||||
|
|
|
@ -20,8 +20,8 @@ use kanidm_proto::v1::{BackupCodesView, OperationError, RadiusAuthToken};
|
||||||
use crate::filter::{Filter, FilterInvalid};
|
use crate::filter::{Filter, FilterInvalid};
|
||||||
use crate::idm::oauth2::{
|
use crate::idm::oauth2::{
|
||||||
AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AccessTokenRequest,
|
AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AccessTokenRequest,
|
||||||
AccessTokenResponse, AuthorisationRequest, AuthorisePermitSuccess, ConsentRequest, JwkKeySet,
|
AccessTokenResponse, AuthorisationRequest, AuthorisePermitSuccess, AuthoriseResponse,
|
||||||
Oauth2Error, OidcDiscoveryResponse, OidcToken,
|
JwkKeySet, Oauth2Error, OidcDiscoveryResponse, OidcToken,
|
||||||
};
|
};
|
||||||
use crate::idm::server::{IdmServer, IdmServerTransaction};
|
use crate::idm::server::{IdmServer, IdmServerTransaction};
|
||||||
use crate::ldap::{LdapBoundToken, LdapResponseState, LdapServer};
|
use crate::ldap::{LdapBoundToken, LdapResponseState, LdapServer};
|
||||||
|
@ -1120,7 +1120,7 @@ impl QueryServerReadV1 {
|
||||||
uat: Option<String>,
|
uat: Option<String>,
|
||||||
auth_req: AuthorisationRequest,
|
auth_req: AuthorisationRequest,
|
||||||
eventid: Uuid,
|
eventid: Uuid,
|
||||||
) -> Result<ConsentRequest, Oauth2Error> {
|
) -> Result<AuthoriseResponse, Oauth2Error> {
|
||||||
let ct = duration_from_epoch_now();
|
let ct = duration_from_epoch_now();
|
||||||
let idms_prox_read = self.idms.proxy_read_async().await;
|
let idms_prox_read = self.idms.proxy_read_async().await;
|
||||||
let res = spanned!("actors::v1_read::handle<Oauth2Authorise>", {
|
let res = spanned!("actors::v1_read::handle<Oauth2Authorise>", {
|
||||||
|
|
|
@ -718,6 +718,37 @@ pub const JSON_SCHEMA_ATTR_OAUTH2_RS_IMPLICIT_SCOPES: &str = r#"{
|
||||||
}
|
}
|
||||||
}"#;
|
}"#;
|
||||||
|
|
||||||
|
pub const JSON_SCHEMA_ATTR_OAUTH2_CONSENT_SCOPE_MAP: &str = r#"{
|
||||||
|
"attrs": {
|
||||||
|
"class": [
|
||||||
|
"object",
|
||||||
|
"system",
|
||||||
|
"attributetype"
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
"A set of scopes mapped from a relying server to a user, where the user has previously consented to the following. If changed or deleted, consent will be re-sought."
|
||||||
|
],
|
||||||
|
"index": [
|
||||||
|
"EQUALITY"
|
||||||
|
],
|
||||||
|
"unique": [
|
||||||
|
"false"
|
||||||
|
],
|
||||||
|
"multivalue": [
|
||||||
|
"true"
|
||||||
|
],
|
||||||
|
"attributename": [
|
||||||
|
"oauth2_consent_scope_map"
|
||||||
|
],
|
||||||
|
"syntax": [
|
||||||
|
"OAUTH_SCOPE_MAP"
|
||||||
|
],
|
||||||
|
"uuid": [
|
||||||
|
"00000000-0000-0000-0000-ffff00000097"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}"#;
|
||||||
|
|
||||||
pub const JSON_SCHEMA_ATTR_ES256_PRIVATE_KEY_DER: &str = r#"{
|
pub const JSON_SCHEMA_ATTR_ES256_PRIVATE_KEY_DER: &str = r#"{
|
||||||
"attrs": {
|
"attrs": {
|
||||||
"class": [
|
"class": [
|
||||||
|
@ -973,7 +1004,8 @@ pub const JSON_SCHEMA_CLASS_ACCOUNT: &str = r#"
|
||||||
"radius_secret",
|
"radius_secret",
|
||||||
"account_expire",
|
"account_expire",
|
||||||
"account_valid_from",
|
"account_valid_from",
|
||||||
"mail"
|
"mail",
|
||||||
|
"oauth2_consent_scope_map"
|
||||||
],
|
],
|
||||||
"systemmust": [
|
"systemmust": [
|
||||||
"displayname",
|
"displayname",
|
||||||
|
|
|
@ -167,6 +167,8 @@ pub const UUID_SCHEMA_ATTR_FERNET_PRIVATE_KEY_STR: Uuid =
|
||||||
uuid!("00000000-0000-0000-0000-ffff00000095");
|
uuid!("00000000-0000-0000-0000-ffff00000095");
|
||||||
pub const _UUID_SCHEMA_ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN: Uuid =
|
pub const _UUID_SCHEMA_ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN: Uuid =
|
||||||
uuid!("00000000-0000-0000-0000-ffff00000096");
|
uuid!("00000000-0000-0000-0000-ffff00000096");
|
||||||
|
pub const _UUID_SCHEMA_CLASS_OAUTH2_CONSENT_SCOPE_MAP: Uuid =
|
||||||
|
uuid!("00000000-0000-0000-0000-ffff00000097");
|
||||||
|
|
||||||
// System and domain infos
|
// System and domain infos
|
||||||
// I'd like to strongly criticise william of the past for making poor choices about these allocations.
|
// I'd like to strongly criticise william of the past for making poor choices about these allocations.
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use kanidm_proto::v1::UserAuthToken;
|
use kanidm_proto::v1::UserAuthToken;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::BTreeSet;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -161,4 +162,14 @@ impl Identity {
|
||||||
.attribute_equality("memberof", &PartialValue::new_refer(group)),
|
.attribute_equality("memberof", &PartialValue::new_refer(group)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_oauth2_consent_scopes(&self, oauth2_rs: Uuid) -> Option<&BTreeSet<String>> {
|
||||||
|
match &self.origin {
|
||||||
|
IdentType::Internal => None,
|
||||||
|
IdentType::User(u) => u
|
||||||
|
.entry
|
||||||
|
.get_ava_as_oauthscopemaps("oauth2_consent_scope_map")
|
||||||
|
.and_then(|scope_map| scope_map.get(&oauth2_rs)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ pub(crate) enum DelayedAction {
|
||||||
UnixPwUpgrade(UnixPasswordUpgrade),
|
UnixPwUpgrade(UnixPasswordUpgrade),
|
||||||
WebauthnCounterIncrement(WebauthnCounterIncrement),
|
WebauthnCounterIncrement(WebauthnCounterIncrement),
|
||||||
BackupCodeRemoval(BackupCodeRemoval),
|
BackupCodeRemoval(BackupCodeRemoval),
|
||||||
|
Oauth2ConsentGrant(Oauth2ConsentGrant),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct PasswordUpgrade {
|
pub(crate) struct PasswordUpgrade {
|
||||||
|
@ -28,3 +29,9 @@ pub(crate) struct BackupCodeRemoval {
|
||||||
pub target_uuid: Uuid,
|
pub target_uuid: Uuid,
|
||||||
pub code_to_remove: String,
|
pub code_to_remove: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct Oauth2ConsentGrant {
|
||||||
|
pub target_uuid: Uuid,
|
||||||
|
pub oauth2_rs_uuid: Uuid,
|
||||||
|
pub scopes: Vec<String>,
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -17,8 +17,8 @@ use crate::idm::event::{
|
||||||
use crate::idm::mfareg::{MfaRegCred, MfaRegNext, MfaRegSession};
|
use crate::idm::mfareg::{MfaRegCred, MfaRegNext, MfaRegSession};
|
||||||
use crate::idm::oauth2::{
|
use crate::idm::oauth2::{
|
||||||
AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AccessTokenRequest,
|
AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AccessTokenRequest,
|
||||||
AccessTokenResponse, AuthorisationRequest, AuthorisePermitSuccess, ConsentRequest, JwkKeySet,
|
AccessTokenResponse, AuthorisationRequest, AuthorisePermitSuccess, AuthoriseResponse,
|
||||||
Oauth2Error, Oauth2ResourceServers, Oauth2ResourceServersReadTransaction,
|
JwkKeySet, Oauth2Error, Oauth2ResourceServers, Oauth2ResourceServersReadTransaction,
|
||||||
Oauth2ResourceServersWriteTransaction, OidcDiscoveryResponse, OidcToken,
|
Oauth2ResourceServersWriteTransaction, OidcDiscoveryResponse, OidcToken,
|
||||||
};
|
};
|
||||||
use crate::idm::radius::RadiusAccount;
|
use crate::idm::radius::RadiusAccount;
|
||||||
|
@ -34,7 +34,8 @@ use crate::utils::{
|
||||||
|
|
||||||
use crate::actors::v1_write::QueryServerWriteV1;
|
use crate::actors::v1_write::QueryServerWriteV1;
|
||||||
use crate::idm::delayed::{
|
use crate::idm::delayed::{
|
||||||
DelayedAction, PasswordUpgrade, UnixPasswordUpgrade, WebauthnCounterIncrement,
|
DelayedAction, Oauth2ConsentGrant, PasswordUpgrade, UnixPasswordUpgrade,
|
||||||
|
WebauthnCounterIncrement,
|
||||||
};
|
};
|
||||||
|
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
|
@ -138,6 +139,7 @@ pub struct IdmServerProxyReadTransaction<'a> {
|
||||||
pub qs_read: QueryServerReadTransaction<'a>,
|
pub qs_read: QueryServerReadTransaction<'a>,
|
||||||
uat_jwt_validator: CowCellReadTxn<JwsValidator>,
|
uat_jwt_validator: CowCellReadTxn<JwsValidator>,
|
||||||
oauth2rs: Oauth2ResourceServersReadTransaction,
|
oauth2rs: Oauth2ResourceServersReadTransaction,
|
||||||
|
async_tx: Sender<DelayedAction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IdmServerProxyWriteTransaction<'a> {
|
pub struct IdmServerProxyWriteTransaction<'a> {
|
||||||
|
@ -158,7 +160,7 @@ pub struct IdmServerProxyWriteTransaction<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IdmServerDelayed {
|
pub struct IdmServerDelayed {
|
||||||
async_rx: Receiver<DelayedAction>,
|
pub(crate) async_rx: Receiver<DelayedAction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdmServer {
|
impl IdmServer {
|
||||||
|
@ -307,6 +309,7 @@ impl IdmServer {
|
||||||
qs_read: self.qs.read_async().await,
|
qs_read: self.qs.read_async().await,
|
||||||
uat_jwt_validator: self.uat_jwt_validator.read(),
|
uat_jwt_validator: self.uat_jwt_validator.read(),
|
||||||
oauth2rs: self.oauth2rs.read(),
|
oauth2rs: self.oauth2rs.read(),
|
||||||
|
async_tx: self.async_tx.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1177,7 +1180,7 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
|
||||||
uat: &UserAuthToken,
|
uat: &UserAuthToken,
|
||||||
auth_req: &AuthorisationRequest,
|
auth_req: &AuthorisationRequest,
|
||||||
ct: Duration,
|
ct: Duration,
|
||||||
) -> Result<ConsentRequest, Oauth2Error> {
|
) -> Result<AuthoriseResponse, Oauth2Error> {
|
||||||
self.oauth2rs
|
self.oauth2rs
|
||||||
.check_oauth2_authorisation(ident, uat, auth_req, ct)
|
.check_oauth2_authorisation(ident, uat, auth_req, ct)
|
||||||
}
|
}
|
||||||
|
@ -1190,7 +1193,7 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
|
||||||
ct: Duration,
|
ct: Duration,
|
||||||
) -> Result<AuthorisePermitSuccess, OperationError> {
|
) -> Result<AuthorisePermitSuccess, OperationError> {
|
||||||
self.oauth2rs
|
self.oauth2rs
|
||||||
.check_oauth2_authorise_permit(ident, uat, consent_req, ct)
|
.check_oauth2_authorise_permit(ident, uat, consent_req, ct, &self.async_tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_oauth2_authorise_reject(
|
pub fn check_oauth2_authorise_reject(
|
||||||
|
@ -2044,6 +2047,27 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn process_oauth2consentgrant(
|
||||||
|
&mut self,
|
||||||
|
o2cg: &Oauth2ConsentGrant,
|
||||||
|
) -> Result<(), OperationError> {
|
||||||
|
let modlist = ModifyList::new_list(vec![
|
||||||
|
Modify::Removed(
|
||||||
|
AttrString::from("oauth2_consent_scope_map"),
|
||||||
|
PartialValue::OauthScopeMap(o2cg.oauth2_rs_uuid),
|
||||||
|
),
|
||||||
|
Modify::Present(
|
||||||
|
AttrString::from("oauth2_consent_scope_map"),
|
||||||
|
Value::OauthScopeMap(o2cg.oauth2_rs_uuid, o2cg.scopes.iter().cloned().collect()),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
self.qs_write.internal_modify(
|
||||||
|
&filter_all!(f_eq("uuid", PartialValue::new_uuid(o2cg.target_uuid))),
|
||||||
|
&modlist,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn process_delayedaction(
|
pub(crate) fn process_delayedaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
da: DelayedAction,
|
da: DelayedAction,
|
||||||
|
@ -2053,6 +2077,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
||||||
DelayedAction::UnixPwUpgrade(upwu) => self.process_unixpwupgrade(&upwu),
|
DelayedAction::UnixPwUpgrade(upwu) => self.process_unixpwupgrade(&upwu),
|
||||||
DelayedAction::WebauthnCounterIncrement(wci) => self.process_webauthncounterinc(&wci),
|
DelayedAction::WebauthnCounterIncrement(wci) => self.process_webauthncounterinc(&wci),
|
||||||
DelayedAction::BackupCodeRemoval(bcr) => self.process_backupcoderemoval(&bcr),
|
DelayedAction::BackupCodeRemoval(bcr) => self.process_backupcoderemoval(&bcr),
|
||||||
|
DelayedAction::Oauth2ConsentGrant(o2cg) => self.process_oauth2consentgrant(&o2cg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2318,6 +2318,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
||||||
JSON_SCHEMA_ATTR_OAUTH2_JWT_LEGACY_CRYPTO_ENABLE,
|
JSON_SCHEMA_ATTR_OAUTH2_JWT_LEGACY_CRYPTO_ENABLE,
|
||||||
JSON_SCHEMA_ATTR_RS256_PRIVATE_KEY_DER,
|
JSON_SCHEMA_ATTR_RS256_PRIVATE_KEY_DER,
|
||||||
JSON_SCHEMA_ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN,
|
JSON_SCHEMA_ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN,
|
||||||
|
JSON_SCHEMA_ATTR_OAUTH2_CONSENT_SCOPE_MAP,
|
||||||
JSON_SCHEMA_CLASS_PERSON,
|
JSON_SCHEMA_CLASS_PERSON,
|
||||||
JSON_SCHEMA_CLASS_ORGPERSON,
|
JSON_SCHEMA_CLASS_ORGPERSON,
|
||||||
JSON_SCHEMA_CLASS_GROUP,
|
JSON_SCHEMA_CLASS_GROUP,
|
||||||
|
|
|
@ -2,9 +2,10 @@ use super::v1::{json_rest_event_get, json_rest_event_post};
|
||||||
use super::{to_tide_response, AppState, RequestExtensions};
|
use super::{to_tide_response, AppState, RequestExtensions};
|
||||||
use kanidm::idm::oauth2::{
|
use kanidm::idm::oauth2::{
|
||||||
AccessTokenIntrospectRequest, AccessTokenRequest, AuthorisationRequest, AuthorisePermitSuccess,
|
AccessTokenIntrospectRequest, AccessTokenRequest, AuthorisationRequest, AuthorisePermitSuccess,
|
||||||
ErrorResponse, Oauth2Error,
|
AuthoriseResponse, ErrorResponse, Oauth2Error,
|
||||||
};
|
};
|
||||||
use kanidm::prelude::*;
|
use kanidm::prelude::*;
|
||||||
|
use kanidm_proto::oauth2::AuthorisationResponse;
|
||||||
use kanidm_proto::v1::Entry as ProtoEntry;
|
use kanidm_proto::v1::Entry as ProtoEntry;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -185,7 +186,13 @@ pub async fn oauth2_id_delete(req: tide::Request<AppState>) -> tide::Result {
|
||||||
|
|
||||||
pub async fn oauth2_authorise_post(mut req: tide::Request<AppState>) -> tide::Result {
|
pub async fn oauth2_authorise_post(mut req: tide::Request<AppState>) -> tide::Result {
|
||||||
let auth_req: AuthorisationRequest = req.body_json().await?;
|
let auth_req: AuthorisationRequest = req.body_json().await?;
|
||||||
oauth2_authorise(req, auth_req).await
|
oauth2_authorise(req, auth_req).await.map(|mut res| {
|
||||||
|
if res.status() == 302 {
|
||||||
|
// in post, we need the redirect not to be issued, so we mask 302 to 200
|
||||||
|
res.set_status(200);
|
||||||
|
}
|
||||||
|
res
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn oauth2_authorise_get(req: tide::Request<AppState>) -> tide::Result {
|
pub async fn oauth2_authorise_get(req: tide::Request<AppState>) -> tide::Result {
|
||||||
|
@ -219,12 +226,43 @@ async fn oauth2_authorise(
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(consent_req) => {
|
Ok(AuthoriseResponse::ConsentRequested {
|
||||||
|
client_name,
|
||||||
|
scopes,
|
||||||
|
pii_scopes,
|
||||||
|
consent_token,
|
||||||
|
}) => {
|
||||||
// Render a redirect to the consent page for the user to interact with
|
// Render a redirect to the consent page for the user to interact with
|
||||||
// to authorise this session-id
|
// to authorise this session-id
|
||||||
let mut res = tide::Response::new(200);
|
let mut res = tide::Response::new(200);
|
||||||
// This is json so later we can expand it with better detail.
|
// This is json so later we can expand it with better detail.
|
||||||
tide::Body::from_json(&consent_req).map(|b| {
|
tide::Body::from_json(&AuthorisationResponse::ConsentRequested {
|
||||||
|
client_name,
|
||||||
|
scopes,
|
||||||
|
pii_scopes,
|
||||||
|
consent_token,
|
||||||
|
})
|
||||||
|
.map(|b| {
|
||||||
|
res.set_body(b);
|
||||||
|
res
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Ok(AuthoriseResponse::Permitted(AuthorisePermitSuccess {
|
||||||
|
mut redirect_uri,
|
||||||
|
state,
|
||||||
|
code,
|
||||||
|
})) => {
|
||||||
|
let mut res = tide::Response::new(302);
|
||||||
|
|
||||||
|
redirect_uri
|
||||||
|
.query_pairs_mut()
|
||||||
|
.clear()
|
||||||
|
.append_pair("state", &state)
|
||||||
|
.append_pair("code", &code);
|
||||||
|
res.insert_header("Location", redirect_uri.as_str());
|
||||||
|
// I think the client server needs this
|
||||||
|
res.insert_header("Access-Control-Allow-Origin", redirect_uri.origin().ascii_serialization());
|
||||||
|
tide::Body::from_json(&AuthorisationResponse::Permitted).map(|b| {
|
||||||
res.set_body(b);
|
res.set_body(b);
|
||||||
res
|
res
|
||||||
})
|
})
|
||||||
|
@ -265,8 +303,10 @@ pub async fn oauth2_authorise_permit_post(mut req: tide::Request<AppState>) -> t
|
||||||
oauth2_authorise_permit(req, consent_req)
|
oauth2_authorise_permit(req, consent_req)
|
||||||
.await
|
.await
|
||||||
.map(|mut res| {
|
.map(|mut res| {
|
||||||
// in post, we need the redirect not to be issued, so we mask 302 to 200
|
if res.status() == 302 {
|
||||||
res.set_status(200);
|
// in post, we need the redirect not to be issued, so we mask 302 to 200
|
||||||
|
res.set_status(200);
|
||||||
|
}
|
||||||
res
|
res
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -318,7 +358,7 @@ async fn oauth2_authorise_permit(
|
||||||
.append_pair("code", &code);
|
.append_pair("code", &code);
|
||||||
res.insert_header("Location", redirect_uri.as_str());
|
res.insert_header("Location", redirect_uri.as_str());
|
||||||
// I think the client server needs this
|
// I think the client server needs this
|
||||||
// res.insert_header("Access-Control-Allow-Origin", redirect_uri.origin().ascii_serialization());
|
res.insert_header("Access-Control-Allow-Origin", redirect_uri.origin().ascii_serialization());
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::common::{setup_async_test, ADMIN_TEST_PASSWORD};
|
||||||
use compact_jwt::{JwkKeySet, JwsValidator, OidcToken, OidcUnverified};
|
use compact_jwt::{JwkKeySet, JwsValidator, OidcToken, OidcUnverified};
|
||||||
use kanidm_proto::oauth2::{
|
use kanidm_proto::oauth2::{
|
||||||
AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AccessTokenRequest,
|
AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AccessTokenRequest,
|
||||||
AccessTokenResponse, ConsentRequest, OidcDiscoveryResponse,
|
AccessTokenResponse, AuthorisationResponse, OidcDiscoveryResponse,
|
||||||
};
|
};
|
||||||
use oauth2_ext::PkceCodeChallenge;
|
use oauth2_ext::PkceCodeChallenge;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -217,18 +217,25 @@ async fn test_oauth2_openid_basic_flow() {
|
||||||
assert!(response.status() == reqwest::StatusCode::OK);
|
assert!(response.status() == reqwest::StatusCode::OK);
|
||||||
assert_no_cache!(response);
|
assert_no_cache!(response);
|
||||||
|
|
||||||
let consent_req: ConsentRequest = response
|
let consent_req: AuthorisationResponse = response
|
||||||
.json()
|
.json()
|
||||||
.await
|
.await
|
||||||
.expect("Failed to access response body");
|
.expect("Failed to access response body");
|
||||||
|
|
||||||
|
let consent_token =
|
||||||
|
if let AuthorisationResponse::ConsentRequested { consent_token, .. } = consent_req {
|
||||||
|
consent_token
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
|
||||||
// Step 2 - we now send the consent get to the server which yields a redirect with a
|
// Step 2 - we now send the consent get to the server which yields a redirect with a
|
||||||
// state and code.
|
// state and code.
|
||||||
|
|
||||||
let response = client
|
let response = client
|
||||||
.get(format!("{}/oauth2/authorise/permit", url))
|
.get(format!("{}/oauth2/authorise/permit", url))
|
||||||
.bearer_auth(admin_uat)
|
.bearer_auth(admin_uat)
|
||||||
.query(&[("token", consent_req.consent_token.as_str())])
|
.query(&[("token", consent_token.as_str())])
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.expect("Failed to send request.");
|
.expect("Failed to send request.");
|
||||||
|
|
|
@ -24,7 +24,7 @@ If you want to deploy Kanidm to see what it can do, you should read the kanidm b
|
||||||
- [Kanidm book (Latest stable)](https://kanidm.github.io/kanidm/stable/)
|
- [Kanidm book (Latest stable)](https://kanidm.github.io/kanidm/stable/)
|
||||||
|
|
||||||
|
|
||||||
We also publish limited [support guidelines](https://github.com/kanidm/kanidm/blob/master/project_docs/RELEASE_AND_SUPPORT.md)).
|
We also publish limited [support guidelines](https://github.com/kanidm/kanidm/blob/master/project_docs/RELEASE_AND_SUPPORT.md).
|
||||||
|
|
||||||
## Code of Conduct / Ethics
|
## Code of Conduct / Ethics
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
||||||
<text y=".9em" font-size="90">🦀</text>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 111 B |
6
kanidmd_web_ui/pkg/kanidmd_web_ui.d.ts
vendored
6
kanidmd_web_ui/pkg/kanidmd_web_ui.d.ts
vendored
|
@ -12,9 +12,9 @@ export interface InitOutput {
|
||||||
readonly __wbindgen_malloc: (a: number) => number;
|
readonly __wbindgen_malloc: (a: number) => number;
|
||||||
readonly __wbindgen_realloc: (a: number, b: number, c: number) => number;
|
readonly __wbindgen_realloc: (a: number, b: number, c: number) => number;
|
||||||
readonly __wbindgen_export_2: WebAssembly.Table;
|
readonly __wbindgen_export_2: WebAssembly.Table;
|
||||||
readonly _dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha6256632cf9b6e15: (a: number, b: number, c: number) => void;
|
readonly _dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h517d7fce3d158796: (a: number, b: number, c: number) => void;
|
||||||
readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h026107843485189d: (a: number, b: number, c: number) => void;
|
readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h6c41bce435f08bdb: (a: number, b: number, c: number) => void;
|
||||||
readonly _dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hf1579e791c670fad: (a: number, b: number, c: number) => void;
|
readonly _dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hea19f293916f5b3a: (a: number, b: number, c: number) => void;
|
||||||
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
|
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
|
||||||
readonly __wbindgen_free: (a: number, b: number) => void;
|
readonly __wbindgen_free: (a: number, b: number) => void;
|
||||||
readonly __wbindgen_exn_store: (a: number) => void;
|
readonly __wbindgen_exn_store: (a: number) => void;
|
||||||
|
|
|
@ -241,7 +241,7 @@ function logError(f, args) {
|
||||||
function __wbg_adapter_30(arg0, arg1, arg2) {
|
function __wbg_adapter_30(arg0, arg1, arg2) {
|
||||||
_assertNum(arg0);
|
_assertNum(arg0);
|
||||||
_assertNum(arg1);
|
_assertNum(arg1);
|
||||||
wasm._dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha6256632cf9b6e15(arg0, arg1, addHeapObject(arg2));
|
wasm._dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h517d7fce3d158796(arg0, arg1, addHeapObject(arg2));
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeMutClosure(arg0, arg1, dtor, f) {
|
function makeMutClosure(arg0, arg1, dtor, f) {
|
||||||
|
@ -271,7 +271,7 @@ function makeMutClosure(arg0, arg1, dtor, f) {
|
||||||
function __wbg_adapter_33(arg0, arg1, arg2) {
|
function __wbg_adapter_33(arg0, arg1, arg2) {
|
||||||
_assertNum(arg0);
|
_assertNum(arg0);
|
||||||
_assertNum(arg1);
|
_assertNum(arg1);
|
||||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h026107843485189d(arg0, arg1, addHeapObject(arg2));
|
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h6c41bce435f08bdb(arg0, arg1, addHeapObject(arg2));
|
||||||
}
|
}
|
||||||
|
|
||||||
let stack_pointer = 32;
|
let stack_pointer = 32;
|
||||||
|
@ -285,7 +285,7 @@ function __wbg_adapter_36(arg0, arg1, arg2) {
|
||||||
try {
|
try {
|
||||||
_assertNum(arg0);
|
_assertNum(arg0);
|
||||||
_assertNum(arg1);
|
_assertNum(arg1);
|
||||||
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hf1579e791c670fad(arg0, arg1, addBorrowedObject(arg2));
|
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hea19f293916f5b3a(arg0, arg1, addBorrowedObject(arg2));
|
||||||
} finally {
|
} finally {
|
||||||
heap[stack_pointer++] = undefined;
|
heap[stack_pointer++] = undefined;
|
||||||
}
|
}
|
||||||
|
@ -425,6 +425,9 @@ async function init(input) {
|
||||||
const ret = arg0;
|
const ret = arg0;
|
||||||
return addHeapObject(ret);
|
return addHeapObject(ret);
|
||||||
};
|
};
|
||||||
|
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
|
||||||
|
takeObject(arg0);
|
||||||
|
};
|
||||||
imports.wbg.__wbg_error_09919627ac0992f5 = function() { return logError(function (arg0, arg1) {
|
imports.wbg.__wbg_error_09919627ac0992f5 = function() { return logError(function (arg0, arg1) {
|
||||||
try {
|
try {
|
||||||
console.error(getStringFromWasm0(arg0, arg1));
|
console.error(getStringFromWasm0(arg0, arg1));
|
||||||
|
@ -443,9 +446,6 @@ async function init(input) {
|
||||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||||
}, arguments) };
|
}, arguments) };
|
||||||
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
|
|
||||||
takeObject(arg0);
|
|
||||||
};
|
|
||||||
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
||||||
const obj = takeObject(arg0).original;
|
const obj = takeObject(arg0).original;
|
||||||
if (obj.cnt-- == 1) {
|
if (obj.cnt-- == 1) {
|
||||||
|
@ -869,16 +869,16 @@ async function init(input) {
|
||||||
const ret = wasm.memory;
|
const ret = wasm.memory;
|
||||||
return addHeapObject(ret);
|
return addHeapObject(ret);
|
||||||
};
|
};
|
||||||
imports.wbg.__wbindgen_closure_wrapper19774 = function() { return logError(function (arg0, arg1, arg2) {
|
imports.wbg.__wbindgen_closure_wrapper19806 = function() { return logError(function (arg0, arg1, arg2) {
|
||||||
const ret = makeClosure(arg0, arg1, 1253, __wbg_adapter_30);
|
const ret = makeClosure(arg0, arg1, 1261, __wbg_adapter_30);
|
||||||
return addHeapObject(ret);
|
return addHeapObject(ret);
|
||||||
}, arguments) };
|
}, arguments) };
|
||||||
imports.wbg.__wbindgen_closure_wrapper21955 = function() { return logError(function (arg0, arg1, arg2) {
|
imports.wbg.__wbindgen_closure_wrapper21982 = function() { return logError(function (arg0, arg1, arg2) {
|
||||||
const ret = makeMutClosure(arg0, arg1, 1273, __wbg_adapter_33);
|
const ret = makeMutClosure(arg0, arg1, 1281, __wbg_adapter_33);
|
||||||
return addHeapObject(ret);
|
return addHeapObject(ret);
|
||||||
}, arguments) };
|
}, arguments) };
|
||||||
imports.wbg.__wbindgen_closure_wrapper22397 = function() { return logError(function (arg0, arg1, arg2) {
|
imports.wbg.__wbindgen_closure_wrapper22336 = function() { return logError(function (arg0, arg1, arg2) {
|
||||||
const ret = makeMutClosure(arg0, arg1, 1300, __wbg_adapter_36);
|
const ret = makeMutClosure(arg0, arg1, 1307, __wbg_adapter_36);
|
||||||
return addHeapObject(ret);
|
return addHeapObject(ret);
|
||||||
}, arguments) };
|
}, arguments) };
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -5,9 +5,9 @@ export function run_app(a: number): void;
|
||||||
export function __wbindgen_malloc(a: number): number;
|
export function __wbindgen_malloc(a: number): number;
|
||||||
export function __wbindgen_realloc(a: number, b: number, c: number): number;
|
export function __wbindgen_realloc(a: number, b: number, c: number): number;
|
||||||
export const __wbindgen_export_2: WebAssembly.Table;
|
export const __wbindgen_export_2: WebAssembly.Table;
|
||||||
export function _dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha6256632cf9b6e15(a: number, b: number, c: number): void;
|
export function _dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h517d7fce3d158796(a: number, b: number, c: number): void;
|
||||||
export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h026107843485189d(a: number, b: number, c: number): void;
|
export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h6c41bce435f08bdb(a: number, b: number, c: number): void;
|
||||||
export function _dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hf1579e791c670fad(a: number, b: number, c: number): void;
|
export function _dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hea19f293916f5b3a(a: number, b: number, c: number): void;
|
||||||
export function __wbindgen_add_to_stack_pointer(a: number): number;
|
export function __wbindgen_add_to_stack_pointer(a: number): number;
|
||||||
export function __wbindgen_free(a: number, b: number): void;
|
export function __wbindgen_free(a: number, b: number): void;
|
||||||
export function __wbindgen_exn_store(a: number): void;
|
export function __wbindgen_exn_store(a: number): void;
|
||||||
|
|
|
@ -14,8 +14,8 @@ use crate::models;
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
|
|
||||||
pub use kanidm_proto::oauth2::{
|
pub use kanidm_proto::oauth2::{
|
||||||
AccessTokenRequest, AccessTokenResponse, AuthorisationRequest, CodeChallengeMethod,
|
AccessTokenRequest, AccessTokenResponse, AuthorisationRequest, AuthorisationResponse,
|
||||||
ConsentRequest, ErrorResponse,
|
CodeChallengeMethod, ErrorResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
|
@ -25,7 +25,13 @@ enum State {
|
||||||
TokenCheck(String),
|
TokenCheck(String),
|
||||||
// Token check done, lets do it.
|
// Token check done, lets do it.
|
||||||
SubmitAuthReq(String),
|
SubmitAuthReq(String),
|
||||||
Consent(String, ConsentRequest),
|
Consent {
|
||||||
|
token: String,
|
||||||
|
client_name: String,
|
||||||
|
scopes: Vec<String>,
|
||||||
|
pii_scopes: Vec<String>,
|
||||||
|
consent_token: String,
|
||||||
|
},
|
||||||
ConsentGranted,
|
ConsentGranted,
|
||||||
ErrInvalidRequest,
|
ErrInvalidRequest,
|
||||||
}
|
}
|
||||||
|
@ -38,9 +44,17 @@ pub enum Oauth2Msg {
|
||||||
LoginProceed,
|
LoginProceed,
|
||||||
ConsentGranted,
|
ConsentGranted,
|
||||||
TokenValid,
|
TokenValid,
|
||||||
Consent(ConsentRequest),
|
Consent {
|
||||||
|
client_name: String,
|
||||||
|
scopes: Vec<String>,
|
||||||
|
pii_scopes: Vec<String>,
|
||||||
|
consent_token: String,
|
||||||
|
},
|
||||||
Redirect(String),
|
Redirect(String),
|
||||||
Error { emsg: String, kopid: Option<String> },
|
Error {
|
||||||
|
emsg: String,
|
||||||
|
kopid: Option<String>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<FetchError> for Oauth2Msg {
|
impl From<FetchError> for Oauth2Msg {
|
||||||
|
@ -116,13 +130,41 @@ impl Oauth2App {
|
||||||
let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type");
|
let resp: Response = resp_value.dyn_into().expect_throw("Invalid response type");
|
||||||
let status = resp.status();
|
let status = resp.status();
|
||||||
let headers = resp.headers();
|
let headers = resp.headers();
|
||||||
|
let kopid = headers.get("x-kanidm-opid").ok().flatten();
|
||||||
|
|
||||||
if status == 200 {
|
if status == 200 {
|
||||||
let jsval = JsFuture::from(resp.json()?).await?;
|
let jsval = JsFuture::from(resp.json()?).await?;
|
||||||
let state: ConsentRequest = jsval.into_serde().expect_throw("Invalid response type");
|
let state: AuthorisationResponse = jsval
|
||||||
Ok(Oauth2Msg::Consent(state))
|
.into_serde()
|
||||||
|
.map_err(|e| {
|
||||||
|
let e_msg = format!("serde error -> {:?}", e);
|
||||||
|
console::log!(e_msg.as_str());
|
||||||
|
})
|
||||||
|
.expect_throw("Invalid response type");
|
||||||
|
match state {
|
||||||
|
AuthorisationResponse::ConsentRequested {
|
||||||
|
client_name,
|
||||||
|
scopes,
|
||||||
|
pii_scopes,
|
||||||
|
consent_token,
|
||||||
|
} => Ok(Oauth2Msg::Consent {
|
||||||
|
client_name,
|
||||||
|
scopes,
|
||||||
|
pii_scopes,
|
||||||
|
consent_token,
|
||||||
|
}),
|
||||||
|
AuthorisationResponse::Permitted => {
|
||||||
|
if let Some(loc) = headers.get("location").ok().flatten() {
|
||||||
|
Ok(Oauth2Msg::Redirect(loc))
|
||||||
|
} else {
|
||||||
|
Ok(Oauth2Msg::Error {
|
||||||
|
emsg: "no location header".to_string(),
|
||||||
|
kopid,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let kopid = headers.get("x-kanidm-opid").ok().flatten();
|
|
||||||
let text = JsFuture::from(resp.text()?).await?;
|
let text = JsFuture::from(resp.text()?).await?;
|
||||||
let emsg = text.as_string().unwrap_or_else(|| "".to_string());
|
let emsg = text.as_string().unwrap_or_else(|| "".to_string());
|
||||||
Ok(Oauth2Msg::Error { emsg, kopid })
|
Ok(Oauth2Msg::Error { emsg, kopid })
|
||||||
|
@ -131,9 +173,9 @@ impl Oauth2App {
|
||||||
|
|
||||||
async fn fetch_consent_token(
|
async fn fetch_consent_token(
|
||||||
token: String,
|
token: String,
|
||||||
consent_req: ConsentRequest,
|
consent_token: String,
|
||||||
) -> Result<Oauth2Msg, FetchError> {
|
) -> Result<Oauth2Msg, FetchError> {
|
||||||
let consentreq_jsvalue = serde_json::to_string(&consent_req.consent_token)
|
let consentreq_jsvalue = serde_json::to_string(&consent_token)
|
||||||
.map(|s| JsValue::from(&s))
|
.map(|s| JsValue::from(&s))
|
||||||
.expect_throw("Failed to serialise consent_req");
|
.expect_throw("Failed to serialise consent_req");
|
||||||
|
|
||||||
|
@ -294,9 +336,20 @@ impl Component for Oauth2App {
|
||||||
};
|
};
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Oauth2Msg::Consent(consent_req) => {
|
Oauth2Msg::Consent {
|
||||||
|
client_name,
|
||||||
|
scopes,
|
||||||
|
pii_scopes,
|
||||||
|
consent_token,
|
||||||
|
} => {
|
||||||
self.state = match &self.state {
|
self.state = match &self.state {
|
||||||
State::SubmitAuthReq(token) => State::Consent(token.clone(), consent_req),
|
State::SubmitAuthReq(token) => State::Consent {
|
||||||
|
token: token.clone(),
|
||||||
|
client_name,
|
||||||
|
scopes,
|
||||||
|
pii_scopes,
|
||||||
|
consent_token,
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
console::log!("Invalid state transition");
|
console::log!("Invalid state transition");
|
||||||
State::ErrInvalidRequest
|
State::ErrInvalidRequest
|
||||||
|
@ -306,9 +359,13 @@ impl Component for Oauth2App {
|
||||||
}
|
}
|
||||||
Oauth2Msg::ConsentGranted => {
|
Oauth2Msg::ConsentGranted => {
|
||||||
self.state = match &self.state {
|
self.state = match &self.state {
|
||||||
State::Consent(token, consent_req) => {
|
State::Consent {
|
||||||
|
token,
|
||||||
|
consent_token,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let token_c = token.clone();
|
let token_c = token.clone();
|
||||||
let cr_c = (*consent_req).clone();
|
let cr_c = consent_token.clone();
|
||||||
ctx.link().send_future(async {
|
ctx.link().send_future(async {
|
||||||
match Self::fetch_consent_token(token_c, cr_c).await {
|
match Self::fetch_consent_token(token_c, cr_c).await {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
|
@ -380,8 +437,36 @@ impl Component for Oauth2App {
|
||||||
</main>
|
</main>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
State::Consent(_, query) => {
|
State::Consent {
|
||||||
let client_name = query.client_name.clone();
|
token: _,
|
||||||
|
client_name,
|
||||||
|
scopes: _,
|
||||||
|
pii_scopes,
|
||||||
|
consent_token: _,
|
||||||
|
} => {
|
||||||
|
let client_name = client_name.clone();
|
||||||
|
|
||||||
|
let pii_req = if pii_scopes.is_empty() {
|
||||||
|
html! {
|
||||||
|
<div>
|
||||||
|
<p>{ "This site will not have access to your personal information" }</p>
|
||||||
|
<p>{ "If this site requests personal information in the future we will check with you" }</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html! {
|
||||||
|
<div>
|
||||||
|
<p>{ "This site has requested to see the following personal information" }</p>
|
||||||
|
<ul>
|
||||||
|
{
|
||||||
|
pii_scopes.iter().map(|s| html! { <li>{ s }</li> } ).collect::<Html>()
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
<p>{ "If this site requests different personal information in the future we will check with you again" }</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// <body class="html-body form-body">
|
// <body class="html-body form-body">
|
||||||
html! {
|
html! {
|
||||||
<main class="form-signin">
|
<main class="form-signin">
|
||||||
|
@ -393,7 +478,9 @@ impl Component for Oauth2App {
|
||||||
} ) }
|
} ) }
|
||||||
action="javascript:void(0);"
|
action="javascript:void(0);"
|
||||||
>
|
>
|
||||||
<h1 class="h3 mb-3 fw-normal">{"Consent to Proceed to " }{ client_name }</h1>
|
<h2 class="h3 mb-3 fw-normal">{"Consent to Proceed to " }{ client_name }</h2>
|
||||||
|
{ pii_req }
|
||||||
|
|
||||||
<button id="autofocus" class="w-100 btn btn-lg btn-primary" type="submit">{ "Proceed" }</button>
|
<button id="autofocus" class="w-100 btn btn-lg btn-primary" type="submit">{ "Proceed" }</button>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
|
|
Loading…
Reference in a new issue