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 pub human_name: Option<String>,
190}
191
192impl
193 From<(
194 mas_data_model::CompatSession,
195 Option<mas_data_model::CompatSsoLogin>,
196 )> for CompatSession
197{
198 fn from(
199 (session, sso_login): (
200 mas_data_model::CompatSession,
201 Option<mas_data_model::CompatSsoLogin>,
202 ),
203 ) -> Self {
204 let finished_at = session.finished_at();
205 Self {
206 id: session.id,
207 user_id: session.user_id,
208 device_id: session.device,
209 user_session_id: session.user_session_id,
210 redirect_uri: sso_login.map(|sso| sso.redirect_uri),
211 created_at: session.created_at,
212 user_agent: session.user_agent,
213 last_active_at: session.last_active_at,
214 last_active_ip: session.last_active_ip,
215 finished_at,
216 human_name: session.human_name,
217 }
218 }
219}
220
221impl Resource for CompatSession {
222 const KIND: &'static str = "compat-session";
223 const PATH: &'static str = "/api/admin/v1/compat-sessions";
224
225 fn id(&self) -> Ulid {
226 self.id
227 }
228}
229
230impl CompatSession {
231 pub fn samples() -> [Self; 3] {
232 [
233 Self {
234 id: Ulid::from_bytes([0x01; 16]),
235 user_id: Ulid::from_bytes([0x01; 16]),
236 device_id: Some("AABBCCDDEE".to_owned().into()),
237 user_session_id: Some(Ulid::from_bytes([0x11; 16])),
238 redirect_uri: Some("https://example.com/redirect".parse().unwrap()),
239 created_at: DateTime::default(),
240 user_agent: Some("Mozilla/5.0".to_owned()),
241 last_active_at: Some(DateTime::default()),
242 last_active_ip: Some([1, 2, 3, 4].into()),
243 finished_at: None,
244 human_name: Some("Laptop".to_owned()),
245 },
246 Self {
247 id: Ulid::from_bytes([0x02; 16]),
248 user_id: Ulid::from_bytes([0x01; 16]),
249 device_id: Some("FFGGHHIIJJ".to_owned().into()),
250 user_session_id: Some(Ulid::from_bytes([0x12; 16])),
251 redirect_uri: None,
252 created_at: DateTime::default(),
253 user_agent: Some("Mozilla/5.0".to_owned()),
254 last_active_at: Some(DateTime::default()),
255 last_active_ip: Some([1, 2, 3, 4].into()),
256 finished_at: Some(DateTime::default()),
257 human_name: None,
258 },
259 Self {
260 id: Ulid::from_bytes([0x03; 16]),
261 user_id: Ulid::from_bytes([0x01; 16]),
262 device_id: None,
263 user_session_id: None,
264 redirect_uri: None,
265 created_at: DateTime::default(),
266 user_agent: None,
267 last_active_at: None,
268 last_active_ip: None,
269 finished_at: None,
270 human_name: None,
271 },
272 ]
273 }
274}
275
276#[derive(Serialize, JsonSchema)]
278pub struct OAuth2Session {
279 #[serde(skip)]
280 id: Ulid,
281
282 created_at: DateTime<Utc>,
284
285 finished_at: Option<DateTime<Utc>>,
287
288 #[schemars(with = "Option<super::schema::Ulid>")]
290 user_id: Option<Ulid>,
291
292 #[schemars(with = "Option<super::schema::Ulid>")]
294 user_session_id: Option<Ulid>,
295
296 #[schemars(with = "super::schema::Ulid")]
298 client_id: Ulid,
299
300 scope: String,
302
303 user_agent: Option<String>,
305
306 last_active_at: Option<DateTime<Utc>>,
308
309 last_active_ip: Option<IpAddr>,
311
312 human_name: Option<String>,
314}
315
316impl From<mas_data_model::Session> for OAuth2Session {
317 fn from(session: mas_data_model::Session) -> Self {
318 Self {
319 id: session.id,
320 created_at: session.created_at,
321 finished_at: session.finished_at(),
322 user_id: session.user_id,
323 user_session_id: session.user_session_id,
324 client_id: session.client_id,
325 scope: session.scope.to_string(),
326 user_agent: session.user_agent,
327 last_active_at: session.last_active_at,
328 last_active_ip: session.last_active_ip,
329 human_name: session.human_name,
330 }
331 }
332}
333
334impl OAuth2Session {
335 pub fn samples() -> [Self; 3] {
337 [
338 Self {
339 id: Ulid::from_bytes([0x01; 16]),
340 created_at: DateTime::default(),
341 finished_at: None,
342 user_id: Some(Ulid::from_bytes([0x02; 16])),
343 user_session_id: Some(Ulid::from_bytes([0x03; 16])),
344 client_id: Ulid::from_bytes([0x04; 16]),
345 scope: "openid".to_owned(),
346 user_agent: Some("Mozilla/5.0".to_owned()),
347 last_active_at: Some(DateTime::default()),
348 last_active_ip: Some("127.0.0.1".parse().unwrap()),
349 human_name: Some("Laptop".to_owned()),
350 },
351 Self {
352 id: Ulid::from_bytes([0x02; 16]),
353 created_at: DateTime::default(),
354 finished_at: None,
355 user_id: None,
356 user_session_id: None,
357 client_id: Ulid::from_bytes([0x05; 16]),
358 scope: "urn:mas:admin".to_owned(),
359 user_agent: None,
360 last_active_at: None,
361 last_active_ip: None,
362 human_name: None,
363 },
364 Self {
365 id: Ulid::from_bytes([0x03; 16]),
366 created_at: DateTime::default(),
367 finished_at: Some(DateTime::default()),
368 user_id: Some(Ulid::from_bytes([0x04; 16])),
369 user_session_id: Some(Ulid::from_bytes([0x05; 16])),
370 client_id: Ulid::from_bytes([0x06; 16]),
371 scope: "urn:matrix:org.matrix.msc2967.client:api:*".to_owned(),
372 user_agent: Some("Mozilla/5.0".to_owned()),
373 last_active_at: Some(DateTime::default()),
374 last_active_ip: Some("127.0.0.1".parse().unwrap()),
375 human_name: None,
376 },
377 ]
378 }
379}
380
381impl Resource for OAuth2Session {
382 const KIND: &'static str = "oauth2-session";
383 const PATH: &'static str = "/api/admin/v1/oauth2-sessions";
384
385 fn id(&self) -> Ulid {
386 self.id
387 }
388}
389
390#[derive(Serialize, JsonSchema)]
392pub struct UserSession {
393 #[serde(skip)]
394 id: Ulid,
395
396 created_at: DateTime<Utc>,
398
399 finished_at: Option<DateTime<Utc>>,
401
402 #[schemars(with = "super::schema::Ulid")]
404 user_id: Ulid,
405
406 user_agent: Option<String>,
408
409 last_active_at: Option<DateTime<Utc>>,
411
412 last_active_ip: Option<IpAddr>,
414}
415
416impl From<mas_data_model::BrowserSession> for UserSession {
417 fn from(value: mas_data_model::BrowserSession) -> Self {
418 Self {
419 id: value.id,
420 created_at: value.created_at,
421 finished_at: value.finished_at,
422 user_id: value.user.id,
423 user_agent: value.user_agent,
424 last_active_at: value.last_active_at,
425 last_active_ip: value.last_active_ip,
426 }
427 }
428}
429
430impl UserSession {
431 pub fn samples() -> [Self; 3] {
433 [
434 Self {
435 id: Ulid::from_bytes([0x01; 16]),
436 created_at: DateTime::default(),
437 finished_at: None,
438 user_id: Ulid::from_bytes([0x02; 16]),
439 user_agent: Some("Mozilla/5.0".to_owned()),
440 last_active_at: Some(DateTime::default()),
441 last_active_ip: Some("127.0.0.1".parse().unwrap()),
442 },
443 Self {
444 id: Ulid::from_bytes([0x02; 16]),
445 created_at: DateTime::default(),
446 finished_at: None,
447 user_id: Ulid::from_bytes([0x03; 16]),
448 user_agent: None,
449 last_active_at: None,
450 last_active_ip: None,
451 },
452 Self {
453 id: Ulid::from_bytes([0x03; 16]),
454 created_at: DateTime::default(),
455 finished_at: Some(DateTime::default()),
456 user_id: Ulid::from_bytes([0x04; 16]),
457 user_agent: Some("Mozilla/5.0".to_owned()),
458 last_active_at: Some(DateTime::default()),
459 last_active_ip: Some("127.0.0.1".parse().unwrap()),
460 },
461 ]
462 }
463}
464
465impl Resource for UserSession {
466 const KIND: &'static str = "user-session";
467 const PATH: &'static str = "/api/admin/v1/user-sessions";
468
469 fn id(&self) -> Ulid {
470 self.id
471 }
472}
473
474#[derive(Serialize, JsonSchema)]
476pub struct UpstreamOAuthLink {
477 #[serde(skip)]
478 id: Ulid,
479
480 created_at: DateTime<Utc>,
482
483 #[schemars(with = "super::schema::Ulid")]
485 provider_id: Ulid,
486
487 subject: String,
489
490 #[schemars(with = "Option<super::schema::Ulid>")]
492 user_id: Option<Ulid>,
493
494 human_account_name: Option<String>,
496}
497
498impl Resource for UpstreamOAuthLink {
499 const KIND: &'static str = "upstream-oauth-link";
500 const PATH: &'static str = "/api/admin/v1/upstream-oauth-links";
501
502 fn id(&self) -> Ulid {
503 self.id
504 }
505}
506
507impl From<mas_data_model::UpstreamOAuthLink> for UpstreamOAuthLink {
508 fn from(value: mas_data_model::UpstreamOAuthLink) -> Self {
509 Self {
510 id: value.id,
511 created_at: value.created_at,
512 provider_id: value.provider_id,
513 subject: value.subject,
514 user_id: value.user_id,
515 human_account_name: value.human_account_name,
516 }
517 }
518}
519
520impl UpstreamOAuthLink {
521 pub fn samples() -> [Self; 3] {
523 [
524 Self {
525 id: Ulid::from_bytes([0x01; 16]),
526 created_at: DateTime::default(),
527 provider_id: Ulid::from_bytes([0x02; 16]),
528 subject: "john-42".to_owned(),
529 user_id: Some(Ulid::from_bytes([0x03; 16])),
530 human_account_name: Some("john.doe@example.com".to_owned()),
531 },
532 Self {
533 id: Ulid::from_bytes([0x02; 16]),
534 created_at: DateTime::default(),
535 provider_id: Ulid::from_bytes([0x03; 16]),
536 subject: "jane-123".to_owned(),
537 user_id: None,
538 human_account_name: None,
539 },
540 Self {
541 id: Ulid::from_bytes([0x03; 16]),
542 created_at: DateTime::default(),
543 provider_id: Ulid::from_bytes([0x04; 16]),
544 subject: "bob@social.example.com".to_owned(),
545 user_id: Some(Ulid::from_bytes([0x05; 16])),
546 human_account_name: Some("bob".to_owned()),
547 },
548 ]
549 }
550}
551
552#[derive(Serialize, JsonSchema)]
554pub struct PolicyData {
555 #[serde(skip)]
556 id: Ulid,
557
558 created_at: DateTime<Utc>,
560
561 data: serde_json::Value,
563}
564
565impl From<mas_data_model::PolicyData> for PolicyData {
566 fn from(policy_data: mas_data_model::PolicyData) -> Self {
567 Self {
568 id: policy_data.id,
569 created_at: policy_data.created_at,
570 data: policy_data.data,
571 }
572 }
573}
574
575impl Resource for PolicyData {
576 const KIND: &'static str = "policy-data";
577 const PATH: &'static str = "/api/admin/v1/policy-data";
578
579 fn id(&self) -> Ulid {
580 self.id
581 }
582}
583
584impl PolicyData {
585 pub fn samples() -> [Self; 1] {
587 [Self {
588 id: Ulid::from_bytes([0x01; 16]),
589 created_at: DateTime::default(),
590 data: serde_json::json!({
591 "hello": "world",
592 "foo": 42,
593 "bar": true
594 }),
595 }]
596 }
597}