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

reworking roles and permissions #20

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
101 changes: 97 additions & 4 deletions src/admin/v1/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,28 @@ pub struct Members {
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct MemberEntry {
pub role: Role,
pub roles: Roles,
}

#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
pub struct Roles(pub Vec<Role>);

#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum Role {
/// Allow everything, including changing members
/// All the permissions except owning the app.
/// Edit members for the applications.
/// Read and write details (devices as well).
/// Publish and subscribe.
Admin,
/// Allow reading and writing, but not changing members.
/// Allow reading and writing devices and application details.
Manager,
/// Allow reading only.
/// Allow reading only of app and devices details.
Reader,
/// Allow consuming app events.
Subscriber,
/// Allow publishing command to the apps
Publisher,
}

impl Display for Role {
Expand All @@ -41,6 +51,8 @@ impl Display for Role {
Self::Admin => write!(f, "Administrator"),
Self::Manager => write!(f, "Manager"),
Self::Reader => write!(f, "Reader"),
Self::Subscriber => write!(f, "Subscriber"),
Self::Publisher => write!(f, "Publisher"),
}
}
}
Expand All @@ -53,7 +65,88 @@ impl FromStr for Role {
"Admin" | "admin" => Ok(Role::Admin),
"Manager" | "manager" => Ok(Role::Manager),
"Reader" | "reader" => Ok(Role::Reader),
"Subscriber" | "subscriber" => Ok(Role::Subscriber),
"Publisher" | "publisher" => Ok(Role::Publisher),
_ => Err(()),
}
}
}

impl Roles {
pub fn contains(&self, role: &Role) -> bool {
if self.0.contains(&Role::Admin) {
return true
} else {
match role {
Role::Admin => self.0.contains(&Role::Admin),
Role::Manager => self.0.contains(&Role::Manager),
Role::Reader => self.0.contains(&Role::Manager) || self.0.contains(&Role::Reader),
Role::Publisher => self.0.contains(&Role::Publisher),
Role::Subscriber => self.0.contains(&Role::Subscriber),
}
}
}
}


#[cfg(test)]
mod tests {
use super::*;

#[test]
fn role_admin_include_all_roles() {

let roles = Roles(vec![Role::Admin]);

assert!(roles.contains(&Role::Publisher));
assert!(roles.contains(&Role::Subscriber));
assert!(roles.contains(&Role::Reader));
assert!(roles.contains(&Role::Manager));
assert!(roles.contains(&Role::Admin));
}

#[test]
fn role_manager_include_reader() {

let roles = Roles(vec![Role::Manager]);

assert!(roles.contains(&Role::Reader));
assert!(roles.contains(&Role::Manager));
assert!(roles.contains(&Role::Manager));
}


#[test]
fn role_reader_include_reader() {

let roles = Roles(vec![Role::Reader]);

assert!(roles.contains(&Role::Reader));
assert_eq!(roles.contains(&Role::Manager), false);
assert_eq!(roles.contains(&Role::Admin), false);
}

#[test]
fn roles_publisher() {

let roles = Roles(vec![Role::Publisher]);

assert!(roles.contains(&Role::Publisher));
assert_eq!(roles.contains(&Role::Manager), false);
assert_eq!(roles.contains(&Role::Admin), false);
assert_eq!(roles.contains(&Role::Reader), false);
assert_eq!(roles.contains(&Role::Subscriber), false);
}

#[test]
fn roles_subscriber() {

let roles = Roles(vec![Role::Subscriber]);

assert!(roles.contains(&Role::Subscriber));
assert_eq!(roles.contains(&Role::Manager), false);
assert_eq!(roles.contains(&Role::Admin), false);
assert_eq!(roles.contains(&Role::Reader), false);
assert_eq!(roles.contains(&Role::Publisher), false);
}
}
23 changes: 23 additions & 0 deletions src/tokens/v1/data.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
use crate::admin::v1::Roles;
use crate::user::v1::authz::TokenPermission;
use chrono::{DateTime, Utc};
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
pub struct AccessTokenClaims {
/// Allow creating applications
pub create: bool,
/// Claims are defined for each application
pub applications: IndexMap<String, Roles>,
/// Access Tokens permissions
pub tokens: Vec<TokenPermission>,
}

#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct AccessToken {
/// The creation date of the access token
Expand All @@ -10,6 +23,8 @@ pub struct AccessToken {
/// The access token description
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default)]
pub claims: Option<AccessTokenClaims>,
}

#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
Expand All @@ -21,3 +36,11 @@ pub struct CreatedAccessToken {
#[serde(default)]
pub prefix: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AccessTokenCreationOptions {
pub description: Option<String>,
/// If no claims are provided, the access token
/// will have the same permissions as its owner
pub claims: Option<AccessTokenClaims>,
}
33 changes: 29 additions & 4 deletions src/user/v1/data/authz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,35 @@ use serde::{Deserialize, Serialize};

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Permission {
Owner,
Admin,
Write,
Read,
Device(DevicePermission),
App(ApplicationPermission),
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum TokenPermission {
Create, // Create new tokens
List, // Read all tokens for this user
Delete, // Delete any token for this user
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum DevicePermission {
Create, // Create a resource
Delete, // delete a resource
Write, // Write resource details
Read, // read resource details
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ApplicationPermission {
Create, // Create a resource
Delete, // delete a resource
Write, // Write resource details
Read, // read resource details
Transfer, // Transfer app to another owner
Subscribe, // consume app events
Command, // publish commands to app
Members, // Members operations : read and write
}

impl fmt::Display for Permission {
Expand Down
4 changes: 4 additions & 0 deletions src/user/v1/data/user.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Structures to work with users and identities.

use crate::tokens::v1::AccessTokenClaims;
use serde::{Deserialize, Serialize};

/// Details on an authenticated user.
Expand All @@ -9,4 +10,7 @@ pub struct UserDetails {
pub user_id: String,
/// Granted roles.
pub roles: Vec<String>,
/// Limited Authorization claims.
/// This will be Some only in the context of a PAT authentication
pub claims: Option<AccessTokenClaims>,
}