#[cfg(feature = "diesel")]
mod diesel;
mod error;
use crate::error::InvalidStateError;
#[cfg(feature = "diesel")]
pub use self::diesel::DieselRoleBasedAuthorizationStore;
pub use error::RoleBasedAuthorizationStoreError;
pub const ADMIN_ROLE_ID: &str = "admin";
#[derive(Clone)]
pub struct Role {
id: String,
display_name: String,
permissions: Vec<String>,
}
impl Role {
pub fn id(&self) -> &str {
&self.id
}
pub fn display_name(&self) -> &str {
&self.display_name
}
pub fn permissions(&self) -> &[String] {
&self.permissions
}
pub fn into_update_builder(self) -> RoleUpdateBuilder {
RoleUpdateBuilder {
id: self.id,
display_name: Some(self.display_name),
permissions: self.permissions,
}
}
pub fn into_parts(self) -> (String, String, Vec<String>) {
(self.id, self.display_name, self.permissions)
}
}
#[derive(Default)]
pub struct RoleBuilder {
id: Option<String>,
display_name: Option<String>,
permissions: Vec<String>,
}
impl RoleBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn with_id(mut self, id: String) -> Self {
self.id = Some(id);
self
}
pub fn with_display_name(mut self, display_name: String) -> Self {
self.display_name = Some(display_name);
self
}
pub fn with_permissions(mut self, permissions: Vec<String>) -> Self {
self.permissions = permissions;
self
}
pub fn build(self) -> Result<Role, InvalidStateError> {
if self.permissions.is_empty() {
return Err(InvalidStateError::with_message(
"A role requires at least one permission".into(),
));
}
let id = self
.id
.ok_or_else(|| InvalidStateError::with_message("A role requires an id field".into()))?;
if id.is_empty() {
return Err(InvalidStateError::with_message(
"A role requires a non-empty id field".into(),
));
}
let display_name = self.display_name.ok_or_else(|| {
InvalidStateError::with_message("A role requires a display_name field".into())
})?;
if display_name.is_empty() {
return Err(InvalidStateError::with_message(
"A role requires a non-empty display_name field".into(),
));
}
Ok(Role {
id,
display_name,
permissions: self.permissions,
})
}
}
pub struct RoleUpdateBuilder {
id: String,
display_name: Option<String>,
permissions: Vec<String>,
}
impl RoleUpdateBuilder {
pub fn with_display_name(mut self, display_name: String) -> Self {
self.display_name = Some(display_name);
self
}
pub fn with_permissions(mut self, permissions: Vec<String>) -> Self {
self.permissions = permissions;
self
}
pub fn build(self) -> Result<Role, InvalidStateError> {
if self.permissions.is_empty() {
return Err(InvalidStateError::with_message(
"A role requires at least one permission".into(),
));
}
let display_name = self.display_name.ok_or_else(|| {
InvalidStateError::with_message("A role requires a display_name field".into())
})?;
if display_name.is_empty() {
return Err(InvalidStateError::with_message(
"A role requires a non-empty display_name field".into(),
));
}
Ok(Role {
id: self.id,
display_name,
permissions: self.permissions,
})
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Identity {
Key(String),
User(String),
}
impl From<&crate::rest_api::auth::identity::Identity> for Option<Identity> {
fn from(identity: &crate::rest_api::auth::identity::Identity) -> Self {
match identity {
crate::rest_api::auth::identity::Identity::Custom(_) => None,
crate::rest_api::auth::identity::Identity::Key(key) => {
Some(Identity::Key(key.to_string()))
}
crate::rest_api::auth::identity::Identity::User(user_id) => {
Some(Identity::User(user_id.to_string()))
}
}
}
}
#[derive(Clone)]
pub struct Assignment {
identity: Identity,
roles: Vec<String>,
}
impl Assignment {
pub fn identity(&self) -> &Identity {
&self.identity
}
pub fn roles(&self) -> &[String] {
&self.roles
}
pub fn into_update_builder(self) -> AssignmentUpdateBuilder {
let Assignment { identity, roles } = self;
AssignmentUpdateBuilder { identity, roles }
}
pub fn into_parts(self) -> (Identity, Vec<String>) {
(self.identity, self.roles)
}
}
#[derive(Default)]
pub struct AssignmentBuilder {
identity: Option<Identity>,
roles: Vec<String>,
}
impl AssignmentBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn with_identity(mut self, identity: Identity) -> Self {
self.identity = Some(identity);
self
}
pub fn with_roles(mut self, roles: Vec<String>) -> Self {
self.roles = roles;
self
}
pub fn build(self) -> Result<Assignment, InvalidStateError> {
if self.roles.is_empty() {
return Err(InvalidStateError::with_message(
"An assignment requires at least one role".into(),
));
}
Ok(Assignment {
identity: self.identity.ok_or_else(|| {
InvalidStateError::with_message("An assignment requires an identity field".into())
})?,
roles: self.roles,
})
}
}
pub struct AssignmentUpdateBuilder {
identity: Identity,
roles: Vec<String>,
}
impl AssignmentUpdateBuilder {
pub fn with_roles(mut self, roles: Vec<String>) -> Self {
self.roles = roles;
self
}
pub fn build(self) -> Result<Assignment, InvalidStateError> {
if self.roles.is_empty() {
return Err(InvalidStateError::with_message(
"An assignment requires at least one role".into(),
));
}
Ok(Assignment {
identity: self.identity,
roles: self.roles,
})
}
}
pub trait RoleBasedAuthorizationStore: Send + Sync {
fn get_role(&self, id: &str) -> Result<Option<Role>, RoleBasedAuthorizationStoreError>;
fn list_roles(
&self,
) -> Result<Box<dyn ExactSizeIterator<Item = Role>>, RoleBasedAuthorizationStoreError>;
fn add_role(&self, role: Role) -> Result<(), RoleBasedAuthorizationStoreError>;
fn update_role(&self, role: Role) -> Result<(), RoleBasedAuthorizationStoreError>;
fn remove_role(&self, role_id: &str) -> Result<(), RoleBasedAuthorizationStoreError>;
fn get_assignment(
&self,
identity: &Identity,
) -> Result<Option<Assignment>, RoleBasedAuthorizationStoreError>;
fn get_assigned_roles(
&self,
identity: &Identity,
) -> Result<Box<dyn ExactSizeIterator<Item = Role>>, RoleBasedAuthorizationStoreError>;
fn list_assignments(
&self,
) -> Result<Box<dyn ExactSizeIterator<Item = Assignment>>, RoleBasedAuthorizationStoreError>;
fn add_assignment(
&self,
assignment: Assignment,
) -> Result<(), RoleBasedAuthorizationStoreError>;
fn update_assignment(
&self,
assignment: Assignment,
) -> Result<(), RoleBasedAuthorizationStoreError>;
fn remove_assignment(
&self,
identity: &Identity,
) -> Result<(), RoleBasedAuthorizationStoreError>;
fn clone_box(&self) -> Box<dyn RoleBasedAuthorizationStore>;
}
impl Clone for Box<dyn RoleBasedAuthorizationStore> {
fn clone(&self) -> Self {
self.clone_box()
}
}