use std::hash::Hash;
use crate::{
domain::{
DomainError, ObjectRef, ObjectType, RelationName, Relationship, SubjectRef, SubjectType,
},
revision::Consistency,
};
#[cfg_attr(
feature = "serde",
derive(serde::Serialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Object {
pub namespace: String,
pub id: String,
}
impl Object {
#[must_use]
pub fn new(namespace: impl Into<String>, id: impl Into<String>) -> Self {
Self {
namespace: namespace.into(),
id: id.into(),
}
}
pub fn validate(&self) -> Result<(), DomainError> {
ObjectRef::try_from(self).map(drop)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Object {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct ObjectSerde {
namespace: String,
id: String,
}
let value = ObjectSerde::deserialize(deserializer)?;
let object = Self {
namespace: value.namespace,
id: value.id,
};
object.validate().map_err(serde::de::Error::custom)?;
Ok(object)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Relation(pub String);
impl Relation {
#[must_use]
pub fn new(name: impl Into<String>) -> Self {
Self(name.into())
}
pub fn validate(&self) -> Result<(), DomainError> {
RelationName::try_from(self).map(drop)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Relation {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let relation = Self(<String as serde::Deserialize>::deserialize(deserializer)?);
relation.validate().map_err(serde::de::Error::custom)?;
Ok(relation)
}
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize),
serde(rename_all = "camelCase", tag = "type", content = "value")
)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum User {
UserId(String),
Userset(Object, Relation),
}
impl User {
#[must_use]
pub fn user_id(id: impl Into<String>) -> Self {
Self::UserId(id.into())
}
#[must_use]
pub fn userset(object: Object, relation: Relation) -> Self {
Self::Userset(object, relation)
}
pub fn validate(&self) -> Result<(), DomainError> {
SubjectRef::try_from(self).map(drop)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for User {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase", tag = "type", content = "value")]
enum UserSerde {
UserId(String),
Userset(Object, Relation),
}
let user = match UserSerde::deserialize(deserializer)? {
UserSerde::UserId(id) => Self::UserId(id),
UserSerde::Userset(object, relation) => Self::Userset(object, relation),
};
user.validate().map_err(serde::de::Error::custom)?;
Ok(user)
}
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RelationTuple {
pub object: Object,
pub relation: Relation,
pub user: User,
}
impl RelationTuple {
#[must_use]
pub fn new(object: Object, relation: Relation, user: User) -> Self {
Self {
object,
relation,
user,
}
}
pub fn validate(&self) -> Result<(), DomainError> {
Relationship::try_from(self).map(drop)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for RelationTuple {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct RelationTupleSerde {
object: Object,
relation: Relation,
user: User,
}
let value = RelationTupleSerde::deserialize(deserializer)?;
let tuple = Self {
object: value.object,
relation: value.relation,
user: value.user,
};
tuple.validate().map_err(serde::de::Error::custom)?;
Ok(tuple)
}
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CheckRequest {
pub object: Object,
pub relation: Relation,
pub user: User,
pub consistency: Consistency,
}
impl CheckRequest {
#[must_use]
pub fn new(object: Object, relation: Relation, user: User, consistency: Consistency) -> Self {
Self {
object,
relation,
user,
consistency,
}
}
pub fn validate(&self) -> Result<(), DomainError> {
self.object.validate()?;
self.relation.validate()?;
self.user.validate()
}
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CheckResponse {
pub allowed: bool,
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExpandRequest {
pub object: Object,
pub relation: Relation,
pub consistency: Consistency,
}
impl ExpandRequest {
#[must_use]
pub fn new(object: Object, relation: Relation, consistency: Consistency) -> Self {
Self {
object,
relation,
consistency,
}
}
pub fn validate(&self) -> Result<(), DomainError> {
self.object.validate()?;
self.relation.validate()
}
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExpandResponse {
pub expanded: ExpandedUserset,
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LookupResourcesRequest {
pub subject: User,
pub permission: Relation,
pub resource_type: String,
}
impl LookupResourcesRequest {
#[must_use]
pub fn new(subject: User, permission: Relation, resource_type: impl Into<String>) -> Self {
Self {
subject,
permission,
resource_type: resource_type.into(),
}
}
pub fn validate(&self) -> Result<(), DomainError> {
self.subject.validate()?;
self.permission.validate()?;
ObjectType::try_from(self.resource_type.as_str()).map(drop)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for LookupResourcesRequest {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct LookupResourcesRequestSerde {
subject: User,
permission: Relation,
resource_type: String,
}
let value = LookupResourcesRequestSerde::deserialize(deserializer)?;
let request = Self {
subject: value.subject,
permission: value.permission,
resource_type: value.resource_type,
};
request.validate().map_err(serde::de::Error::custom)?;
Ok(request)
}
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LookupResources {
pub resources: Vec<Object>,
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LookupSubjectsRequest {
pub resource: Object,
pub permission: Relation,
pub subject_type: String,
}
impl LookupSubjectsRequest {
#[must_use]
pub fn new(resource: Object, permission: Relation, subject_type: impl Into<String>) -> Self {
Self {
resource,
permission,
subject_type: subject_type.into(),
}
}
pub fn validate(&self) -> Result<(), DomainError> {
self.resource.validate()?;
self.permission.validate()?;
SubjectType::try_from(self.subject_type.as_str()).map(drop)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for LookupSubjectsRequest {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct LookupSubjectsRequestSerde {
resource: Object,
permission: Relation,
subject_type: String,
}
let value = LookupSubjectsRequestSerde::deserialize(deserializer)?;
let request = Self {
resource: value.resource,
permission: value.permission,
subject_type: value.subject_type,
};
request.validate().map_err(serde::de::Error::custom)?;
Ok(request)
}
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LookupSubjects {
pub subjects: Vec<User>,
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LookupPermissionsRequest {
pub subject: User,
pub resource: Object,
pub consistency: Consistency,
}
impl LookupPermissionsRequest {
#[must_use]
pub fn new(subject: User, resource: Object, consistency: Consistency) -> Self {
Self {
subject,
resource,
consistency,
}
}
pub fn validate(&self) -> Result<(), DomainError> {
self.subject.validate()?;
self.resource.validate()
}
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LookupPermissions {
pub permissions: Vec<Relation>,
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LookupObjectPermissionsRequest {
pub resource: Object,
pub subject_type: String,
pub consistency: Consistency,
}
impl LookupObjectPermissionsRequest {
#[must_use]
pub fn new(
resource: Object,
subject_type: impl Into<String>,
consistency: Consistency,
) -> Self {
Self {
resource,
subject_type: subject_type.into(),
consistency,
}
}
pub fn validate(&self) -> Result<(), DomainError> {
self.resource.validate()?;
SubjectType::try_from(self.subject_type.as_str()).map(drop)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for LookupObjectPermissionsRequest {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
struct LookupObjectPermissionsRequestSerde {
resource: Object,
subject_type: String,
consistency: Consistency,
}
let value = LookupObjectPermissionsRequestSerde::deserialize(deserializer)?;
let request = Self {
resource: value.resource,
subject_type: value.subject_type,
consistency: value.consistency,
};
request.validate().map_err(serde::de::Error::custom)?;
Ok(request)
}
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LookupObjectPermissions {
pub permissions: Vec<PermissionSubjects>,
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PermissionSubjects {
pub permission: Relation,
pub subjects: Vec<User>,
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone, Default)]
pub struct NamespaceConfig {
pub name: String,
pub relations: std::collections::HashMap<Relation, RelationConfig>,
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase", deny_unknown_fields)
)]
#[derive(Debug, Clone)]
pub struct RelationConfig {
pub name: Relation,
pub userset_rewrite: Option<UsersetExpression>,
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase")
)]
#[derive(Debug, Clone)]
pub enum UsersetExpression {
This,
ComputedUserset {
relation: Relation,
},
TupleToUserset {
tupleset_relation: Relation,
computed_userset_relation: Relation,
},
Union(Vec<UsersetExpression>),
Intersection(Vec<UsersetExpression>),
Exclusion {
base: Box<UsersetExpression>,
exclude: Box<UsersetExpression>,
},
}
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(rename_all = "camelCase")
)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExpandedUserset {
User(String),
Userset(Object, Relation),
Union(Vec<ExpandedUserset>),
Intersection(Vec<ExpandedUserset>),
Exclusion {
base: Box<ExpandedUserset>,
exclude: Box<ExpandedUserset>,
},
}