1use std::net::IpAddr;
8
9use chrono::{DateTime, Utc};
10use mas_data_model::Device;
11use schemars::JsonSchema;
12use serde::Serialize;
13use ulid::Ulid;
14use url::Url;
15
16pub trait Resource {
18 const KIND: &'static str;
20
21 const PATH: &'static str;
23
24 fn id(&self) -> Ulid;
26
27 fn path(&self) -> String {
31 format!("{}/{}", Self::PATH, self.id())
32 }
33}
34
35#[derive(Serialize, JsonSchema)]
37pub struct User {
38 #[serde(skip)]
39 id: Ulid,
40
41 username: String,
43
44 created_at: DateTime<Utc>,
46
47 locked_at: Option<DateTime<Utc>>,
49
50 admin: bool,
52}
53
54impl User {
55 pub fn samples() -> [Self; 3] {
57 [
58 Self {
59 id: Ulid::from_bytes([0x01; 16]),
60 username: "alice".to_owned(),
61 created_at: DateTime::default(),
62 locked_at: None,
63 admin: false,
64 },
65 Self {
66 id: Ulid::from_bytes([0x02; 16]),
67 username: "bob".to_owned(),
68 created_at: DateTime::default(),
69 locked_at: None,
70 admin: true,
71 },
72 Self {
73 id: Ulid::from_bytes([0x03; 16]),
74 username: "charlie".to_owned(),
75 created_at: DateTime::default(),
76 locked_at: Some(DateTime::default()),
77 admin: false,
78 },
79 ]
80 }
81}
82
83impl From<mas_data_model::User> for User {
84 fn from(user: mas_data_model::User) -> Self {
85 Self {
86 id: user.id,
87 username: user.username,
88 created_at: user.created_at,
89 locked_at: user.locked_at,
90 admin: user.can_request_admin,
91 }
92 }
93}
94
95impl Resource for User {
96 const KIND: &'static str = "user";
97 const PATH: &'static str = "/api/admin/v1/users";
98
99 fn id(&self) -> Ulid {
100 self.id
101 }
102}
103
104#[derive(Serialize, JsonSchema)]
106pub struct UserEmail {
107 #[serde(skip)]
108 id: Ulid,
109
110 created_at: DateTime<Utc>,
112
113 #[schemars(with = "super::schema::Ulid")]
115 user_id: Ulid,
116
117 email: String,
119}
120
121impl Resource for UserEmail {
122 const KIND: &'static str = "user-email";
123 const PATH: &'static str = "/api/admin/v1/user-emails";
124
125 fn id(&self) -> Ulid {
126 self.id
127 }
128}
129
130impl From<mas_data_model::UserEmail> for UserEmail {
131 fn from(value: mas_data_model::UserEmail) -> Self {
132 Self {
133 id: value.id,
134 created_at: value.created_at,
135 user_id: value.user_id,
136 email: value.email,
137 }
138 }
139}
140
141impl UserEmail {
142 pub fn samples() -> [Self; 1] {
143 [Self {
144 id: Ulid::from_bytes([0x01; 16]),
145 created_at: DateTime::default(),
146 user_id: Ulid::from_bytes([0x02; 16]),
147 email: "alice@example.com".to_owned(),
148 }]
149 }
150}
151
152#[derive(Serialize, JsonSchema)]
154pub struct CompatSession {
155 #[serde(skip)]
156 pub id: Ulid,
157
158 #[schemars(with = "super::schema::Ulid")]
160 pub user_id: Ulid,
161
162 #[schemars(with = "super::schema::Device")]
164 pub device_id: Option<Device>,
165
166 #[schemars(with = "super::schema::Ulid")]
168 pub user_session_id: Option<Ulid>,
169
170 pub redirect_uri: Option<Url>,
172
173 pub created_at: DateTime<Utc>,
175
176 pub user_agent: Option<String>,
178
179 pub last_active_at: Option<DateTime<Utc>>,
181
182 pub last_active_ip: Option<std::net::IpAddr>,
184
185 pub finished_at: Option<DateTime<Utc>>,
187}
188
189impl
190 From<(
191 mas_data_model::CompatSession,
192 Option<mas_data_model::CompatSsoLogin>,
193 )> for CompatSession
194{
195 fn from(
196 (session, sso_login): (
197 mas_data_model::CompatSession,
198 Option<mas_data_model::CompatSsoLogin>,
199 ),
200 ) -> Self {
201 let finished_at = session.finished_at();
202 Self {
203 id: session.id,
204 user_id: session.user_id,
205 device_id: session.device,
206 user_session_id: session.user_session_id,
207 redirect_uri: sso_login.map(|sso| sso.redirect_uri),
208 created_at: session.created_at,
209 user_agent: session.user_agent.map(|ua| ua.raw),
210 last_active_at: session.last_active_at,
211 last_active_ip: session.last_active_ip,
212 finished_at,
213 }
214 }
215}
216
217impl Resource for CompatSession {
218 const KIND: &'static str = "compat-session";
219 const PATH: &'static str = "/api/admin/v1/compat-sessions";
220
221 fn id(&self) -> Ulid {
222 self.id
223 }
224}
225
226impl CompatSession {
227 pub fn samples() -> [Self; 3] {
228 [
229 Self {
230 id: Ulid::from_bytes([0x01; 16]),
231 user_id: Ulid::from_bytes([0x01; 16]),
232 device_id: Some("AABBCCDDEE".to_owned().into()),
233 user_session_id: Some(Ulid::from_bytes([0x11; 16])),
234 redirect_uri: Some("https://example.com/redirect".parse().unwrap()),
235 created_at: DateTime::default(),
236 user_agent: Some("Mozilla/5.0".to_owned()),
237 last_active_at: Some(DateTime::default()),
238 last_active_ip: Some([1, 2, 3, 4].into()),
239 finished_at: None,
240 },
241 Self {
242 id: Ulid::from_bytes([0x02; 16]),
243 user_id: Ulid::from_bytes([0x01; 16]),
244 device_id: Some("FFGGHHIIJJ".to_owned().into()),
245 user_session_id: Some(Ulid::from_bytes([0x12; 16])),
246 redirect_uri: None,
247 created_at: DateTime::default(),
248 user_agent: Some("Mozilla/5.0".to_owned()),
249 last_active_at: Some(DateTime::default()),
250 last_active_ip: Some([1, 2, 3, 4].into()),
251 finished_at: Some(DateTime::default()),
252 },
253 Self {
254 id: Ulid::from_bytes([0x03; 16]),
255 user_id: Ulid::from_bytes([0x01; 16]),
256 device_id: None,
257 user_session_id: None,
258 redirect_uri: None,
259 created_at: DateTime::default(),
260 user_agent: None,
261 last_active_at: None,
262 last_active_ip: None,
263 finished_at: None,
264 },
265 ]
266 }
267}
268
269#[derive(Serialize, JsonSchema)]
271pub struct OAuth2Session {
272 #[serde(skip)]
273 id: Ulid,
274
275 created_at: DateTime<Utc>,
277
278 finished_at: Option<DateTime<Utc>>,
280
281 #[schemars(with = "Option<super::schema::Ulid>")]
283 user_id: Option<Ulid>,
284
285 #[schemars(with = "Option<super::schema::Ulid>")]
287 user_session_id: Option<Ulid>,
288
289 #[schemars(with = "super::schema::Ulid")]
291 client_id: Ulid,
292
293 scope: String,
295
296 user_agent: Option<String>,
298
299 last_active_at: Option<DateTime<Utc>>,
301
302 last_active_ip: Option<IpAddr>,
304}
305
306impl From<mas_data_model::Session> for OAuth2Session {
307 fn from(session: mas_data_model::Session) -> Self {
308 Self {
309 id: session.id,
310 created_at: session.created_at,
311 finished_at: session.finished_at(),
312 user_id: session.user_id,
313 user_session_id: session.user_session_id,
314 client_id: session.client_id,
315 scope: session.scope.to_string(),
316 user_agent: session.user_agent.map(|ua| ua.raw),
317 last_active_at: session.last_active_at,
318 last_active_ip: session.last_active_ip,
319 }
320 }
321}
322
323impl OAuth2Session {
324 pub fn samples() -> [Self; 3] {
326 [
327 Self {
328 id: Ulid::from_bytes([0x01; 16]),
329 created_at: DateTime::default(),
330 finished_at: None,
331 user_id: Some(Ulid::from_bytes([0x02; 16])),
332 user_session_id: Some(Ulid::from_bytes([0x03; 16])),
333 client_id: Ulid::from_bytes([0x04; 16]),
334 scope: "openid".to_owned(),
335 user_agent: Some("Mozilla/5.0".to_owned()),
336 last_active_at: Some(DateTime::default()),
337 last_active_ip: Some("127.0.0.1".parse().unwrap()),
338 },
339 Self {
340 id: Ulid::from_bytes([0x02; 16]),
341 created_at: DateTime::default(),
342 finished_at: None,
343 user_id: None,
344 user_session_id: None,
345 client_id: Ulid::from_bytes([0x05; 16]),
346 scope: "urn:mas:admin".to_owned(),
347 user_agent: None,
348 last_active_at: None,
349 last_active_ip: None,
350 },
351 Self {
352 id: Ulid::from_bytes([0x03; 16]),
353 created_at: DateTime::default(),
354 finished_at: Some(DateTime::default()),
355 user_id: Some(Ulid::from_bytes([0x04; 16])),
356 user_session_id: Some(Ulid::from_bytes([0x05; 16])),
357 client_id: Ulid::from_bytes([0x06; 16]),
358 scope: "urn:matrix:org.matrix.msc2967.client:api:*".to_owned(),
359 user_agent: Some("Mozilla/5.0".to_owned()),
360 last_active_at: Some(DateTime::default()),
361 last_active_ip: Some("127.0.0.1".parse().unwrap()),
362 },
363 ]
364 }
365}
366
367impl Resource for OAuth2Session {
368 const KIND: &'static str = "oauth2-session";
369 const PATH: &'static str = "/api/admin/v1/oauth2-sessions";
370
371 fn id(&self) -> Ulid {
372 self.id
373 }
374}
375
376#[derive(Serialize, JsonSchema)]
378pub struct UserSession {
379 #[serde(skip)]
380 id: Ulid,
381
382 created_at: DateTime<Utc>,
384
385 finished_at: Option<DateTime<Utc>>,
387
388 #[schemars(with = "super::schema::Ulid")]
390 user_id: Ulid,
391
392 user_agent: Option<String>,
394
395 last_active_at: Option<DateTime<Utc>>,
397
398 last_active_ip: Option<IpAddr>,
400}
401
402impl From<mas_data_model::BrowserSession> for UserSession {
403 fn from(value: mas_data_model::BrowserSession) -> Self {
404 Self {
405 id: value.id,
406 created_at: value.created_at,
407 finished_at: value.finished_at,
408 user_id: value.user.id,
409 user_agent: value.user_agent.map(|ua| ua.raw),
410 last_active_at: value.last_active_at,
411 last_active_ip: value.last_active_ip,
412 }
413 }
414}
415
416impl UserSession {
417 pub fn samples() -> [Self; 3] {
419 [
420 Self {
421 id: Ulid::from_bytes([0x01; 16]),
422 created_at: DateTime::default(),
423 finished_at: None,
424 user_id: Ulid::from_bytes([0x02; 16]),
425 user_agent: Some("Mozilla/5.0".to_owned()),
426 last_active_at: Some(DateTime::default()),
427 last_active_ip: Some("127.0.0.1".parse().unwrap()),
428 },
429 Self {
430 id: Ulid::from_bytes([0x02; 16]),
431 created_at: DateTime::default(),
432 finished_at: None,
433 user_id: Ulid::from_bytes([0x03; 16]),
434 user_agent: None,
435 last_active_at: None,
436 last_active_ip: None,
437 },
438 Self {
439 id: Ulid::from_bytes([0x03; 16]),
440 created_at: DateTime::default(),
441 finished_at: Some(DateTime::default()),
442 user_id: Ulid::from_bytes([0x04; 16]),
443 user_agent: Some("Mozilla/5.0".to_owned()),
444 last_active_at: Some(DateTime::default()),
445 last_active_ip: Some("127.0.0.1".parse().unwrap()),
446 },
447 ]
448 }
449}
450
451impl Resource for UserSession {
452 const KIND: &'static str = "user-session";
453 const PATH: &'static str = "/api/admin/v1/user-sessions";
454
455 fn id(&self) -> Ulid {
456 self.id
457 }
458}
459
460#[derive(Serialize, JsonSchema)]
462pub struct UpstreamOAuthLink {
463 #[serde(skip)]
464 id: Ulid,
465
466 created_at: DateTime<Utc>,
468
469 #[schemars(with = "super::schema::Ulid")]
471 provider_id: Ulid,
472
473 subject: String,
475
476 #[schemars(with = "Option<super::schema::Ulid>")]
478 user_id: Option<Ulid>,
479
480 human_account_name: Option<String>,
482}
483
484impl Resource for UpstreamOAuthLink {
485 const KIND: &'static str = "upstream-oauth-link";
486 const PATH: &'static str = "/api/admin/v1/upstream-oauth-links";
487
488 fn id(&self) -> Ulid {
489 self.id
490 }
491}
492
493impl From<mas_data_model::UpstreamOAuthLink> for UpstreamOAuthLink {
494 fn from(value: mas_data_model::UpstreamOAuthLink) -> Self {
495 Self {
496 id: value.id,
497 created_at: value.created_at,
498 provider_id: value.provider_id,
499 subject: value.subject,
500 user_id: value.user_id,
501 human_account_name: value.human_account_name,
502 }
503 }
504}
505
506impl UpstreamOAuthLink {
507 pub fn samples() -> [Self; 3] {
509 [
510 Self {
511 id: Ulid::from_bytes([0x01; 16]),
512 created_at: DateTime::default(),
513 provider_id: Ulid::from_bytes([0x02; 16]),
514 subject: "john-42".to_owned(),
515 user_id: Some(Ulid::from_bytes([0x03; 16])),
516 human_account_name: Some("john.doe@example.com".to_owned()),
517 },
518 Self {
519 id: Ulid::from_bytes([0x02; 16]),
520 created_at: DateTime::default(),
521 provider_id: Ulid::from_bytes([0x03; 16]),
522 subject: "jane-123".to_owned(),
523 user_id: None,
524 human_account_name: None,
525 },
526 Self {
527 id: Ulid::from_bytes([0x03; 16]),
528 created_at: DateTime::default(),
529 provider_id: Ulid::from_bytes([0x04; 16]),
530 subject: "bob@social.example.com".to_owned(),
531 user_id: Some(Ulid::from_bytes([0x05; 16])),
532 human_account_name: Some("bob".to_owned()),
533 },
534 ]
535 }
536}
537
538#[derive(Serialize, JsonSchema)]
540pub struct PolicyData {
541 #[serde(skip)]
542 id: Ulid,
543
544 created_at: DateTime<Utc>,
546
547 data: serde_json::Value,
549}
550
551impl From<mas_data_model::PolicyData> for PolicyData {
552 fn from(policy_data: mas_data_model::PolicyData) -> Self {
553 Self {
554 id: policy_data.id,
555 created_at: policy_data.created_at,
556 data: policy_data.data,
557 }
558 }
559}
560
561impl Resource for PolicyData {
562 const KIND: &'static str = "policy-data";
563 const PATH: &'static str = "/api/admin/v1/policy-data";
564
565 fn id(&self) -> Ulid {
566 self.id
567 }
568}
569
570impl PolicyData {
571 pub fn samples() -> [Self; 1] {
573 [Self {
574 id: Ulid::from_bytes([0x01; 16]),
575 created_at: DateTime::default(),
576 data: serde_json::json!({
577 "hello": "world",
578 "foo": 42,
579 "bar": true
580 }),
581 }]
582 }
583}