1
0
Fork 0
mirror of https://gitlab.com/famedly/conduit.git synced 2025-07-02 16:38:36 +00:00
This commit is contained in:
avdb13 2024-07-11 22:24:22 +02:00
parent 10ce7ea3a9
commit b80141b33b
8 changed files with 255 additions and 599 deletions

View file

@ -1,50 +1,36 @@
mod data;
use std::{
borrow::Borrow,
collections::{HashMap, HashSet},
collections::HashSet,
hash::{Hash, Hasher},
str::FromStr,
sync::{Arc, RwLock},
time::{Duration, SystemTime, UNIX_EPOCH},
sync::Arc,
};
use crate::{
api::client_server::TOKEN_LENGTH,
config::{sso::ProviderConfig as Config, IdpConfig},
api::client_server::{LOGIN_TOKEN_EXPIRATION_SECS, TOKEN_LENGTH},
config::IdpConfig,
utils, Error, Result,
};
pub use data::Data;
use email_address::EmailAddress;
use futures_util::future::{self};
use mas_oidc_client::{
http_service::{hyper, HttpService},
jose::jwk::PublicJsonWebKeySet,
requests::{
authorization_code::{self, AuthorizationRequestData, AuthorizationValidationData},
discovery,
jose::{self, JwtVerificationData},
userinfo,
},
types::{
iana::jose::JsonWebSignatureAlg, oidc::VerifiedProviderMetadata,
requests::AccessTokenResponse, IdToken,
},
requests::{authorization_code::AuthorizationValidationData, discovery},
types::oidc::VerifiedProviderMetadata,
};
use rand::SeedableRng;
use ruma::{api::client::error::ErrorKind, MilliSecondsSinceUnixEpoch, OwnedUserId, UserId};
use ruma::{api::client::session::get_login_types::v3::IdentityProvider, OwnedUserId, UserId};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tokio::sync::{oneshot, OnceCell};
use tokio::sync::OnceCell;
use tracing::error;
use url::Url;
use crate::services;
mod data;
pub use data::Data;
pub const SSO_AUTH_EXPIRATION_SECS: u64 = 60 * 60;
pub const SSO_TOKEN_EXPIRATION_SECS: u64 = 60 * 2;
pub const SSO_SESSION_COOKIE: &str = "sso-auth";
pub const SUBJECT_CLAIM_KEY: &str = "sub";
pub struct Service {
db: &'static dyn Data,
@ -69,7 +55,7 @@ impl Service {
let providers = services().globals.config.idps.iter();
self.providers
.get_or_try_init(|| {
.get_or_try_init(|| async move {
future::try_join_all(providers.map(Provider::fetch_metadata))
.await
.map(Vec::into_iter)
@ -86,6 +72,12 @@ impl Service {
providers.get(provider)
}
pub fn login_type(&self) -> impl Iterator<Item = IdentityProvider> + '_ {
let providers = self.providers.get().expect("");
providers.iter().map(|p| p.config.inner.clone())
}
pub fn user_from_subject(&self, provider: &str, subject: &str) -> Result<Option<OwnedUserId>> {
self.db.user_from_subject(provider, subject)
}
@ -111,30 +103,6 @@ impl Provider {
Error::bad_config("Failed to fetch identity provider metadata.")
})
}
async fn fetch_signing_keys(&self) -> Result<PublicJsonWebKeySet> {
jose::fetch_jwks(&services().sso.service, self.metadata.jwks_uri())
.await
.map_err(|e| {
error!("Failed to fetch signing keys for token endpoint: {}", e);
Error::bad_config("Failed to fetch signing keys for token endpoint.")
})
}
pub async fn fetch_access_token(
&self,
auth_code: String,
validation_data: AuthorizationValidationData,
) -> Result<(AccessTokenResponse, Option<IdToken<'_>>)> {
}
pub async fn fetch_userinfo(
&self,
access_token: &str,
id_token: &IdToken<'_>,
) -> Result<Option<HashMap<String, Value>>> {
}
}
impl Borrow<str> for Provider {
@ -157,105 +125,28 @@ impl Hash for Provider {
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct RegistrationToken {
pub info: RegistrationInfo,
pub provider_id: String,
pub unique_claim: String,
pub redirect_uri: Url,
pub expires_at: MilliSecondsSinceUnixEpoch,
}
impl RegistrationToken {
pub fn new(
provider_id: String,
unique_claim: String,
redirect_uri: Url,
info: RegistrationInfo,
) -> Self {
let expires_at = MilliSecondsSinceUnixEpoch::from_system_time(
UNIX_EPOCH
.checked_add(Duration::from_secs(REGISTRATION_EXPIRATION_SECS))
.expect("SystemTime should not overflow"),
)
.expect("MilliSecondsSinceUnixEpoch is not too large");
Self {
info,
provider_id,
unique_claim,
redirect_uri,
expires_at,
}
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct RegistrationInfo {
pub username: String,
pub displayname: Option<String>,
pub avatar_url: Option<Url>,
pub email: Option<EmailAddress>,
}
impl RegistrationInfo {
pub fn new(
claims: &HashMap<String, Value>,
username: &str,
displayname: &str,
avatar_url: &str,
email: &str,
) -> Self {
Self {
username: claims
.get(username)
.and_then(|v| v.as_str())
.map(ToOwned::to_owned)
.unwrap_or_default(),
displayname: claims
.get(displayname)
.and_then(|v| v.as_str())
.map(ToOwned::to_owned),
avatar_url: claims
.get(avatar_url)
.and_then(|v| v.as_str())
.map(Url::parse)
.and_then(Result::ok),
email: claims
.get(email)
.and_then(|v| v.as_str())
.map(EmailAddress::from_str)
.and_then(Result::ok),
}
}
}
#[derive(Clone, Deserialize, Serialize)]
pub struct LoginToken {
pub inner: String,
pub provider_id: String,
pub user_id: OwnedUserId,
#[serde(rename = "exp")]
expires_at: u64,
pub iss: String,
pub aud: OwnedUserId,
pub sub: String,
pub exp: u64,
}
impl LoginToken {
pub fn new(provider_id: String, user_id: OwnedUserId) -> Self {
let expires_at = SystemTime::now()
.checked_add(Duration::from_secs(LOGIN_TOKEN_EXPIRATION_SECS))
.expect("SystemTime should not overflow")
.duration_since(UNIX_EPOCH)
.expect("SystemTime went backwards")
.as_secs();
pub fn new(provider: String, user_id: OwnedUserId) -> Self {
Self {
inner: utils::random_string(TOKEN_LENGTH),
provider_id,
user_id,
expires_at,
iss: provider,
aud: user_id,
sub: utils::random_string(TOKEN_LENGTH),
exp: utils::millis_since_unix_epoch()
.checked_add(LOGIN_TOKEN_EXPIRATION_SECS * 1000)
.expect("time overflow"),
}
}
pub fn audience(&self) -> &UserId {
&self.aud
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]