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 deactivated_at: Option<DateTime<Utc>>,
52
53 admin: bool,
55}
56
57impl User {
58 pub fn samples() -> [Self; 3] {
60 [
61 Self {
62 id: Ulid::from_bytes([0x01; 16]),
63 username: "alice".to_owned(),
64 created_at: DateTime::default(),
65 locked_at: None,
66 deactivated_at: None,
67 admin: false,
68 },
69 Self {
70 id: Ulid::from_bytes([0x02; 16]),
71 username: "bob".to_owned(),
72 created_at: DateTime::default(),
73 locked_at: None,
74 deactivated_at: None,
75 admin: true,
76 },
77 Self {
78 id: Ulid::from_bytes([0x03; 16]),
79 username: "charlie".to_owned(),
80 created_at: DateTime::default(),
81 locked_at: Some(DateTime::default()),
82 deactivated_at: None,
83 admin: false,
84 },
85 ]
86 }
87}
88
89impl From<mas_data_model::User> for User {
90 fn from(user: mas_data_model::User) -> Self {
91 Self {
92 id: user.id,
93 username: user.username,
94 created_at: user.created_at,
95 locked_at: user.locked_at,
96 deactivated_at: user.deactivated_at,
97 admin: user.can_request_admin,
98 }
99 }
100}
101
102impl Resource for User {
103 const KIND: &'static str = "user";
104 const PATH: &'static str = "/api/admin/v1/users";
105
106 fn id(&self) -> Ulid {
107 self.id
108 }
109}
110
111#[derive(Serialize, JsonSchema)]
113pub struct UserEmail {
114 #[serde(skip)]
115 id: Ulid,
116
117 created_at: DateTime<Utc>,
119
120 #[schemars(with = "super::schema::Ulid")]
122 user_id: Ulid,
123
124 email: String,
126}
127
128impl Resource for UserEmail {
129 const KIND: &'static str = "user-email";
130 const PATH: &'static str = "/api/admin/v1/user-emails";
131
132 fn id(&self) -> Ulid {
133 self.id
134 }
135}
136
137impl From<mas_data_model::UserEmail> for UserEmail {
138 fn from(value: mas_data_model::UserEmail) -> Self {
139 Self {
140 id: value.id,
141 created_at: value.created_at,
142 user_id: value.user_id,
143 email: value.email,
144 }
145 }
146}
147
148impl UserEmail {
149 pub fn samples() -> [Self; 1] {
150 [Self {
151 id: Ulid::from_bytes([0x01; 16]),
152 created_at: DateTime::default(),
153 user_id: Ulid::from_bytes([0x02; 16]),
154 email: "alice@example.com".to_owned(),
155 }]
156 }
157}
158
159#[derive(Serialize, JsonSchema)]
161pub struct CompatSession {
162 #[serde(skip)]
163 pub id: Ulid,
164
165 #[schemars(with = "super::schema::Ulid")]
167 pub user_id: Ulid,
168
169 #[schemars(with = "super::schema::Device")]
171 pub device_id: Option<Device>,
172
173 #[schemars(with = "super::schema::Ulid")]
175 pub user_session_id: Option<Ulid>,
176
177 pub redirect_uri: Option<Url>,
179
180 pub created_at: DateTime<Utc>,
182
183 pub user_agent: Option<String>,
185
186 pub last_active_at: Option<DateTime<Utc>>,
188
189 pub last_active_ip: Option<std::net::IpAddr>,
191
192 pub finished_at: Option<DateTime<Utc>>,
194
195 pub human_name: Option<String>,
197}
198
199impl
200 From<(
201 mas_data_model::CompatSession,
202 Option<mas_data_model::CompatSsoLogin>,
203 )> for CompatSession
204{
205 fn from(
206 (session, sso_login): (
207 mas_data_model::CompatSession,
208 Option<mas_data_model::CompatSsoLogin>,
209 ),
210 ) -> Self {
211 let finished_at = session.finished_at();
212 Self {
213 id: session.id,
214 user_id: session.user_id,
215 device_id: session.device,
216 user_session_id: session.user_session_id,
217 redirect_uri: sso_login.map(|sso| sso.redirect_uri),
218 created_at: session.created_at,
219 user_agent: session.user_agent,
220 last_active_at: session.last_active_at,
221 last_active_ip: session.last_active_ip,
222 finished_at,
223 human_name: session.human_name,
224 }
225 }
226}
227
228impl Resource for CompatSession {
229 const KIND: &'static str = "compat-session";
230 const PATH: &'static str = "/api/admin/v1/compat-sessions";
231
232 fn id(&self) -> Ulid {
233 self.id
234 }
235}
236
237impl CompatSession {
238 pub fn samples() -> [Self; 3] {
239 [
240 Self {
241 id: Ulid::from_bytes([0x01; 16]),
242 user_id: Ulid::from_bytes([0x01; 16]),
243 device_id: Some("AABBCCDDEE".to_owned().into()),
244 user_session_id: Some(Ulid::from_bytes([0x11; 16])),
245 redirect_uri: Some("https://example.com/redirect".parse().unwrap()),
246 created_at: DateTime::default(),
247 user_agent: Some("Mozilla/5.0".to_owned()),
248 last_active_at: Some(DateTime::default()),
249 last_active_ip: Some([1, 2, 3, 4].into()),
250 finished_at: None,
251 human_name: Some("Laptop".to_owned()),
252 },
253 Self {
254 id: Ulid::from_bytes([0x02; 16]),
255 user_id: Ulid::from_bytes([0x01; 16]),
256 device_id: Some("FFGGHHIIJJ".to_owned().into()),
257 user_session_id: Some(Ulid::from_bytes([0x12; 16])),
258 redirect_uri: None,
259 created_at: DateTime::default(),
260 user_agent: Some("Mozilla/5.0".to_owned()),
261 last_active_at: Some(DateTime::default()),
262 last_active_ip: Some([1, 2, 3, 4].into()),
263 finished_at: Some(DateTime::default()),
264 human_name: None,
265 },
266 Self {
267 id: Ulid::from_bytes([0x03; 16]),
268 user_id: Ulid::from_bytes([0x01; 16]),
269 device_id: None,
270 user_session_id: None,
271 redirect_uri: None,
272 created_at: DateTime::default(),
273 user_agent: None,
274 last_active_at: None,
275 last_active_ip: None,
276 finished_at: None,
277 human_name: None,
278 },
279 ]
280 }
281}
282
283#[derive(Serialize, JsonSchema)]
285pub struct OAuth2Session {
286 #[serde(skip)]
287 id: Ulid,
288
289 created_at: DateTime<Utc>,
291
292 finished_at: Option<DateTime<Utc>>,
294
295 #[schemars(with = "Option<super::schema::Ulid>")]
297 user_id: Option<Ulid>,
298
299 #[schemars(with = "Option<super::schema::Ulid>")]
301 user_session_id: Option<Ulid>,
302
303 #[schemars(with = "super::schema::Ulid")]
305 client_id: Ulid,
306
307 scope: String,
309
310 user_agent: Option<String>,
312
313 last_active_at: Option<DateTime<Utc>>,
315
316 last_active_ip: Option<IpAddr>,
318
319 human_name: Option<String>,
321}
322
323impl From<mas_data_model::Session> for OAuth2Session {
324 fn from(session: mas_data_model::Session) -> Self {
325 Self {
326 id: session.id,
327 created_at: session.created_at,
328 finished_at: session.finished_at(),
329 user_id: session.user_id,
330 user_session_id: session.user_session_id,
331 client_id: session.client_id,
332 scope: session.scope.to_string(),
333 user_agent: session.user_agent,
334 last_active_at: session.last_active_at,
335 last_active_ip: session.last_active_ip,
336 human_name: session.human_name,
337 }
338 }
339}
340
341impl OAuth2Session {
342 pub fn samples() -> [Self; 3] {
344 [
345 Self {
346 id: Ulid::from_bytes([0x01; 16]),
347 created_at: DateTime::default(),
348 finished_at: None,
349 user_id: Some(Ulid::from_bytes([0x02; 16])),
350 user_session_id: Some(Ulid::from_bytes([0x03; 16])),
351 client_id: Ulid::from_bytes([0x04; 16]),
352 scope: "openid".to_owned(),
353 user_agent: Some("Mozilla/5.0".to_owned()),
354 last_active_at: Some(DateTime::default()),
355 last_active_ip: Some("127.0.0.1".parse().unwrap()),
356 human_name: Some("Laptop".to_owned()),
357 },
358 Self {
359 id: Ulid::from_bytes([0x02; 16]),
360 created_at: DateTime::default(),
361 finished_at: None,
362 user_id: None,
363 user_session_id: None,
364 client_id: Ulid::from_bytes([0x05; 16]),
365 scope: "urn:mas:admin".to_owned(),
366 user_agent: None,
367 last_active_at: None,
368 last_active_ip: None,
369 human_name: None,
370 },
371 Self {
372 id: Ulid::from_bytes([0x03; 16]),
373 created_at: DateTime::default(),
374 finished_at: Some(DateTime::default()),
375 user_id: Some(Ulid::from_bytes([0x04; 16])),
376 user_session_id: Some(Ulid::from_bytes([0x05; 16])),
377 client_id: Ulid::from_bytes([0x06; 16]),
378 scope: "urn:matrix:org.matrix.msc2967.client:api:*".to_owned(),
379 user_agent: Some("Mozilla/5.0".to_owned()),
380 last_active_at: Some(DateTime::default()),
381 last_active_ip: Some("127.0.0.1".parse().unwrap()),
382 human_name: None,
383 },
384 ]
385 }
386}
387
388impl Resource for OAuth2Session {
389 const KIND: &'static str = "oauth2-session";
390 const PATH: &'static str = "/api/admin/v1/oauth2-sessions";
391
392 fn id(&self) -> Ulid {
393 self.id
394 }
395}
396
397#[derive(Serialize, JsonSchema)]
399pub struct UserSession {
400 #[serde(skip)]
401 id: Ulid,
402
403 created_at: DateTime<Utc>,
405
406 finished_at: Option<DateTime<Utc>>,
408
409 #[schemars(with = "super::schema::Ulid")]
411 user_id: Ulid,
412
413 user_agent: Option<String>,
415
416 last_active_at: Option<DateTime<Utc>>,
418
419 last_active_ip: Option<IpAddr>,
421}
422
423impl From<mas_data_model::BrowserSession> for UserSession {
424 fn from(value: mas_data_model::BrowserSession) -> Self {
425 Self {
426 id: value.id,
427 created_at: value.created_at,
428 finished_at: value.finished_at,
429 user_id: value.user.id,
430 user_agent: value.user_agent,
431 last_active_at: value.last_active_at,
432 last_active_ip: value.last_active_ip,
433 }
434 }
435}
436
437impl UserSession {
438 pub fn samples() -> [Self; 3] {
440 [
441 Self {
442 id: Ulid::from_bytes([0x01; 16]),
443 created_at: DateTime::default(),
444 finished_at: None,
445 user_id: Ulid::from_bytes([0x02; 16]),
446 user_agent: Some("Mozilla/5.0".to_owned()),
447 last_active_at: Some(DateTime::default()),
448 last_active_ip: Some("127.0.0.1".parse().unwrap()),
449 },
450 Self {
451 id: Ulid::from_bytes([0x02; 16]),
452 created_at: DateTime::default(),
453 finished_at: None,
454 user_id: Ulid::from_bytes([0x03; 16]),
455 user_agent: None,
456 last_active_at: None,
457 last_active_ip: None,
458 },
459 Self {
460 id: Ulid::from_bytes([0x03; 16]),
461 created_at: DateTime::default(),
462 finished_at: Some(DateTime::default()),
463 user_id: Ulid::from_bytes([0x04; 16]),
464 user_agent: Some("Mozilla/5.0".to_owned()),
465 last_active_at: Some(DateTime::default()),
466 last_active_ip: Some("127.0.0.1".parse().unwrap()),
467 },
468 ]
469 }
470}
471
472impl Resource for UserSession {
473 const KIND: &'static str = "user-session";
474 const PATH: &'static str = "/api/admin/v1/user-sessions";
475
476 fn id(&self) -> Ulid {
477 self.id
478 }
479}
480
481#[derive(Serialize, JsonSchema)]
483pub struct UpstreamOAuthLink {
484 #[serde(skip)]
485 id: Ulid,
486
487 created_at: DateTime<Utc>,
489
490 #[schemars(with = "super::schema::Ulid")]
492 provider_id: Ulid,
493
494 subject: String,
496
497 #[schemars(with = "Option<super::schema::Ulid>")]
499 user_id: Option<Ulid>,
500
501 human_account_name: Option<String>,
503}
504
505impl Resource for UpstreamOAuthLink {
506 const KIND: &'static str = "upstream-oauth-link";
507 const PATH: &'static str = "/api/admin/v1/upstream-oauth-links";
508
509 fn id(&self) -> Ulid {
510 self.id
511 }
512}
513
514impl From<mas_data_model::UpstreamOAuthLink> for UpstreamOAuthLink {
515 fn from(value: mas_data_model::UpstreamOAuthLink) -> Self {
516 Self {
517 id: value.id,
518 created_at: value.created_at,
519 provider_id: value.provider_id,
520 subject: value.subject,
521 user_id: value.user_id,
522 human_account_name: value.human_account_name,
523 }
524 }
525}
526
527impl UpstreamOAuthLink {
528 pub fn samples() -> [Self; 3] {
530 [
531 Self {
532 id: Ulid::from_bytes([0x01; 16]),
533 created_at: DateTime::default(),
534 provider_id: Ulid::from_bytes([0x02; 16]),
535 subject: "john-42".to_owned(),
536 user_id: Some(Ulid::from_bytes([0x03; 16])),
537 human_account_name: Some("john.doe@example.com".to_owned()),
538 },
539 Self {
540 id: Ulid::from_bytes([0x02; 16]),
541 created_at: DateTime::default(),
542 provider_id: Ulid::from_bytes([0x03; 16]),
543 subject: "jane-123".to_owned(),
544 user_id: None,
545 human_account_name: None,
546 },
547 Self {
548 id: Ulid::from_bytes([0x03; 16]),
549 created_at: DateTime::default(),
550 provider_id: Ulid::from_bytes([0x04; 16]),
551 subject: "bob@social.example.com".to_owned(),
552 user_id: Some(Ulid::from_bytes([0x05; 16])),
553 human_account_name: Some("bob".to_owned()),
554 },
555 ]
556 }
557}
558
559#[derive(Serialize, JsonSchema)]
561pub struct PolicyData {
562 #[serde(skip)]
563 id: Ulid,
564
565 created_at: DateTime<Utc>,
567
568 data: serde_json::Value,
570}
571
572impl From<mas_data_model::PolicyData> for PolicyData {
573 fn from(policy_data: mas_data_model::PolicyData) -> Self {
574 Self {
575 id: policy_data.id,
576 created_at: policy_data.created_at,
577 data: policy_data.data,
578 }
579 }
580}
581
582impl Resource for PolicyData {
583 const KIND: &'static str = "policy-data";
584 const PATH: &'static str = "/api/admin/v1/policy-data";
585
586 fn id(&self) -> Ulid {
587 self.id
588 }
589}
590
591impl PolicyData {
592 pub fn samples() -> [Self; 1] {
594 [Self {
595 id: Ulid::from_bytes([0x01; 16]),
596 created_at: DateTime::default(),
597 data: serde_json::json!({
598 "hello": "world",
599 "foo": 42,
600 "bar": true
601 }),
602 }]
603 }
604}
605
606#[derive(Serialize, JsonSchema)]
608pub struct UserRegistrationToken {
609 #[serde(skip)]
610 id: Ulid,
611
612 token: String,
614
615 valid: bool,
617
618 usage_limit: Option<u32>,
620
621 times_used: u32,
623
624 created_at: DateTime<Utc>,
626
627 last_used_at: Option<DateTime<Utc>>,
629
630 expires_at: Option<DateTime<Utc>>,
632
633 revoked_at: Option<DateTime<Utc>>,
635}
636
637impl UserRegistrationToken {
638 pub fn new(token: mas_data_model::UserRegistrationToken, now: DateTime<Utc>) -> Self {
639 Self {
640 id: token.id,
641 valid: token.is_valid(now),
642 token: token.token,
643 usage_limit: token.usage_limit,
644 times_used: token.times_used,
645 created_at: token.created_at,
646 last_used_at: token.last_used_at,
647 expires_at: token.expires_at,
648 revoked_at: token.revoked_at,
649 }
650 }
651}
652
653impl Resource for UserRegistrationToken {
654 const KIND: &'static str = "user-registration_token";
655 const PATH: &'static str = "/api/admin/v1/user-registration-tokens";
656
657 fn id(&self) -> Ulid {
658 self.id
659 }
660}
661
662impl UserRegistrationToken {
663 pub fn samples() -> [Self; 2] {
665 [
666 Self {
667 id: Ulid::from_bytes([0x01; 16]),
668 token: "abc123def456".to_owned(),
669 valid: true,
670 usage_limit: Some(10),
671 times_used: 5,
672 created_at: DateTime::default(),
673 last_used_at: Some(DateTime::default()),
674 expires_at: Some(DateTime::default() + chrono::Duration::days(30)),
675 revoked_at: None,
676 },
677 Self {
678 id: Ulid::from_bytes([0x02; 16]),
679 token: "xyz789abc012".to_owned(),
680 valid: false,
681 usage_limit: None,
682 times_used: 0,
683 created_at: DateTime::default(),
684 last_used_at: None,
685 expires_at: None,
686 revoked_at: Some(DateTime::default()),
687 },
688 ]
689 }
690}