Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 46 additions & 11 deletions crates/data-model/src/upstream_oauth2/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ pub enum UpstreamOAuthAuthorizationSessionState {
extra_callback_parameters: Option<serde_json::Value>,
userinfo: Option<serde_json::Value>,
},
Unlinked {
completed_at: DateTime<Utc>,
consumed_at: Option<DateTime<Utc>>,
unlinked_at: DateTime<Utc>,
id_token: Option<String>,
},
}

impl UpstreamOAuthAuthorizationSessionState {
Expand Down Expand Up @@ -57,7 +63,9 @@ impl UpstreamOAuthAuthorizationSessionState {
extra_callback_parameters,
userinfo,
}),
Self::Completed { .. } | Self::Consumed { .. } => Err(InvalidTransitionError),
Self::Completed { .. } | Self::Consumed { .. } | Self::Unlinked { .. } => {
Err(InvalidTransitionError)
}
}
}

Expand Down Expand Up @@ -85,7 +93,9 @@ impl UpstreamOAuthAuthorizationSessionState {
extra_callback_parameters,
userinfo,
}),
Self::Pending | Self::Consumed { .. } => Err(InvalidTransitionError),
Self::Pending | Self::Consumed { .. } | Self::Unlinked { .. } => {
Err(InvalidTransitionError)
}
}
}

Expand All @@ -98,7 +108,7 @@ impl UpstreamOAuthAuthorizationSessionState {
#[must_use]
pub fn link_id(&self) -> Option<Ulid> {
match self {
Self::Pending => None,
Self::Pending | Self::Unlinked { .. } => None,
Self::Completed { link_id, .. } | Self::Consumed { link_id, .. } => Some(*link_id),
}
}
Expand All @@ -114,9 +124,9 @@ impl UpstreamOAuthAuthorizationSessionState {
pub fn completed_at(&self) -> Option<DateTime<Utc>> {
match self {
Self::Pending => None,
Self::Completed { completed_at, .. } | Self::Consumed { completed_at, .. } => {
Some(*completed_at)
}
Self::Completed { completed_at, .. }
| Self::Consumed { completed_at, .. }
| Self::Unlinked { completed_at, .. } => Some(*completed_at),
}
}

Expand All @@ -130,9 +140,9 @@ impl UpstreamOAuthAuthorizationSessionState {
pub fn id_token(&self) -> Option<&str> {
match self {
Self::Pending => None,
Self::Completed { id_token, .. } | Self::Consumed { id_token, .. } => {
id_token.as_deref()
}
Self::Completed { id_token, .. }
| Self::Consumed { id_token, .. }
| Self::Unlinked { id_token, .. } => id_token.as_deref(),
}
}

Expand All @@ -145,7 +155,7 @@ impl UpstreamOAuthAuthorizationSessionState {
#[must_use]
pub fn extra_callback_parameters(&self) -> Option<&serde_json::Value> {
match self {
Self::Pending => None,
Self::Pending | Self::Unlinked { .. } => None,
Self::Completed {
extra_callback_parameters,
..
Expand All @@ -160,7 +170,7 @@ impl UpstreamOAuthAuthorizationSessionState {
#[must_use]
pub fn userinfo(&self) -> Option<&serde_json::Value> {
match self {
Self::Pending => None,
Self::Pending | Self::Unlinked { .. } => None,
Self::Completed { userinfo, .. } | Self::Consumed { userinfo, .. } => userinfo.as_ref(),
}
}
Expand All @@ -177,6 +187,22 @@ impl UpstreamOAuthAuthorizationSessionState {
match self {
Self::Pending | Self::Completed { .. } => None,
Self::Consumed { consumed_at, .. } => Some(*consumed_at),
Self::Unlinked { consumed_at, .. } => *consumed_at,
}
}

/// Get the time at which the upstream OAuth 2.0 authorization session was
/// unlinked.
///
/// Returns `None` if the upstream OAuth 2.0 authorization session state is
/// not [`Unlinked`].
///
/// [`Unlinked`]: UpstreamOAuthAuthorizationSessionState::Unlinked
#[must_use]
pub fn unlinked_at(&self) -> Option<DateTime<Utc>> {
match self {
Self::Pending | Self::Completed { .. } | Self::Consumed { .. } => None,
Self::Unlinked { unlinked_at, .. } => Some(*unlinked_at),
}
}

Expand Down Expand Up @@ -206,6 +232,15 @@ impl UpstreamOAuthAuthorizationSessionState {
pub fn is_consumed(&self) -> bool {
matches!(self, Self::Consumed { .. })
}

/// Returns `true` if the upstream OAuth 2.0 authorization session state is
/// [`Unlinked`].
///
/// [`Unlinked`]: UpstreamOAuthAuthorizationSessionState::Unlinked
#[must_use]
pub fn is_unlinked(&self) -> bool {
matches!(self, Self::Unlinked { .. })
}
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
Expand Down
9 changes: 9 additions & 0 deletions crates/handlers/src/admin/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ mod user_emails;
mod user_sessions;
mod users;

#[allow(clippy::too_many_lines)]
pub fn router<S>() -> ApiRouter<S>
where
S: Clone + Send + Sync + 'static,
Expand Down Expand Up @@ -123,13 +124,21 @@ where
get_with(
self::upstream_oauth_links::list,
self::upstream_oauth_links::list_doc,
)
.post_with(
self::upstream_oauth_links::add,
self::upstream_oauth_links::add_doc,
),
)
.api_route(
"/upstream-oauth-links/{id}",
get_with(
self::upstream_oauth_links::get,
self::upstream_oauth_links::get_doc,
)
.delete_with(
self::upstream_oauth_links::delete,
self::upstream_oauth_links::delete_doc,
),
)
}
Loading
Loading