Skip to content

Commit

Permalink
feat: refactor access control to enable swapping implementation (#859)
Browse files Browse the repository at this point in the history
  • Loading branch information
khorshuheng authored Oct 10, 2024
1 parent 98347b8 commit 8c14612
Show file tree
Hide file tree
Showing 50 changed files with 1,382 additions and 1,286 deletions.
22 changes: 4 additions & 18 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ gotrue-entity = { path = "libs/gotrue-entity" }
infra = { path = "libs/infra" }
authentication.workspace = true
access-control.workspace = true
workspace-access.workspace = true
app-error = { workspace = true, features = [
"sqlx_error",
"actix_web_error",
Expand Down Expand Up @@ -195,7 +194,6 @@ members = [
"libs/gotrue-entity",
"admin_frontend",
"libs/app-error",
"libs/workspace-access",
"libs/workspace-template",
"libs/encrypt",
"libs/authentication",
Expand Down Expand Up @@ -226,7 +224,6 @@ shared-entity = { path = "libs/shared-entity" }
gotrue-entity = { path = "libs/gotrue-entity" }
authentication = { path = "libs/authentication" }
access-control = { path = "libs/access-control" }
workspace-access = { path = "libs/workspace-access" }
app-error = { path = "libs/app-error" }
async-trait = "0.1.77"
prometheus-client = "0.22.0"
Expand Down
17 changes: 12 additions & 5 deletions libs/access-control/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ actix-http.workspace = true
app-error.workspace = true
anyhow.workspace = true
async-trait.workspace = true
casbin = { version = "2.2.0", features = ["cached", "runtime-tokio", "incremental"] }
casbin = { version = "2.2.0", features = [
"cached",
"runtime-tokio",
"incremental",
], optional = true }
database.workspace = true
database-entity.workspace = true
futures-util.workspace = true
Expand All @@ -17,8 +21,11 @@ prometheus-client.workspace = true
redis.workspace = true
sqlx = { workspace = true, default-features = false, features = ["postgres"] }
tracing.workspace = true
tokio = { workspace = true, features = [
"macros",
"time",
] }
tokio = { workspace = true, features = ["macros", "time"] }
tokio-stream.workspace = true
uuid = { version = "1.8.0", features = ["v4"] }
serde = { version = "1.0.200", features = ["derive"] }

[features]
default = ["casbin"]
casbin = ["dep:casbin"]
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use super::adapter::PgAdapter;
use super::enforcer::{AFEnforcer, NoEnforceGroup};
use crate::act::{Action, ActionVariant, Acts};
use crate::adapter::PgAdapter;
use crate::enforcer::{AFEnforcer, NoEnforceGroup};
use crate::entity::ObjectType;
use crate::metrics::{tick_metric, AccessControlMetrics};

use anyhow::anyhow;
use app_error::AppError;
use casbin::rhai::ImmutableString;
use casbin::{CoreApi, DefaultModel, Enforcer, MgmtApi};
use database_entity::dto::{AFAccessLevel, AFRole};
use lazy_static::lazy_static;

use sqlx::PgPool;

Expand Down Expand Up @@ -80,30 +80,19 @@ impl AccessControl {
obj: ObjectType<'_>,
act: ActionVariant<'_>,
) -> Result<(), AppError> {
if enable_access_control() {
let change = AccessControlChange::UpdatePolicy {
uid: *uid,
oid: obj.object_id().to_string(),
};
self.enforcer.update_policy(uid, obj, act).await?;
let access_control_change = self.enforcer.update_policy(uid, obj, act).await?;
if let Some(change) = access_control_change {
let _ = self.change_tx.send(change);
Ok(())
} else {
Ok(())
}
Ok(())
}

pub async fn remove_policy(&self, uid: &i64, obj: &ObjectType<'_>) -> Result<(), AppError> {
if enable_access_control() {
self.enforcer.remove_policy(uid, obj).await?;
let _ = self.change_tx.send(AccessControlChange::RemovePolicy {
uid: *uid,
oid: obj.object_id().to_string(),
});
Ok(())
} else {
Ok(())
let access_control_change = self.enforcer.remove_policy(uid, obj).await?;
if let Some(change) = access_control_change {
let _ = self.change_tx.send(change);
}
Ok(())
}

pub async fn enforce(
Expand All @@ -113,14 +102,10 @@ impl AccessControl {
obj: ObjectType<'_>,
act: ActionVariant<'_>,
) -> Result<bool, AppError> {
if enable_access_control() {
self
.enforcer
.enforce_policy(workspace_id, uid, obj, act)
.await
} else {
Ok(true)
}
self
.enforcer
.enforce_policy(workspace_id, uid, obj, act)
.await
}
}

Expand Down Expand Up @@ -256,45 +241,6 @@ const GROUPING_FIELD_INDEX_ROLE: usize = 0;
#[allow(dead_code)]
const GROUPING_FIELD_INDEX_ACTION: usize = 1;

/// Represents the object type that is stored in the access control policy.
#[derive(Debug)]
pub enum ObjectType<'id> {
/// Stored as `workspace::<uuid>`
Workspace(&'id str),
/// Stored as `collab::<uuid>`
Collab(&'id str),
}

impl ObjectType<'_> {
pub fn policy_object(&self) -> String {
match self {
ObjectType::Collab(s) => format!("collab::{}", s),
ObjectType::Workspace(s) => format!("workspace::{}", s),
}
}

pub fn object_id(&self) -> &str {
match self {
ObjectType::Collab(s) => s,
ObjectType::Workspace(s) => s,
}
}
}

lazy_static! {
static ref ENABLE_ACCESS_CONTROL: bool = {
match std::env::var("APPFLOWY_ACCESS_CONTROL") {
Ok(value) => value.eq_ignore_ascii_case("true") || value.eq("1"),
Err(_) => false,
}
};
}

#[inline]
pub fn enable_access_control() -> bool {
*ENABLE_ACCESS_CONTROL
}

pub(crate) async fn load_group_policies(enforcer: &mut Enforcer) -> Result<(), AppError> {
// Grouping definition of access level to action.
let af_access_levels = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::access::ObjectType;

use async_trait::async_trait;

use crate::entity::ObjectType;
use crate::metrics::AccessControlMetrics;
use casbin::Adapter;
use casbin::Filter;
Expand Down
Loading

0 comments on commit 8c14612

Please sign in to comment.