Scoped PATs (#651)

* Scoped PATs

* fix threads issues

* fix migration
This commit is contained in:
Geometrically
2023-07-10 16:44:40 -07:00
committed by GitHub
parent 366ea63209
commit 7fbb8838e7
42 changed files with 2560 additions and 1402 deletions

View File

@@ -1,6 +1,7 @@
use thiserror::Error;
pub use super::notifications::NotificationId;
pub use super::pats::PatId;
pub use super::projects::{ProjectId, VersionId};
pub use super::reports::ReportId;
pub use super::sessions::SessionId;
@@ -115,6 +116,7 @@ base62_id_impl!(NotificationId, NotificationId);
base62_id_impl!(ThreadId, ThreadId);
base62_id_impl!(ThreadMessageId, ThreadMessageId);
base62_id_impl!(SessionId, SessionId);
base62_id_impl!(PatId, PatId);
pub mod base62_impl {
use serde::de::{self, Deserializer, Visitor};

View File

@@ -2,6 +2,7 @@ pub mod error;
pub mod ids;
pub mod notifications;
pub mod pack;
pub mod pats;
pub mod projects;
pub mod reports;
pub mod sessions;

137
src/models/pats.rs Normal file
View File

@@ -0,0 +1,137 @@
use super::ids::Base62Id;
use crate::models::ids::UserId;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
/// The ID of a team
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(from = "Base62Id")]
#[serde(into = "Base62Id")]
pub struct PatId(pub u64);
bitflags::bitflags! {
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct Scopes: u64 {
// read a user's email
const USER_READ_EMAIL = 1 << 0;
// read a user's data
const USER_READ = 1 << 1;
// write to a user's profile (edit username, email, avatar, follows, etc)
const USER_WRITE = 1 << 2;
// delete a user
const USER_DELETE = 1 << 3;
// modify a user's authentication data
const USER_AUTH_WRITE = 1 << 4;
// read a user's notifications
const NOTIFICATION_READ = 1 << 5;
// delete or read a notification
const NOTIFICATION_WRITE = 1 << 6;
// read a user's payouts data
const PAYOUTS_READ = 1 << 7;
// withdraw money from a user's account
const PAYOUTS_WRITE = 1<< 8;
// access user analytics (payout analytics at the moment)
const ANALYTICS = 1 << 9;
// create a project
const PROJECT_CREATE = 1 << 10;
// read a user's projects (including private)
const PROJECT_READ = 1 << 11;
// write to a project's data (metadata, title, team members, etc)
const PROJECT_WRITE = 1 << 12;
// delete a project
const PROJECT_DELETE = 1 << 13;
// create a version
const VERSION_CREATE = 1 << 14;
// read a user's versions (including private)
const VERSION_READ = 1 << 15;
// write to a version's data (metadata, files, etc)
const VERSION_WRITE = 1 << 16;
// delete a project
const VERSION_DELETE = 1 << 17;
// create a report
const REPORT_CREATE = 1 << 18;
// read a user's reports
const REPORT_READ = 1 << 19;
// edit a report
const REPORT_WRITE = 1 << 20;
// delete a report
const REPORT_DELETE = 1 << 21;
// read a thread
const THREAD_READ = 1 << 22;
// write to a thread (send a message, delete a message)
const THREAD_WRITE = 1 << 23;
// create a pat
const PAT_CREATE = 1 << 24;
// read a user's pats
const PAT_READ = 1 << 25;
// edit a pat
const PAT_WRITE = 1 << 26;
// delete a pat
const PAT_DELETE = 1 << 27;
// read a user's sessions
const SESSION_READ = 1 << 28;
// delete a session22
const SESSION_DELETE = 1 << 29;
const ALL = 0b11111111111111111111111111;
const NONE = 0b0;
}
}
impl Scopes {
// these scopes cannot be specified in a personal access token
pub fn restricted(&self) -> bool {
self.contains(
Scopes::PAT_CREATE
| Scopes::PAT_READ
| Scopes::PAT_WRITE
| Scopes::PAT_DELETE
| Scopes::SESSION_READ
| Scopes::SESSION_DELETE
| Scopes::USER_AUTH_WRITE,
)
}
}
#[derive(Serialize, Deserialize)]
pub struct PersonalAccessToken {
pub id: PatId,
pub name: String,
pub access_token: Option<String>,
pub scopes: Scopes,
pub user_id: UserId,
pub created: DateTime<Utc>,
pub expires: DateTime<Utc>,
pub last_used: Option<DateTime<Utc>>,
}
impl PersonalAccessToken {
pub fn from(
data: crate::database::models::pat_item::PersonalAccessToken,
include_token: bool,
) -> Self {
Self {
id: data.id.into(),
name: data.name,
access_token: if include_token {
Some(data.access_token)
} else {
None
},
scopes: data.scopes,
user_id: data.user_id.into(),
created: data.created,
expires: data.expires,
last_used: data.last_used,
}
}
}

View File

@@ -106,7 +106,7 @@ pub struct Project {
pub color: Option<u32>,
/// The thread of the moderation messages of the project
pub thread_id: Option<ThreadId>,
pub thread_id: ThreadId,
/// The monetization status of this project
pub monetization_status: MonetizationStatus,
@@ -196,7 +196,7 @@ impl From<QueryProject> for Project {
})
.collect(),
color: m.color,
thread_id: m.thread_id.map(|x| x.into()),
thread_id: data.thread_id.into(),
monetization_status: m.monetization_status,
}
}

View File

@@ -19,7 +19,7 @@ pub struct Report {
pub body: String,
pub created: DateTime<Utc>,
pub closed: bool,
pub thread_id: Option<ThreadId>,
pub thread_id: ThreadId,
}
#[derive(Serialize, Deserialize, Clone)]
@@ -67,7 +67,7 @@ impl From<DBReport> for Report {
body: x.body,
created: x.created,
closed: x.closed,
thread_id: x.thread_id.map(|x| x.into()),
thread_id: x.thread_id.into(),
}
}
}

View File

@@ -12,7 +12,6 @@ pub struct TeamId(pub u64);
pub const OWNER_ROLE: &str = "Owner";
pub const DEFAULT_ROLE: &str = "Member";
// TODO: permissions, role names, etc
/// A team of users who control a project
#[derive(Serialize, Deserialize)]
pub struct Team {

View File

@@ -3,6 +3,7 @@ use crate::models::projects::ProjectStatus;
use crate::models::users::{User, UserId};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::models::ids::{ProjectId, ReportId};
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(from = "Base62Id")]
@@ -19,6 +20,8 @@ pub struct Thread {
pub id: ThreadId,
#[serde(rename = "type")]
pub type_: ThreadType,
pub project_id: Option<ProjectId>,
pub report_id: Option<ReportId>,
pub messages: Vec<ThreadMessage>,
pub members: Vec<User>,
}
@@ -90,6 +93,8 @@ impl Thread {
Thread {
id: data.id.into(),
type_: thread_type,
project_id: data.project_id.map(|x| x.into()),
report_id: data.report_id.map(|x| x.into()),
messages: data
.messages
.into_iter()