Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: option to ban user #1238

Merged
merged 18 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion src/libs/satellite/src/db/assert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use crate::hooks::{invoke_assert_delete_doc, invoke_assert_set_doc};
use crate::types::store::StoreContext;
use crate::usage::assert::{assert_user_usage_collection_data, increment_and_assert_db_usage};
use crate::user::assert::{
assert_user_collection_caller_key, assert_user_collection_data, assert_user_write_permission,
assert_user_collection_caller_key, assert_user_collection_data, assert_user_is_not_banned,
assert_user_write_permission,
};
use crate::{DelDoc, Doc, SetDoc};
use candid::Principal;
Expand All @@ -27,11 +28,25 @@ pub fn assert_get_doc(
rule: &Rule,
current_doc: &Doc,
) -> Result<(), String> {
assert_user_is_not_banned(caller, controllers)?;

assert_read_permission(caller, controllers, current_doc, &rule.read)?;

Ok(())
}

pub fn assert_get_docs(
&StoreContext {
caller,
controllers,
collection: _,
}: &StoreContext,
) -> Result<(), String> {
assert_user_is_not_banned(caller, controllers)?;

Ok(())
}

pub fn assert_set_doc(
&StoreContext {
caller,
Expand All @@ -44,6 +59,8 @@ pub fn assert_set_doc(
rule: &Rule,
current_doc: &Option<Doc>,
) -> Result<(), String> {
assert_user_is_not_banned(caller, controllers)?;

assert_write_permission(caller, controllers, current_doc, &rule.write)?;

assert_memory_size(config)?;
Expand Down Expand Up @@ -88,6 +105,8 @@ pub fn assert_delete_doc(
rule: &Rule,
current_doc: &Option<Doc>,
) -> Result<(), String> {
assert_user_is_not_banned(caller, controllers)?;

assert_write_permission(caller, controllers, current_doc, &rule.write)?;

assert_write_version(current_doc, value.version)?;
Expand Down
10 changes: 9 additions & 1 deletion src/libs/satellite/src/db/store.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::controllers::store::get_controllers;
use crate::db::assert::{assert_delete_doc, assert_get_doc, assert_set_doc};
use crate::db::assert::{assert_delete_doc, assert_get_doc, assert_get_docs, assert_set_doc};
use crate::db::state::{
count_docs_heap, count_docs_stable, delete_collection as delete_state_collection,
delete_doc as delete_state_doc, get_config, get_doc as get_state_doc, get_docs_heap,
Expand Down Expand Up @@ -253,6 +253,14 @@ fn secure_get_docs(
collection: CollectionKey,
filter: &ListParams,
) -> Result<ListResults<Doc>, String> {
let context: StoreContext = StoreContext {
caller,
collection: &collection,
controllers,
};

assert_get_docs(&context)?;

let rule = get_state_rule(&collection)?;

match rule.mem() {
Expand Down
51 changes: 48 additions & 3 deletions src/libs/satellite/src/storage/assert.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,51 @@
use crate::hooks::invoke_assert_delete_asset;
use crate::types::store::StoreContext;
use crate::usage::assert::increment_and_assert_storage_usage;
use crate::user::assert::is_known_user;
use crate::user::assert::{assert_user_is_not_banned, is_known_user};
use candid::Principal;
use junobuild_collections::assert::stores::{assert_permission, public_permission};
use junobuild_collections::types::rules::Rule;
use junobuild_collections::types::rules::{Permission, Rule};
use junobuild_shared::controllers::is_controller;
use junobuild_shared::types::state::Controllers;
use junobuild_storage::msg::{ERROR_ASSET_NOT_FOUND, UPLOAD_NOT_ALLOWED};
use junobuild_storage::msg::{ERROR_ASSET_NOT_FOUND, ERROR_CANNOT_READ_ASSET, UPLOAD_NOT_ALLOWED};
use junobuild_storage::runtime::increment_and_assert_rate as increment_and_assert_rate_runtime;
use junobuild_storage::types::store::Asset;

pub fn assert_get_asset(
&StoreContext {
caller,
controllers,
collection: _,
}: &StoreContext,
rule: &Rule,
current_asset: &Asset,
) -> Result<(), String> {
assert_user_is_not_banned(caller, controllers)?;

assert_read_permission(caller, controllers, current_asset, &rule.read)?;

Ok(())
}

pub fn assert_list_assets(
&StoreContext {
caller,
controllers,
collection: _,
}: &StoreContext,
) -> Result<(), String> {
assert_user_is_not_banned(caller, controllers)?;

Ok(())
}

pub fn assert_create_batch(
caller: Principal,
controllers: &Controllers,
rule: &Rule,
) -> Result<(), String> {
assert_user_is_not_banned(caller, controllers)?;

if !(public_permission(&rule.write)
|| is_known_user(caller)
|| is_controller(caller, controllers))
Expand All @@ -31,6 +61,8 @@ pub fn assert_delete_asset(
rule: &Rule,
asset: &Asset,
) -> Result<(), String> {
assert_user_is_not_banned(context.caller, context.controllers)?;

if !assert_permission(
&rule.write,
asset.key.owner,
Expand All @@ -53,3 +85,16 @@ pub fn assert_delete_asset(

Ok(())
}

fn assert_read_permission(
caller: Principal,
controllers: &Controllers,
current_asset: &Asset,
rule: &Permission,
) -> Result<(), String> {
if !assert_permission(&rule, current_asset.key.owner, caller, controllers) {
return Err(ERROR_CANNOT_READ_ASSET.to_string());
}

Ok(())
}
14 changes: 6 additions & 8 deletions src/libs/satellite/src/storage/store.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::controllers::store::get_controllers;
use crate::memory::STATE;
use crate::storage::assert::{assert_create_batch, assert_delete_asset};
use crate::storage::assert::{
assert_create_batch, assert_delete_asset, assert_get_asset, assert_list_assets,
};
use crate::storage::certified_assets::runtime::init_certified_assets as init_runtime_certified_assets;
use crate::storage::state::{
count_assets_stable, delete_asset as delete_state_asset, delete_domain as delete_state_domain,
Expand All @@ -13,7 +15,6 @@ use crate::storage::state::{
use crate::storage::strategy_impls::{StorageAssertions, StorageState, StorageUpload};
use crate::types::store::StoreContext;
use candid::Principal;
use junobuild_collections::assert::stores::assert_permission;
use junobuild_collections::msg::msg_storage_collection_not_empty;
use junobuild_collections::types::core::CollectionKey;
use junobuild_collections::types::rules::{Memory, Rule};
Expand Down Expand Up @@ -244,12 +245,7 @@ fn get_asset_impl(
match asset {
None => Ok(None),
Some(asset) => {
if !assert_permission(
&rule.read,
asset.key.owner,
context.caller,
context.controllers,
) {
if let Err(_) = assert_get_asset(context, rule, &asset) {
return Ok(None);
}

Expand Down Expand Up @@ -313,6 +309,8 @@ fn secure_list_assets_impl(
context: &StoreContext,
filters: &ListParams,
) -> Result<ListResults<AssetNoContent>, String> {
assert_list_assets(context)?;

let rule = get_state_rule(context.collection)?;

match rule.mem() {
Expand Down
28 changes: 26 additions & 2 deletions src/libs/satellite/src/user/assert.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::user::types::state::UserData;
use crate::user::types::state::{BannedReason, UserData};
use crate::{get_doc_store, Doc, SetDoc};
use candid::Principal;
use ic_cdk::id;
use junobuild_collections::constants::db::COLLECTION_USER_KEY;
use junobuild_collections::types::core::CollectionKey;
use junobuild_shared::controllers::is_admin_controller;
use junobuild_shared::controllers::{is_admin_controller, is_controller};
use junobuild_shared::types::core::Key;
use junobuild_shared::types::state::Controllers;
use junobuild_shared::utils::principal_not_equal;
Expand Down Expand Up @@ -75,3 +75,27 @@ pub fn assert_user_write_permission(
// The user already exist but the caller is not a controller
Err("Cannot update user.".to_string())
}

pub fn assert_user_is_not_banned(
caller: Principal,
controllers: &Controllers,
) -> Result<(), String> {
// This way we spare loading the user for controllers calls.
if is_controller(caller, controllers) {
return Ok(());
}

let user_key = caller.to_text();

let user = get_doc_store(id(), COLLECTION_USER_KEY.to_string(), user_key)?;

if let Some(user) = user {
let user_data = decode_doc_data::<UserData>(&user.data)?;

if let Some(BannedReason::Indefinite) = user_data.banned {
return Err("Not allowed.".to_string());
}
}

Ok(())
}
7 changes: 7 additions & 0 deletions src/libs/satellite/src/user/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod state {
#[serde(deny_unknown_fields)]
pub struct UserData {
pub provider: Option<AuthProvider>,
pub banned: Option<BannedReason>,
}

#[derive(Serialize, Deserialize)]
Expand All @@ -13,4 +14,10 @@ pub mod state {
InternetIdentity,
Nfid,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BannedReason {
Indefinite,
}
}
1 change: 1 addition & 0 deletions src/libs/storage/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ pub const UPLOAD_NOT_ALLOWED: &str = "Caller not allowed to upload data.";
pub const SET_NOT_ALLOWED: &str = "Caller not allowed to set data.";
pub const ERROR_CANNOT_COMMIT_BATCH: &str = "Cannot commit batch.";
pub const ERROR_ASSET_NOT_FOUND: &str = "No asset.";
pub const ERROR_CANNOT_READ_ASSET: &str = "Cannot read asset.";
1 change: 1 addition & 0 deletions src/tests/specs/constants/satellite-tests.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export const NO_VERSION_ERROR_MSG = 'error_no_version_provided';
export const INVALID_VERSION_ERROR_MSG = 'error_version_outdated_or_future';
export const USER_CANNOT_WRITE = 'Cannot write.';
export const CANNOT_UPDATE_USER = 'Cannot update user.';
export const USER_NOT_ALLOWED = 'Not allowed.';
Loading