mas_handlers/admin/v1/oauth2_sessions/
get.rs1use aide::{OperationIo, transform::TransformOperation};
8use axum::{Json, response::IntoResponse};
9use hyper::StatusCode;
10use mas_axum_utils::record_error;
11use ulid::Ulid;
12
13use crate::{
14 admin::{
15 call_context::CallContext,
16 model::OAuth2Session,
17 params::UlidPathParam,
18 response::{ErrorResponse, SingleResponse},
19 },
20 impl_from_error_for_route,
21};
22
23#[derive(Debug, thiserror::Error, OperationIo)]
24#[aide(output_with = "Json<ErrorResponse>")]
25pub enum RouteError {
26 #[error(transparent)]
27 Internal(Box<dyn std::error::Error + Send + Sync + 'static>),
28
29 #[error("OAuth 2.0 session ID {0} not found")]
30 NotFound(Ulid),
31}
32
33impl_from_error_for_route!(mas_storage::RepositoryError);
34
35impl IntoResponse for RouteError {
36 fn into_response(self) -> axum::response::Response {
37 let error = ErrorResponse::from_error(&self);
38 let sentry_event_id = record_error!(self, RouteError::Internal(_));
39 let status = match self {
40 Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
41 Self::NotFound(_) => StatusCode::NOT_FOUND,
42 };
43 (status, sentry_event_id, Json(error)).into_response()
44 }
45}
46
47pub fn doc(operation: TransformOperation) -> TransformOperation {
48 operation
49 .id("getOAuth2Session")
50 .summary("Get an OAuth 2.0 session")
51 .tag("oauth2-session")
52 .response_with::<200, Json<SingleResponse<OAuth2Session>>, _>(|t| {
53 let [sample, ..] = OAuth2Session::samples();
54 let response = SingleResponse::new_canonical(sample);
55 t.description("OAuth 2.0 session was found")
56 .example(response)
57 })
58 .response_with::<404, RouteError, _>(|t| {
59 let response = ErrorResponse::from_error(&RouteError::NotFound(Ulid::nil()));
60 t.description("OAuth 2.0 session was not found")
61 .example(response)
62 })
63}
64
65#[tracing::instrument(name = "handler.admin.v1.oauth2_session.get", skip_all)]
66pub async fn handler(
67 CallContext { mut repo, .. }: CallContext,
68 id: UlidPathParam,
69) -> Result<Json<SingleResponse<OAuth2Session>>, RouteError> {
70 let session = repo
71 .oauth2_session()
72 .lookup(*id)
73 .await?
74 .ok_or(RouteError::NotFound(*id))?;
75
76 Ok(Json(SingleResponse::new_canonical(OAuth2Session::from(
77 session,
78 ))))
79}
80
81#[cfg(test)]
82mod tests {
83 use hyper::{Request, StatusCode};
84 use mas_data_model::AccessToken;
85 use sqlx::PgPool;
86 use ulid::Ulid;
87
88 use crate::test_utils::{RequestBuilderExt, ResponseExt, TestState, setup};
89
90 #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
91 async fn test_get(pool: PgPool) {
92 setup();
93 let mut state = TestState::from_pool(pool).await.unwrap();
94 let token = state.token_with_scope("urn:mas:admin").await;
95
96 let mut repo = state.repository().await.unwrap();
98 let AccessToken { session_id, .. } = repo
99 .oauth2_access_token()
100 .find_by_token(&token)
101 .await
102 .unwrap()
103 .unwrap();
104 repo.save().await.unwrap();
105
106 let request = Request::get(format!("/api/admin/v1/oauth2-sessions/{session_id}"))
107 .bearer(&token)
108 .empty();
109 let response = state.request(request).await;
110 response.assert_status(StatusCode::OK);
111 let body: serde_json::Value = response.json();
112 assert_eq!(body["data"]["type"], "oauth2-session");
113 insta::assert_json_snapshot!(body, @r###"
114 {
115 "data": {
116 "type": "oauth2-session",
117 "id": "01FSHN9AG0MKGTBNZ16RDR3PVY",
118 "attributes": {
119 "created_at": "2022-01-16T14:40:00Z",
120 "finished_at": null,
121 "user_id": null,
122 "user_session_id": null,
123 "client_id": "01FSHN9AG0FAQ50MT1E9FFRPZR",
124 "scope": "urn:mas:admin",
125 "user_agent": null,
126 "last_active_at": null,
127 "last_active_ip": null
128 },
129 "links": {
130 "self": "/api/admin/v1/oauth2-sessions/01FSHN9AG0MKGTBNZ16RDR3PVY"
131 }
132 },
133 "links": {
134 "self": "/api/admin/v1/oauth2-sessions/01FSHN9AG0MKGTBNZ16RDR3PVY"
135 }
136 }
137 "###);
138 }
139
140 #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
141 async fn test_not_found(pool: PgPool) {
142 setup();
143 let mut state = TestState::from_pool(pool).await.unwrap();
144 let token = state.token_with_scope("urn:mas:admin").await;
145
146 let session_id = Ulid::nil();
147 let request = Request::get(format!("/api/admin/v1/oauth2-sessions/{session_id}"))
148 .bearer(&token)
149 .empty();
150 let response = state.request(request).await;
151 response.assert_status(StatusCode::NOT_FOUND);
152 }
153}