Skip to content

Commit 469922e

Browse files
committed
Merge #703: Feat: allow overwriting casbin configuration
c1a5c25 feat: [#702] allow overwriting casbin configuration (Jose Celano) Pull request description: This is an **unstable** feature. You can overwrite casbin configuration to change permissions for roles: guest, registered and admin. You can do it by adding this new section to the TOML config file: ```toml [unstable.auth.casbin] model = """ [request_definition] r = role, action [policy_definition] p = role, action [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.role == p.role && r.action == p.action """ policy = """ admin, GetAboutPage admin, GetLicensePage admin, AddCategory admin, DeleteCategory admin, GetCategories admin, GetImageByUrl admin, GetSettings admin, GetSettingsSecret admin, GetPublicSettings admin, AddTag admin, DeleteTag admin, GetTags admin, AddTorrent admin, GetTorrent admin, DeleteTorrent admin, GetTorrentInfo admin, GenerateTorrentInfoListing admin, GetCanonicalInfoHash admin, ChangePassword admin, BanUser registered, GetAboutPage registered, GetLicensePage registered, GetCategories registered, GetImageByUrl registered, GetPublicSettings registered, GetTags registered, AddTorrent registered, GetTorrent registered, GetTorrentInfo registered, GenerateTorrentInfoListing registered, GetCanonicalInfoHash registered, ChangePassword guest, GetAboutPage guest, GetLicensePage guest, GetCategories guest, GetPublicSettings guest, GetTags guest, GetTorrent guest, GetTorrentInfo guest, GenerateTorrentInfoListing guest, GetCanonicalInfoHash """ ``` For example, if you want to force users to log in to see the torrent list, you can remove the following line from the policy: ``` guest, GenerateTorrentInfoListing ``` **NOTICE**: This is an unstable feature. It will panic with wrong casbin configuration, invalid roles, etcetera. ACKs for top commit: josecelano: ACK c1a5c25 Tree-SHA512: 2bcc4bc69befd081aeef730768b4fa0f13bed14dc9c5f522ddbd0aa534b7679dc7adb870147e17120ff70af8e626b76b8662ea9d584ec4b1e11e9842f7fca8e4
2 parents a39ad21 + c1a5c25 commit 469922e

File tree

5 files changed

+145
-18
lines changed

5 files changed

+145
-18
lines changed

project-words.txt

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ binascii
99
btih
1010
buildx
1111
camino
12+
Casbin
1213
chrono
1314
clippy
1415
codecov
@@ -51,6 +52,7 @@ luckythelab
5152
mailcatcher
5253
mandelbrotset
5354
metainfo
55+
Mgmt
5456
migth
5557
nanos
5658
NCCA

src/app.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::config::validator::Validator;
1111
use crate::config::Configuration;
1212
use crate::databases::database;
1313
use crate::services::authentication::{DbUserAuthenticationRepository, JsonWebToken, Service};
14+
use crate::services::authorization::{CasbinConfiguration, CasbinEnforcer};
1415
use crate::services::category::{self, DbCategoryRepository};
1516
use crate::services::tag::{self, DbTagRepository};
1617
use crate::services::torrent::{
@@ -62,6 +63,8 @@ pub async fn run(configuration: Configuration, api_version: &Version) -> Running
6263
// From [net] config
6364
let config_bind_address = settings.net.bind_address;
6465
let opt_net_tsl = settings.net.tsl.clone();
66+
// Unstable config
67+
let unstable = settings.unstable.clone();
6568

6669
// IMPORTANT: drop settings before starting server to avoid read locks that
6770
// leads to requests hanging.
@@ -87,7 +90,19 @@ pub async fn run(configuration: Configuration, api_version: &Version) -> Running
8790
let torrent_tag_repository = Arc::new(DbTorrentTagRepository::new(database.clone()));
8891
let torrent_listing_generator = Arc::new(DbTorrentListingGenerator::new(database.clone()));
8992
let banned_user_list = Arc::new(DbBannedUserList::new(database.clone()));
90-
let casbin_enforcer = Arc::new(authorization::CasbinEnforcer::new().await);
93+
let casbin_enforcer = Arc::new(
94+
if let Some(casbin) = unstable
95+
.as_ref()
96+
.and_then(|u| u.auth.as_ref())
97+
.and_then(|auth| auth.casbin.as_ref())
98+
{
99+
println!("loading custom");
100+
CasbinEnforcer::with_configuration(CasbinConfiguration::new(&casbin.model, &casbin.policy)).await
101+
} else {
102+
println!("loading default");
103+
CasbinEnforcer::with_default_configuration().await
104+
},
105+
);
91106

92107
// Services
93108
let authorization_service = Arc::new(authorization::Service::new(user_repository.clone(), casbin_enforcer.clone()));

src/config/v2/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ pub mod net;
88
pub mod registration;
99
pub mod tracker;
1010
pub mod tracker_statistics_importer;
11+
pub mod unstable;
1112
pub mod website;
1213

1314
use logging::Logging;
1415
use registration::Registration;
1516
use serde::{Deserialize, Serialize};
17+
use unstable::Unstable;
1618

1719
use self::api::Api;
1820
use self::auth::{Auth, ClaimTokenPepper};
@@ -76,6 +78,10 @@ pub struct Settings {
7678
/// The tracker statistics importer job configuration.
7779
#[serde(default = "Settings::default_tracker_statistics_importer")]
7880
pub tracker_statistics_importer: TrackerStatisticsImporter,
81+
82+
/// The unstable configuration.
83+
#[serde(default = "Settings::default_unstable")]
84+
pub unstable: Option<Unstable>,
7985
}
8086

8187
impl Default for Settings {
@@ -93,6 +99,7 @@ impl Default for Settings {
9399
api: Self::default_api(),
94100
registration: Self::default_registration(),
95101
tracker_statistics_importer: Self::default_tracker_statistics_importer(),
102+
unstable: Self::default_unstable(),
96103
}
97104
}
98105
}
@@ -174,6 +181,10 @@ impl Settings {
174181
fn default_tracker_statistics_importer() -> TrackerStatisticsImporter {
175182
TrackerStatisticsImporter::default()
176183
}
184+
185+
fn default_unstable() -> Option<Unstable> {
186+
None
187+
}
177188
}
178189

179190
impl Validator for Settings {

src/config/v2/unstable.rs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
/// Unstable configuration options.
4+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5+
pub struct Unstable {
6+
/// The casbin configuration used for authorization.
7+
#[serde(default = "Unstable::default_auth")]
8+
pub auth: Option<Auth>,
9+
}
10+
11+
impl Default for Unstable {
12+
fn default() -> Self {
13+
Self {
14+
auth: Self::default_auth(),
15+
}
16+
}
17+
}
18+
19+
impl Unstable {
20+
fn default_auth() -> Option<Auth> {
21+
None
22+
}
23+
}
24+
25+
/// Unstable auth configuration options.
26+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
27+
pub struct Auth {
28+
/// The casbin configuration used for authorization.
29+
#[serde(default = "Auth::default_casbin")]
30+
pub casbin: Option<Casbin>,
31+
}
32+
33+
impl Default for Auth {
34+
fn default() -> Self {
35+
Self {
36+
casbin: Self::default_casbin(),
37+
}
38+
}
39+
}
40+
41+
impl Auth {
42+
fn default_casbin() -> Option<Casbin> {
43+
None
44+
}
45+
}
46+
47+
/// Authentication options.
48+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
49+
pub struct Casbin {
50+
/// The model. See <https://casbin.org>.
51+
pub model: String,
52+
53+
/// The policy. See <https://casbin.org>.
54+
pub policy: String,
55+
}

src/services/authorization.rs

+61-17
Original file line numberDiff line numberDiff line change
@@ -132,40 +132,84 @@ pub struct CasbinEnforcer {
132132
impl CasbinEnforcer {
133133
/// # Panics
134134
///
135-
/// It panics if the policy and/or model file cannot be loaded
136-
pub async fn new() -> Self {
137-
let casbin_configuration = CasbinConfiguration::new();
135+
/// Will panic if:
136+
///
137+
/// - The enforcer can't be created.
138+
/// - The policies can't be loaded.
139+
pub async fn with_default_configuration() -> Self {
140+
let casbin_configuration = CasbinConfiguration::default();
138141

139-
let model = DefaultModel::from_str(&casbin_configuration.model)
142+
let mut enforcer = Enforcer::new(casbin_configuration.default_model().await, ())
140143
.await
141-
.expect("Error loading the model");
144+
.expect("Error creating the enforcer");
142145

143-
// Converts the policy from a string type to a vector
144-
let policy = casbin_configuration
145-
.policy
146-
.lines()
147-
.filter(|line| !line.trim().is_empty())
148-
.map(|line| line.split(',').map(|s| s.trim().to_owned()).collect::<Vec<String>>())
149-
.collect();
146+
enforcer
147+
.add_policies(casbin_configuration.policy_lines())
148+
.await
149+
.expect("Error loading the policy");
150+
151+
let enforcer = Arc::new(RwLock::new(enforcer));
152+
153+
Self { enforcer }
154+
}
150155

151-
let mut enforcer = Enforcer::new(model, ()).await.expect("Error creating the enforcer");
156+
/// # Panics
157+
///
158+
/// Will panic if:
159+
///
160+
/// - The enforcer can't be created.
161+
/// - The policies can't be loaded.
162+
pub async fn with_configuration(casbin_configuration: CasbinConfiguration) -> Self {
163+
let mut enforcer = Enforcer::new(casbin_configuration.default_model().await, ())
164+
.await
165+
.expect("Error creating the enforcer");
152166

153-
enforcer.add_policies(policy).await.expect("Error loading the policy");
167+
enforcer
168+
.add_policies(casbin_configuration.policy_lines())
169+
.await
170+
.expect("Error loading the policy");
154171

155172
let enforcer = Arc::new(RwLock::new(enforcer));
156173

157174
Self { enforcer }
158175
}
159176
}
177+
160178
#[allow(dead_code)]
161-
struct CasbinConfiguration {
179+
pub struct CasbinConfiguration {
162180
model: String,
163181
policy: String,
164182
}
165183

166184
impl CasbinConfiguration {
167-
pub fn new() -> Self {
168-
CasbinConfiguration {
185+
#[must_use]
186+
pub fn new(model: &str, policy: &str) -> Self {
187+
Self {
188+
model: model.to_owned(),
189+
policy: policy.to_owned(),
190+
}
191+
}
192+
193+
/// # Panics
194+
///
195+
/// It panics if the model cannot be loaded.
196+
async fn default_model(&self) -> DefaultModel {
197+
DefaultModel::from_str(&self.model).await.expect("Error loading the model")
198+
}
199+
200+
/// Converts the policy from a string type to a vector.
201+
fn policy_lines(&self) -> Vec<Vec<String>> {
202+
self.policy
203+
.lines()
204+
.filter(|line| !line.trim().is_empty())
205+
.map(|line| line.split(',').map(|s| s.trim().to_owned()).collect::<Vec<String>>())
206+
.collect()
207+
}
208+
}
209+
210+
impl Default for CasbinConfiguration {
211+
fn default() -> Self {
212+
Self {
169213
model: String::from(
170214
"
171215
[request_definition]

0 commit comments

Comments
 (0)