use std::net::IpAddr;
use chrono::{DateTime, Utc};
use mas_data_model::Device;
use schemars::JsonSchema;
use serde::Serialize;
use ulid::Ulid;
use url::Url;
pub trait Resource {
const KIND: &'static str;
const PATH: &'static str;
fn id(&self) -> Ulid;
fn path(&self) -> String {
format!("{}/{}", Self::PATH, self.id())
}
}
#[derive(Serialize, JsonSchema)]
pub struct User {
#[serde(skip)]
id: Ulid,
username: String,
created_at: DateTime<Utc>,
locked_at: Option<DateTime<Utc>>,
admin: bool,
}
impl User {
pub fn samples() -> [Self; 3] {
[
Self {
id: Ulid::from_bytes([0x01; 16]),
username: "alice".to_owned(),
created_at: DateTime::default(),
locked_at: None,
admin: false,
},
Self {
id: Ulid::from_bytes([0x02; 16]),
username: "bob".to_owned(),
created_at: DateTime::default(),
locked_at: None,
admin: true,
},
Self {
id: Ulid::from_bytes([0x03; 16]),
username: "charlie".to_owned(),
created_at: DateTime::default(),
locked_at: Some(DateTime::default()),
admin: false,
},
]
}
}
impl From<mas_data_model::User> for User {
fn from(user: mas_data_model::User) -> Self {
Self {
id: user.id,
username: user.username,
created_at: user.created_at,
locked_at: user.locked_at,
admin: user.can_request_admin,
}
}
}
impl Resource for User {
const KIND: &'static str = "user";
const PATH: &'static str = "/api/admin/v1/users";
fn id(&self) -> Ulid {
self.id
}
}
#[derive(Serialize, JsonSchema)]
pub struct UserEmail {
#[serde(skip)]
id: Ulid,
created_at: DateTime<Utc>,
#[schemars(with = "super::schema::Ulid")]
user_id: Ulid,
email: String,
}
impl Resource for UserEmail {
const KIND: &'static str = "user-email";
const PATH: &'static str = "/api/admin/v1/user-emails";
fn id(&self) -> Ulid {
self.id
}
}
impl From<mas_data_model::UserEmail> for UserEmail {
fn from(value: mas_data_model::UserEmail) -> Self {
Self {
id: value.id,
created_at: value.created_at,
user_id: value.user_id,
email: value.email,
}
}
}
impl UserEmail {
pub fn samples() -> [Self; 1] {
[Self {
id: Ulid::from_bytes([0x01; 16]),
created_at: DateTime::default(),
user_id: Ulid::from_bytes([0x02; 16]),
email: "alice@example.com".to_owned(),
}]
}
}
#[derive(Serialize, JsonSchema)]
pub struct CompatSession {
#[serde(skip)]
pub id: Ulid,
#[schemars(with = "super::schema::Ulid")]
pub user_id: Ulid,
#[schemars(with = "super::schema::Device")]
pub device_id: Option<Device>,
#[schemars(with = "super::schema::Ulid")]
pub user_session_id: Option<Ulid>,
pub redirect_uri: Option<Url>,
pub created_at: DateTime<Utc>,
pub user_agent: Option<String>,
pub last_active_at: Option<DateTime<Utc>>,
pub last_active_ip: Option<std::net::IpAddr>,
pub finished_at: Option<DateTime<Utc>>,
}
impl
From<(
mas_data_model::CompatSession,
Option<mas_data_model::CompatSsoLogin>,
)> for CompatSession
{
fn from(
(session, sso_login): (
mas_data_model::CompatSession,
Option<mas_data_model::CompatSsoLogin>,
),
) -> Self {
let finished_at = session.finished_at();
Self {
id: session.id,
user_id: session.user_id,
device_id: session.device,
user_session_id: session.user_session_id,
redirect_uri: sso_login.map(|sso| sso.redirect_uri),
created_at: session.created_at,
user_agent: session.user_agent.map(|ua| ua.raw),
last_active_at: session.last_active_at,
last_active_ip: session.last_active_ip,
finished_at,
}
}
}
impl Resource for CompatSession {
const KIND: &'static str = "compat-session";
const PATH: &'static str = "/api/admin/v1/compat-sessions";
fn id(&self) -> Ulid {
self.id
}
}
impl CompatSession {
pub fn samples() -> [Self; 3] {
[
Self {
id: Ulid::from_bytes([0x01; 16]),
user_id: Ulid::from_bytes([0x01; 16]),
device_id: Some("AABBCCDDEE".to_owned().into()),
user_session_id: Some(Ulid::from_bytes([0x11; 16])),
redirect_uri: Some("https://example.com/redirect".parse().unwrap()),
created_at: DateTime::default(),
user_agent: Some("Mozilla/5.0".to_owned()),
last_active_at: Some(DateTime::default()),
last_active_ip: Some([1, 2, 3, 4].into()),
finished_at: None,
},
Self {
id: Ulid::from_bytes([0x02; 16]),
user_id: Ulid::from_bytes([0x01; 16]),
device_id: Some("FFGGHHIIJJ".to_owned().into()),
user_session_id: Some(Ulid::from_bytes([0x12; 16])),
redirect_uri: None,
created_at: DateTime::default(),
user_agent: Some("Mozilla/5.0".to_owned()),
last_active_at: Some(DateTime::default()),
last_active_ip: Some([1, 2, 3, 4].into()),
finished_at: Some(DateTime::default()),
},
Self {
id: Ulid::from_bytes([0x03; 16]),
user_id: Ulid::from_bytes([0x01; 16]),
device_id: None,
user_session_id: None,
redirect_uri: None,
created_at: DateTime::default(),
user_agent: None,
last_active_at: None,
last_active_ip: None,
finished_at: None,
},
]
}
}
#[derive(Serialize, JsonSchema)]
pub struct OAuth2Session {
#[serde(skip)]
id: Ulid,
created_at: DateTime<Utc>,
finished_at: Option<DateTime<Utc>>,
#[schemars(with = "Option<super::schema::Ulid>")]
user_id: Option<Ulid>,
#[schemars(with = "Option<super::schema::Ulid>")]
user_session_id: Option<Ulid>,
#[schemars(with = "super::schema::Ulid")]
client_id: Ulid,
scope: String,
user_agent: Option<String>,
last_active_at: Option<DateTime<Utc>>,
last_active_ip: Option<IpAddr>,
}
impl From<mas_data_model::Session> for OAuth2Session {
fn from(session: mas_data_model::Session) -> Self {
Self {
id: session.id,
created_at: session.created_at,
finished_at: session.finished_at(),
user_id: session.user_id,
user_session_id: session.user_session_id,
client_id: session.client_id,
scope: session.scope.to_string(),
user_agent: session.user_agent.map(|ua| ua.raw),
last_active_at: session.last_active_at,
last_active_ip: session.last_active_ip,
}
}
}
impl OAuth2Session {
pub fn samples() -> [Self; 3] {
[
Self {
id: Ulid::from_bytes([0x01; 16]),
created_at: DateTime::default(),
finished_at: None,
user_id: Some(Ulid::from_bytes([0x02; 16])),
user_session_id: Some(Ulid::from_bytes([0x03; 16])),
client_id: Ulid::from_bytes([0x04; 16]),
scope: "openid".to_owned(),
user_agent: Some("Mozilla/5.0".to_owned()),
last_active_at: Some(DateTime::default()),
last_active_ip: Some("127.0.0.1".parse().unwrap()),
},
Self {
id: Ulid::from_bytes([0x02; 16]),
created_at: DateTime::default(),
finished_at: None,
user_id: None,
user_session_id: None,
client_id: Ulid::from_bytes([0x05; 16]),
scope: "urn:mas:admin".to_owned(),
user_agent: None,
last_active_at: None,
last_active_ip: None,
},
Self {
id: Ulid::from_bytes([0x03; 16]),
created_at: DateTime::default(),
finished_at: Some(DateTime::default()),
user_id: Some(Ulid::from_bytes([0x04; 16])),
user_session_id: Some(Ulid::from_bytes([0x05; 16])),
client_id: Ulid::from_bytes([0x06; 16]),
scope: "urn:matrix:org.matrix.msc2967.client:api:*".to_owned(),
user_agent: Some("Mozilla/5.0".to_owned()),
last_active_at: Some(DateTime::default()),
last_active_ip: Some("127.0.0.1".parse().unwrap()),
},
]
}
}
impl Resource for OAuth2Session {
const KIND: &'static str = "oauth2-session";
const PATH: &'static str = "/api/admin/v1/oauth2-sessions";
fn id(&self) -> Ulid {
self.id
}
}
#[derive(Serialize, JsonSchema)]
pub struct UserSession {
#[serde(skip)]
id: Ulid,
created_at: DateTime<Utc>,
finished_at: Option<DateTime<Utc>>,
#[schemars(with = "super::schema::Ulid")]
user_id: Ulid,
user_agent: Option<String>,
last_active_at: Option<DateTime<Utc>>,
last_active_ip: Option<IpAddr>,
}
impl From<mas_data_model::BrowserSession> for UserSession {
fn from(value: mas_data_model::BrowserSession) -> Self {
Self {
id: value.id,
created_at: value.created_at,
finished_at: value.finished_at,
user_id: value.user.id,
user_agent: value.user_agent.map(|ua| ua.raw),
last_active_at: value.last_active_at,
last_active_ip: value.last_active_ip,
}
}
}
impl UserSession {
pub fn samples() -> [Self; 3] {
[
Self {
id: Ulid::from_bytes([0x01; 16]),
created_at: DateTime::default(),
finished_at: None,
user_id: Ulid::from_bytes([0x02; 16]),
user_agent: Some("Mozilla/5.0".to_owned()),
last_active_at: Some(DateTime::default()),
last_active_ip: Some("127.0.0.1".parse().unwrap()),
},
Self {
id: Ulid::from_bytes([0x02; 16]),
created_at: DateTime::default(),
finished_at: None,
user_id: Ulid::from_bytes([0x03; 16]),
user_agent: None,
last_active_at: None,
last_active_ip: None,
},
Self {
id: Ulid::from_bytes([0x03; 16]),
created_at: DateTime::default(),
finished_at: Some(DateTime::default()),
user_id: Ulid::from_bytes([0x04; 16]),
user_agent: Some("Mozilla/5.0".to_owned()),
last_active_at: Some(DateTime::default()),
last_active_ip: Some("127.0.0.1".parse().unwrap()),
},
]
}
}
impl Resource for UserSession {
const KIND: &'static str = "user-session";
const PATH: &'static str = "/api/admin/v1/user-sessions";
fn id(&self) -> Ulid {
self.id
}
}
#[derive(Serialize, JsonSchema)]
pub struct UpstreamOAuthLink {
#[serde(skip)]
id: Ulid,
created_at: DateTime<Utc>,
#[schemars(with = "super::schema::Ulid")]
provider_id: Ulid,
subject: String,
#[schemars(with = "Option<super::schema::Ulid>")]
user_id: Option<Ulid>,
human_account_name: Option<String>,
}
impl Resource for UpstreamOAuthLink {
const KIND: &'static str = "upstream-oauth-link";
const PATH: &'static str = "/api/admin/v1/upstream-oauth-links";
fn id(&self) -> Ulid {
self.id
}
}
impl From<mas_data_model::UpstreamOAuthLink> for UpstreamOAuthLink {
fn from(value: mas_data_model::UpstreamOAuthLink) -> Self {
Self {
id: value.id,
created_at: value.created_at,
provider_id: value.provider_id,
subject: value.subject,
user_id: value.user_id,
human_account_name: value.human_account_name,
}
}
}
impl UpstreamOAuthLink {
pub fn samples() -> [Self; 3] {
[
Self {
id: Ulid::from_bytes([0x01; 16]),
created_at: DateTime::default(),
provider_id: Ulid::from_bytes([0x02; 16]),
subject: "john-42".to_owned(),
user_id: Some(Ulid::from_bytes([0x03; 16])),
human_account_name: Some("john.doe@example.com".to_owned()),
},
Self {
id: Ulid::from_bytes([0x02; 16]),
created_at: DateTime::default(),
provider_id: Ulid::from_bytes([0x03; 16]),
subject: "jane-123".to_owned(),
user_id: None,
human_account_name: None,
},
Self {
id: Ulid::from_bytes([0x03; 16]),
created_at: DateTime::default(),
provider_id: Ulid::from_bytes([0x04; 16]),
subject: "bob@social.example.com".to_owned(),
user_id: Some(Ulid::from_bytes([0x05; 16])),
human_account_name: Some("bob".to_owned()),
},
]
}
}
#[derive(Serialize, JsonSchema)]
pub struct PolicyData {
#[serde(skip)]
id: Ulid,
created_at: DateTime<Utc>,
data: serde_json::Value,
}
impl From<mas_data_model::PolicyData> for PolicyData {
fn from(policy_data: mas_data_model::PolicyData) -> Self {
Self {
id: policy_data.id,
created_at: policy_data.created_at,
data: policy_data.data,
}
}
}
impl Resource for PolicyData {
const KIND: &'static str = "policy-data";
const PATH: &'static str = "/api/admin/v1/policy-data";
fn id(&self) -> Ulid {
self.id
}
}
impl PolicyData {
pub fn samples() -> [Self; 1] {
[Self {
id: Ulid::from_bytes([0x01; 16]),
created_at: DateTime::default(),
data: serde_json::json!({
"hello": "world",
"foo": 42,
"bar": true
}),
}]
}
}