1use std::str::FromStr;
2
3use actix_web::{
4 HttpResponse, Responder,
5 web::{Data, Json, Path, ServiceConfig, delete, get, post, put},
6};
7use async_trait::async_trait;
8use serde::{Deserialize, Serialize};
9use uuid::Uuid;
10
11use crate::{IntoPublic, ObjectId, session::SessionRes};
12
13pub enum IdentityError {
14 NotFound,
15 InternalServerError,
16 ServiceUnavailable,
17 Unauthorized,
18 InvalidId,
19 UsernameAlreadyInUse,
20}
21
22impl From<IdentityError> for HttpResponse {
23 fn from(value: IdentityError) -> Self {
24 match value {
25 IdentityError::InternalServerError => HttpResponse::InternalServerError().finish(),
26 IdentityError::NotFound => HttpResponse::NotFound().finish(),
27 IdentityError::ServiceUnavailable => HttpResponse::ServiceUnavailable().finish(),
28 IdentityError::Unauthorized => HttpResponse::Unauthorized().finish(),
29 IdentityError::InvalidId => HttpResponse::BadRequest().finish(),
30 IdentityError::UsernameAlreadyInUse => HttpResponse::Conflict().finish(),
31 }
32 }
33}
34
35#[derive(Deserialize)]
36pub struct IdentityGetPath {
37 id: String,
38}
39
40#[derive(Clone)]
41pub struct IdentityProvider<T>
42where
43 T: IntoPublic
44 + ObjectId
45 + Serialize
46 + for<'de> Deserialize<'de>
47 + Clone
48 + Send
49 + Sync
50 + 'static,
51{
52 identity_base_path: String,
53 backend: Data<Box<dyn IdentityBackend<T>>>,
54}
55
56impl<
57 T: IntoPublic + ObjectId + Serialize + for<'de> Deserialize<'de> + Clone + Send + Sync + 'static,
58> IdentityProvider<T>
59{
60 pub fn default_with_backend(backend: Data<Box<dyn IdentityBackend<T>>>) -> Self {
61 Self {
62 identity_base_path: String::from("identity"),
63 backend,
64 }
65 }
66
67 pub fn configure(&self, cfg: &mut ServiceConfig) {
68 let data = Data::new(self.clone());
69 cfg.app_data(data.clone())
70 .route(&data.identity_base_path, get().to(get_all::<T>))
71 .route(&data.identity_base_path, post().to(create::<T>))
72 .route(
73 &format!("{}/{{id}}", data.identity_base_path),
74 get().to(get_by_id::<T>),
75 )
76 .route(
77 &format!("{}/{{id}}", data.identity_base_path),
78 put().to(update_by_id::<T>),
79 )
80 .route(
81 &format!("{}/{{id}}", data.identity_base_path),
82 delete().to(delete_by_id::<T>),
83 );
84 }
85
86 pub async fn get_all(&self) -> Result<Vec<T>, IdentityError> {
87 self.backend.get_all().await
88 }
89
90 pub async fn create(&self, identity: T) -> Result<(), IdentityError> {
91 let by_username = self.backend.get_by_username(identity.username()).await?;
92 if by_username.is_some() {
93 return Err(IdentityError::UsernameAlreadyInUse);
94 }
95
96 self.backend.create(identity).await
97 }
98
99 pub async fn get_by_id(&self, id: String) -> Result<T, IdentityError> {
100 self.backend.get_by_id(id).await
101 }
102
103 pub async fn update(&self, id: String, identity: T) -> Result<(), IdentityError> {
104 self.backend.update_by_id(id, identity).await
105 }
106
107 pub async fn delete(&self, id: String) -> Result<(), IdentityError> {
108 self.backend.delete_by_id(id).await
109 }
110}
111
112async fn get_all<
113 T: IntoPublic + ObjectId + Serialize + for<'de> Deserialize<'de> + Clone + Send + Sync + 'static,
114>(
115 identity_provider: Data<IdentityProvider<T>>,
116) -> impl Responder {
117 match identity_provider.get_all().await {
118 Ok(result) => HttpResponse::Ok().json(
119 result
120 .into_iter()
121 .map(|res| res.into_public())
122 .collect::<Vec<T::Public>>(),
123 ),
124 Err(e) => e.into(),
125 }
126}
127
128async fn create<
129 T: IntoPublic + ObjectId + Serialize + for<'de> Deserialize<'de> + Clone + Send + Sync + 'static,
130>(
131 identity_provider: Data<IdentityProvider<T>>,
132 identity: Json<T>,
133) -> impl Responder {
134 match identity_provider.create(identity.0).await {
135 Ok(_) => HttpResponse::Created().finish(),
136 Err(e) => e.into(),
137 }
138}
139
140async fn get_by_id<
141 T: IntoPublic + ObjectId + Serialize + for<'de> Deserialize<'de> + Clone + Send + Sync + 'static,
142>(
143 identity_provider: Data<IdentityProvider<T>>,
144 path: Path<IdentityGetPath>,
145) -> impl Responder {
146 match identity_provider.get_by_id(path.id.clone()).await {
147 Ok(res) => HttpResponse::Ok().json(res.into_public()),
148 Err(e) => e.into(),
149 }
150}
151
152async fn update_by_id<
153 T: IntoPublic + ObjectId + Serialize + for<'de> Deserialize<'de> + Clone + Send + Sync + 'static,
154>(
155 identity_provider: Data<IdentityProvider<T>>,
156 path: Path<IdentityGetPath>,
157 identity: Json<T>,
158 session: SessionRes<T>,
159) -> impl Responder {
160 if session.inner.id() != Uuid::from_str(&path.id).ok() {
161 return HttpResponse::Unauthorized().finish();
162 }
163
164 match identity_provider.update(path.id.clone(), identity.0).await {
165 Ok(_) => HttpResponse::Ok().finish(),
166 Err(e) => e.into(),
167 }
168}
169
170async fn delete_by_id<
171 T: IntoPublic + ObjectId + Serialize + for<'de> Deserialize<'de> + Clone + Send + Sync + 'static,
172>(
173 identity_provider: Data<IdentityProvider<T>>,
174 path: Path<IdentityGetPath>,
175 session: SessionRes<T>,
176) -> impl Responder {
177 if session.inner.id() != Uuid::from_str(&path.id).ok() {
178 return HttpResponse::Unauthorized().finish();
179 }
180
181 match identity_provider.delete(path.id.clone()).await {
182 Ok(_) => HttpResponse::NoContent().finish(),
183 Err(e) => e.into(),
184 }
185}
186
187#[async_trait]
188pub trait IdentityBackend<T>: Send + Sync
189where
190 T: ObjectId + Serialize + for<'de> Deserialize<'de>,
191{
192 async fn get_all(&self) -> Result<Vec<T>, IdentityError>;
193 async fn create(&self, mut identity: T) -> Result<(), IdentityError>;
194 async fn get_by_id(&self, id: String) -> Result<T, IdentityError>;
195 async fn get_by_username(&self, username: String) -> Result<Option<T>, IdentityError>;
196 async fn update_by_id(&self, id: String, identity: T) -> Result<(), IdentityError>;
197 async fn delete_by_id(&self, id: String) -> Result<(), IdentityError>;
198}