oidc authentication
This commit is contained in:
4
backend/src/lib/domain/oidc/mod.rs
Normal file
4
backend/src/lib/domain/oidc/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod models;
|
||||
pub mod ports;
|
||||
pub mod requests;
|
||||
pub mod service;
|
||||
69
backend/src/lib/domain/oidc/models.rs
Normal file
69
backend/src/lib/domain/oidc/models.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use crate::domain::warren::models::user::{UserEmail, UserName};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct UserInfo {
|
||||
sub: String,
|
||||
name: UserName,
|
||||
email: UserEmail,
|
||||
preferred_username: Option<UserName>,
|
||||
picture: Option<String>,
|
||||
locale: Option<String>,
|
||||
updated_at: Option<i64>,
|
||||
warren_admin: Option<bool>,
|
||||
}
|
||||
|
||||
impl UserInfo {
|
||||
pub fn new(
|
||||
sub: String,
|
||||
name: UserName,
|
||||
email: UserEmail,
|
||||
preferred_username: Option<UserName>,
|
||||
picture: Option<String>,
|
||||
locale: Option<String>,
|
||||
updated_at: Option<i64>,
|
||||
warren_admin: Option<bool>,
|
||||
) -> Self {
|
||||
Self {
|
||||
sub,
|
||||
name,
|
||||
email,
|
||||
preferred_username,
|
||||
picture,
|
||||
locale,
|
||||
updated_at,
|
||||
warren_admin,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sub(&self) -> &String {
|
||||
&self.sub
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &UserName {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn email(&self) -> &UserEmail {
|
||||
&self.email
|
||||
}
|
||||
|
||||
pub fn preferred_username(&self) -> Option<&UserName> {
|
||||
self.preferred_username.as_ref()
|
||||
}
|
||||
|
||||
pub fn picture(&self) -> Option<&String> {
|
||||
self.picture.as_ref()
|
||||
}
|
||||
|
||||
pub fn locale(&self) -> Option<&String> {
|
||||
self.locale.as_ref()
|
||||
}
|
||||
|
||||
pub fn updated_at(&self) -> Option<i64> {
|
||||
self.updated_at
|
||||
}
|
||||
|
||||
pub fn warren_admin(&self) -> Option<bool> {
|
||||
self.warren_admin
|
||||
}
|
||||
}
|
||||
39
backend/src/lib/domain/oidc/ports.rs
Normal file
39
backend/src/lib/domain/oidc/ports.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use super::requests::{
|
||||
GetRedirectError, GetRedirectRequest, GetRedirectResponse, GetUserInfoError,
|
||||
GetUserInfoRequest, GetUserInfoResponse,
|
||||
};
|
||||
|
||||
pub trait OidcService: Clone + Send + Sync + 'static {
|
||||
fn get_redirect(
|
||||
&self,
|
||||
request: GetRedirectRequest,
|
||||
) -> impl Future<Output = Result<GetRedirectResponse, GetRedirectError>> + Send;
|
||||
fn get_user_info(
|
||||
&self,
|
||||
request: GetUserInfoRequest,
|
||||
) -> impl Future<Output = Result<GetUserInfoResponse, GetUserInfoError>> + Send;
|
||||
}
|
||||
|
||||
pub trait OidcRepository: Clone + Send + Sync + 'static {
|
||||
fn get_redirect(
|
||||
&self,
|
||||
request: GetRedirectRequest,
|
||||
) -> impl Future<Output = Result<GetRedirectResponse, GetRedirectError>> + Send;
|
||||
fn get_user_info(
|
||||
&self,
|
||||
request: GetUserInfoRequest,
|
||||
) -> impl Future<Output = Result<GetUserInfoResponse, GetUserInfoError>> + Send;
|
||||
}
|
||||
|
||||
pub trait OidcMetrics: Clone + Send + Sync + 'static {
|
||||
fn record_get_redirect_success(&self) -> impl Future<Output = ()> + Send;
|
||||
fn record_get_redirect_failure(&self) -> impl Future<Output = ()> + Send;
|
||||
|
||||
fn record_get_user_info_success(&self) -> impl Future<Output = ()> + Send;
|
||||
fn record_get_user_info_failure(&self) -> impl Future<Output = ()> + Send;
|
||||
}
|
||||
|
||||
pub trait OidcNotifier: Clone + Send + Sync + 'static {
|
||||
fn get_redirect(&self, response: &GetRedirectResponse) -> impl Future<Output = ()> + Send;
|
||||
fn get_user_info(&self, response: &GetUserInfoResponse) -> impl Future<Output = ()> + Send;
|
||||
}
|
||||
74
backend/src/lib/domain/oidc/requests.rs
Normal file
74
backend/src/lib/domain/oidc/requests.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use thiserror::Error;
|
||||
|
||||
use super::models::UserInfo;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct GetRedirectRequest {}
|
||||
|
||||
impl GetRedirectRequest {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct GetRedirectResponse {
|
||||
url: String,
|
||||
}
|
||||
|
||||
impl GetRedirectResponse {
|
||||
pub fn new(url: String) -> Self {
|
||||
Self { url }
|
||||
}
|
||||
|
||||
pub fn url(&self) -> &String {
|
||||
&self.url
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum GetRedirectError {
|
||||
#[error(transparent)]
|
||||
Unknown(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct GetUserInfoRequest {
|
||||
code: String,
|
||||
state: Option<String>,
|
||||
}
|
||||
|
||||
impl GetUserInfoRequest {
|
||||
pub fn new(code: String, state: Option<String>) -> Self {
|
||||
Self { code, state }
|
||||
}
|
||||
|
||||
pub fn code(&self) -> &String {
|
||||
&self.code
|
||||
}
|
||||
|
||||
pub fn state(&self) -> Option<&String> {
|
||||
self.state.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct GetUserInfoResponse {
|
||||
info: UserInfo,
|
||||
}
|
||||
|
||||
impl GetUserInfoResponse {
|
||||
pub fn new(info: UserInfo) -> Self {
|
||||
Self { info }
|
||||
}
|
||||
|
||||
pub fn info(&self) -> &UserInfo {
|
||||
&self.info
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum GetUserInfoError {
|
||||
#[error(transparent)]
|
||||
Unknown(#[from] anyhow::Error),
|
||||
}
|
||||
73
backend/src/lib/domain/oidc/service.rs
Normal file
73
backend/src/lib/domain/oidc/service.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use super::{
|
||||
ports::{OidcMetrics, OidcNotifier, OidcRepository, OidcService},
|
||||
requests::{
|
||||
GetRedirectError, GetRedirectRequest, GetRedirectResponse, GetUserInfoError,
|
||||
GetUserInfoRequest, GetUserInfoResponse,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Service<R, M, N>
|
||||
where
|
||||
R: OidcRepository,
|
||||
M: OidcMetrics,
|
||||
N: OidcNotifier,
|
||||
{
|
||||
repository: R,
|
||||
metrics: M,
|
||||
notifier: N,
|
||||
}
|
||||
|
||||
impl<R, M, N> Service<R, M, N>
|
||||
where
|
||||
R: OidcRepository,
|
||||
M: OidcMetrics,
|
||||
N: OidcNotifier,
|
||||
{
|
||||
pub fn new(repository: R, metrics: M, notifier: N) -> Self {
|
||||
Self {
|
||||
repository,
|
||||
metrics,
|
||||
notifier,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, M, N> OidcService for Service<R, M, N>
|
||||
where
|
||||
R: OidcRepository,
|
||||
M: OidcMetrics,
|
||||
N: OidcNotifier,
|
||||
{
|
||||
async fn get_redirect(
|
||||
&self,
|
||||
request: GetRedirectRequest,
|
||||
) -> Result<GetRedirectResponse, GetRedirectError> {
|
||||
let result = self.repository.get_redirect(request).await;
|
||||
|
||||
if let Ok(response) = result.as_ref() {
|
||||
self.metrics.record_get_redirect_success().await;
|
||||
self.notifier.get_redirect(response).await;
|
||||
} else {
|
||||
self.metrics.record_get_redirect_failure().await;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
async fn get_user_info(
|
||||
&self,
|
||||
request: GetUserInfoRequest,
|
||||
) -> Result<GetUserInfoResponse, GetUserInfoError> {
|
||||
let result = self.repository.get_user_info(request).await;
|
||||
|
||||
if let Ok(response) = result.as_ref() {
|
||||
self.metrics.record_get_user_info_success().await;
|
||||
self.notifier.get_user_info(response).await;
|
||||
} else {
|
||||
self.metrics.record_get_user_info_failure().await;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user