cedarling 0.0.8

The Cedarling: a high-performance local authorization service powered by the Rust Cedar Engine.
Documentation
// This software is available under the Apache-2.0 license.
// See https://www.apache.org/licenses/LICENSE-2.0.txt for full text.
//
// Copyright (c) 2024, Gluu, Inc.

use cedar_policy::{ContextJsonError, ParseErrors, RequestValidationError};
use serde_json::Error as SerdeJsonError;

// Re-export commonly used error types
use crate::entity_builder::MultiIssuerEntityError;
use crate::entity_builder::{BuildEntityError, BuildUnsignedEntityError};
use crate::jwt::JwtProcessingError;
use cedar_policy::entities_errors::EntitiesError;

/// Error type for multi-issuer validation
#[derive(Debug, thiserror::Error)]
pub enum MultiIssuerValidationError {
    #[error("Token input validation failed: {0}")]
    TokenInput(#[from] TokenInputError),

    #[error("Empty token array")]
    EmptyTokenArray,

    #[error("Could not validate tokens.")]
    TokenValidationFailed,

    #[error("Invalid JSON in context field")]
    InvalidContextJson,

    #[error("Missing issuer claim in JWT")]
    MissingIssuer,

    #[error("JWT claims are not a JSON object: {0}")]
    InvalidClaims(&'static str),
}

/// Error type for token input validation
#[derive(Debug, thiserror::Error)]
pub enum TokenInputError {
    #[error("Empty mapping string")]
    EmptyMapping,

    #[error("Empty payload")]
    EmptyPayload,
}

/// Error type for Authorization Service
#[derive(thiserror::Error, Debug)]
pub enum AuthorizeError {
    /// Error encountered while processing JWT token data
    #[error(transparent)]
    ProcessTokens(#[from] JwtProcessingError),
    /// Error encountered while parsing Action to `EntityUid`
    #[error("could not parse action: {0}")]
    Action(Box<ParseErrors>),
    /// Error encountered while parsing an entity type name or action identifier
    #[error("could not parse identifier: {0}")]
    IdentifierParsing(Box<ParseErrors>),
    /// Error encountered while validating context according to the schema
    #[error("could not create context: {0}")]
    CreateContext(#[from] Box<ContextJsonError>),
    /// Error encountered while creating [`cedar_policy::Request`] for entity principal
    #[error(transparent)]
    InvalidPrincipal(#[from] Box<InvalidPrincipalError>),
    /// Error encountered while validating the Cedar request
    #[error("request validation error: {0}")]
    RequestValidation(#[from] Box<RequestValidationError>),
    /// Error encountered while checking if the Entities adhere to the schema
    #[error("failed to validate Cedar entities: {0:?}")]
    ValidateEntities(#[from] Box<EntitiesError>),
    /// Error encountered while parsing all entities to json for logging
    #[error("could not convert entities to json: {0}")]
    EntitiesToJson(SerdeJsonError),
    /// Error encountered while building the context for the request
    #[error("Failed to build context: {0}")]
    BuildContext(#[from] BuildContextError),
    /// Error encountered while building Cedar Entities
    #[error(transparent)]
    BuildEntity(#[from] BuildEntityError),
    #[error("failed to build role entities for unsigned request: {0}")]
    /// Error encountered while building Role entity in an unsigned request
    BuildUnsignedRoleEntity(#[from] BuildUnsignedEntityError),
    /// Error encountered while validating multi-issuer request
    #[error(transparent)]
    MultiIssuerValidation(#[from] MultiIssuerValidationError),
    /// Error encountered while building multi-issuer entities
    #[error("Multi-issuer entity building failed: {0}")]
    MultiIssuerEntity(MultiIssuerEntityError),
}

impl From<ParseErrors> for AuthorizeError {
    fn from(err: ParseErrors) -> Self {
        Self::Action(Box::new(err))
    }
}

impl From<InvalidPrincipalError> for AuthorizeError {
    fn from(err: InvalidPrincipalError) -> Self {
        Self::InvalidPrincipal(Box::new(err))
    }
}

/// Error type for building context
#[derive(Debug, thiserror::Error)]
pub enum BuildContextError {
    /// Error encountered while validating context according to the schema
    #[error("failed to merge JSON objects due to conflicting keys: {0}")]
    KeyConflict(String),
    /// Error encountered while deserializing the Context from JSON
    #[error(transparent)]
    DeserializeFromJson(#[from] Box<ContextJsonError>),
    /// Error encountered if the action being used as the reference to build the Context
    /// is not in the schema
    #[error("failed to find the action `{0}` in the schema")]
    UnknownAction(String),
    /// Error encountered while building entity references in the Context
    #[error("failed to build entity reference for `{0}` since an entity id was not provided")]
    MissingEntityId(String),
    #[error("invalid action context type: {0}. expected: {1}")]
    InvalidKind(String, String),
    #[error("failed to parse the entity name `{0}`: {1}")]
    ParseEntityName(String, Box<ParseErrors>),
    /// Error encountered while creating Cedar context
    #[error("failed to create Cedar context: {0}")]
    ContextCreation(String),
}

impl From<ContextJsonError> for BuildContextError {
    fn from(err: ContextJsonError) -> Self {
        BuildContextError::DeserializeFromJson(Box::new(err))
    }
}

/// Error for invalid principal in authorization request
#[derive(Debug, derive_more::Error, derive_more::Display)]
#[display("The request for `{principal}` does not conform to the schema: {err}")]
pub struct InvalidPrincipalError {
    /// Principal [`EntityUid`] value used for authorization request
    pub principal: cedar_policy::EntityUid,
    /// Error value
    pub err: RequestValidationError,
}

impl InvalidPrincipalError {
    pub fn new(principal: &cedar_policy::EntityUid, err: RequestValidationError) -> Self {
        InvalidPrincipalError {
            principal: principal.clone(),
            err,
        }
    }
}