Skip to content

Commit

Permalink
Improvements in the permission system
Browse files Browse the repository at this point in the history
  • Loading branch information
wyatt-herkamp committed Aug 23, 2024
1 parent 0f612ff commit 680f360
Show file tree
Hide file tree
Showing 35 changed files with 498 additions and 263 deletions.
6 changes: 3 additions & 3 deletions crates/core/src/database/user/auth_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use uuid::Uuid;

use crate::{
database::DateTime,
user::{permissions::RepositoryActionOptions, scopes::Scopes},
user::{permissions::RepositoryActions, scopes::Scopes},
};

use super::ReferencesUser;
Expand Down Expand Up @@ -73,7 +73,7 @@ impl AuthToken {
pub async fn has_repository_action(
&self,
repository_id: Uuid,
repository_action: RepositoryActionOptions,
repository_action: RepositoryActions,
database: &PgPool,
) -> sqlx::Result<bool> {
// Check if the user has the general scope. See RepositoryActions for more info
Expand All @@ -82,7 +82,7 @@ impl AuthToken {
return Ok(true);
}
// TODO condense this into one query
let Some(actions) = sqlx::query_scalar::<_, Vec<RepositoryActionOptions>>(
let Some(actions) = sqlx::query_scalar::<_, Vec<RepositoryActions>>(
r#"SELECT actions FROM user_auth_token_repository_scopes WHERE user_auth_token_id = $1 AND repository_id = $2"#,
)
.bind(self.id)
Expand Down
18 changes: 7 additions & 11 deletions crates/core/src/database/user/auth_token/repository_scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use sqlx::{
use tracing::{debug, instrument, span};
use uuid::Uuid;

use crate::{database::DateTime, user::permissions::RepositoryActionOptions};
use crate::{database::DateTime, user::permissions::RepositoryActions};

use super::{create_token, hash_token};
/// Represents the actions that can be taken on a repository
Expand All @@ -22,23 +22,23 @@ pub struct AuthTokenRepositoryScope {
pub id: i32,
pub user_auth_token_id: i32,
pub repository_id: Uuid,
pub action: Vec<RepositoryActionOptions>,
pub action: Vec<RepositoryActions>,
pub created_at: DateTime,
}

#[derive(Debug, Clone, PartialEq, Eq, Builder)]
pub struct NewRepositoryToken {
pub user_id: i32,
pub source: String,
pub repositories: Vec<(Uuid, Vec<RepositoryActionOptions>)>,
pub repositories: Vec<(Uuid, Vec<RepositoryActions>)>,
pub expires_at: Option<DateTime>,
}
impl NewRepositoryToken {
pub fn new(
user_id: i32,
source: String,
repository: Uuid,
actions: Vec<RepositoryActionOptions>,
actions: Vec<RepositoryActions>,
) -> Self {
Self {
user_id,
Expand All @@ -47,11 +47,7 @@ impl NewRepositoryToken {
expires_at: None,
}
}
pub fn add_repository(
mut self,
repository: Uuid,
actions: Vec<RepositoryActionOptions>,
) -> Self {
pub fn add_repository(mut self, repository: Uuid, actions: Vec<RepositoryActions>) -> Self {
self.repositories.push((repository, actions));
self
}
Expand Down Expand Up @@ -92,7 +88,7 @@ impl NewRepositoryToken {
pub struct NewRepositoryScope {
pub token_id: i32,
pub repository: Uuid,
pub actions: Vec<RepositoryActionOptions>,
pub actions: Vec<RepositoryActions>,
}
impl NewRepositoryScope {
#[instrument]
Expand All @@ -103,7 +99,7 @@ impl NewRepositoryScope {
actions,
} = self;
sqlx::query(
r#"INSERT INTO user_auth_token_repository_scopes (user_auth_token_id, repository, actions) VALUES ($1, $2, $3)"#,
r#"INSERT INTO user_auth_token_repository_scopes (user_auth_token_id, repository_id, actions) VALUES ($1, $2, $3)"#,
)
.bind(token_id)
.bind(repository)
Expand Down
27 changes: 10 additions & 17 deletions crates/core/src/database/user/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ use sqlx::{postgres::PgRow, types::Json, FromRow, PgPool};
use utoipa::ToSchema;

use crate::user::{
permissions::{HasPermissions, RepositoryActionOptions, UserPermissions},
permissions::{HasPermissions, RepositoryActions, UserPermissions},
Email, Username,
};

use super::DateTime;
pub mod auth_token;
pub mod password_reset;
pub mod permissions;
pub mod user_utils;
/// Implements on types that references a user in the database.
///
Expand Down Expand Up @@ -181,12 +182,10 @@ pub struct UserModel {
pub require_password_change: bool,
pub admin: bool,
pub user_manager: bool,
/// Storage Manager will be able to create and delete storage locations
pub storage_manager: bool,
/// Repository Manager will be able to create and delete repositories
/// Also will have full read/write access to all repositories
pub repository_manager: bool,
pub default_repository_actions: Vec<RepositoryActionOptions>,
pub system_manager: bool,
pub default_repository_actions: Vec<RepositoryActions>,
pub updated_at: DateTime,
pub created_at: DateTime,
}
Expand Down Expand Up @@ -223,12 +222,10 @@ pub struct UserSafeData {
pub active: bool,
pub admin: bool,
pub user_manager: bool,
/// Storage Manager will be able to create and delete storage locations
pub storage_manager: bool,
/// Repository Manager will be able to create and delete repositories
/// Also will have full read/write access to all repositories
pub repository_manager: bool,
pub default_repository_actions: Vec<RepositoryActionOptions>,
pub system_manager: bool,
pub default_repository_actions: Vec<RepositoryActions>,
pub updated_at: DateTime,
pub created_at: DateTime,
}
Expand All @@ -243,8 +240,7 @@ impl UserType for UserSafeData {
"active",
"admin",
"user_manager",
"storage_manager",
"repository_manager",
"system_manager",
"default_repository_actions",
"updated_at",
"created_at",
Expand All @@ -265,8 +261,7 @@ impl HasPermissions for UserSafeData {
id: self.id,
admin: self.admin,
user_manager: self.user_manager,
storage_manager: self.storage_manager,
repository_manager: self.repository_manager,
system_manager: self.system_manager,
default_repository_actions: self.default_repository_actions.clone(),
})
}
Expand All @@ -284,8 +279,7 @@ impl From<UserModel> for UserSafeData {
created_at: user.created_at,
admin: user.admin,
user_manager: user.user_manager,
storage_manager: user.storage_manager,
repository_manager: user.repository_manager,
system_manager: user.system_manager,
default_repository_actions: user.default_repository_actions,
}
}
Expand All @@ -307,8 +301,7 @@ mod tests {
require_password_change: Default::default(),
admin: Default::default(),
user_manager: Default::default(),
storage_manager: Default::default(),
repository_manager: Default::default(),
system_manager: Default::default(),
default_repository_actions: Default::default(),
updated_at: Default::default(),
created_at: Default::default(),
Expand Down
125 changes: 125 additions & 0 deletions crates/core/src/database/user/permissions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use sqlx::{prelude::FromRow, PgPool};
use tracing::instrument;
use utoipa::ToSchema;
use uuid::Uuid;

use crate::{
database::DateTime,
user::permissions::{RepositoryActions, UserPermissions},
};

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, ToSchema, FromRow)]

pub struct UserRepositoryPermissions {
pub id: i32,
pub user_id: i32,
pub repository_id: Uuid,
pub actions: Vec<RepositoryActions>,
pub updated_at: DateTime,
pub created_at: DateTime,
}
impl UserRepositoryPermissions {
pub async fn has_repository_action(
user_id: i32,
repository: Uuid,
action: RepositoryActions,
database: &PgPool,
) -> sqlx::Result<bool> {
let Some(actions) = sqlx::query_scalar::<_, Vec<RepositoryActions>>(
r#"SELECT * FROM user_repository_permissions WHERE user_id = $1 AND repository_id = $2 "#,
)
.bind(user_id)
.bind(repository)
.fetch_optional(database)
.await? else{
return Ok(false);
};
Ok(actions.contains(&action))
}
pub async fn get_all_for_user_as_map(
user_id: i32,
database: &PgPool,
) -> sqlx::Result<HashMap<Uuid, Vec<RepositoryActions>>> {
let permissions = sqlx::query_scalar::<_, (Uuid, Vec<RepositoryActions>)>(
r#"SELECT (repository_id, actions) FROM user_repository_permissions WHERE user_id = $1"#,
)
.bind(user_id)
.fetch_all(database)
.await?;
let mut map = HashMap::new();
for (repository, actions) in permissions {
map.insert(repository, actions);
}
Ok(map)
}
pub async fn delete(user_id: i32, repository_id: Uuid, database: &PgPool) -> sqlx::Result<()> {
sqlx::query(
r#"DELETE FROM user_repository_permissions WHERE user_id = $1 AND repository_id = $2"#,
)
.bind(user_id)
.bind(repository_id)
.execute(database)
.await?;
Ok(())
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, ToSchema, FromRow)]

pub struct NewUserRepositoryPermissions {
pub user_id: i32,
pub repository_id: Uuid,
pub actions: Vec<RepositoryActions>,
}
impl NewUserRepositoryPermissions {
#[instrument]
pub async fn insert(self, database: &PgPool) -> sqlx::Result<i32> {
let row:i32 = sqlx::query_scalar(
r#"INSERT INTO user_repository_permissions (user_id, repository_id, actions) VALUES ($1, $2, $3)
ON CONFLICT (user_id, repository_id) DO UPDATE SET actions = $3
RETURNING id"#,
)
.bind(self.user_id)
.bind(self.repository_id)
.bind(self.actions)
.fetch_one(database)
.await?;
Ok(row)
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, ToSchema)]
pub struct FullUserPermissions {
pub user_id: i32,
pub admin: bool,
pub user_manager: bool,
/// Repository Manager will be able to create and delete repositories
/// Also will have full read/write access to all repositories
pub system_manager: bool,
pub default_repository_actions: Vec<RepositoryActions>,
pub repository_permissions: HashMap<Uuid, Vec<RepositoryActions>>,
}
impl FullUserPermissions {
pub async fn get_by_id(user_id: i32, database: &PgPool) -> sqlx::Result<Option<Self>> {
let permissions =
sqlx::query_as::<_, UserPermissions>(r#"SELECT * FROM users WHERE id = $1"#)
.bind(user_id)
.fetch_optional(database)
.await?;
let Some(permissions) = permissions else {
return Ok(None);
};
let repository_permissions =
UserRepositoryPermissions::get_all_for_user_as_map(user_id, database).await?;
let permissions = FullUserPermissions {
user_id,
admin: permissions.admin,
user_manager: permissions.user_manager,
system_manager: permissions.system_manager,
default_repository_actions: permissions.default_repository_actions,
repository_permissions,
};
Ok(Some(permissions))
}
}
Loading

0 comments on commit 680f360

Please sign in to comment.