use std::sync::Once;
pub use entities::Level;
use thiserror::Error;
pub use token::Token;
pub mod access;
pub mod auth;
pub mod base;
pub mod check;
pub mod clear;
pub mod entities;
pub(crate) mod file;
pub mod issue;
#[cfg(feature = "jwks")]
pub mod jwks;
pub mod reset;
pub mod signin;
pub mod signup;
pub mod token;
pub mod verify;
pub use self::auth::*;
pub use self::entities::*;
use crate::catalog;
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum Error {
#[error("Invalid role '{0}'")]
InvalidRole(String),
#[error("Not enough permissions to perform this action")]
NotAllowed {
actor: String,
action: String,
resource: String,
},
}
fn algorithm_to_jwt_algorithm(alg: catalog::Algorithm) -> jsonwebtoken::Algorithm {
match alg {
catalog::Algorithm::Hs256 => jsonwebtoken::Algorithm::HS256,
catalog::Algorithm::Hs384 => jsonwebtoken::Algorithm::HS384,
catalog::Algorithm::Hs512 => jsonwebtoken::Algorithm::HS512,
catalog::Algorithm::EdDSA => jsonwebtoken::Algorithm::EdDSA,
catalog::Algorithm::Es256 => jsonwebtoken::Algorithm::ES256,
catalog::Algorithm::Es384 => jsonwebtoken::Algorithm::ES384,
catalog::Algorithm::Es512 => {
static ES512_WARN: Once = Once::new();
ES512_WARN.call_once(|| {
warn!("ES512 is not currently supported by the underlying cryptography library and will fall back to ES384. Please update your access definition to use ES384 or another supported algorithm.");
});
jsonwebtoken::Algorithm::ES384
}
catalog::Algorithm::Ps256 => jsonwebtoken::Algorithm::PS256,
catalog::Algorithm::Ps384 => jsonwebtoken::Algorithm::PS384,
catalog::Algorithm::Ps512 => jsonwebtoken::Algorithm::PS512,
catalog::Algorithm::Rs256 => jsonwebtoken::Algorithm::RS256,
catalog::Algorithm::Rs384 => jsonwebtoken::Algorithm::RS384,
catalog::Algorithm::Rs512 => jsonwebtoken::Algorithm::RS512,
}
}
pub fn is_expired_token_error(e: &anyhow::Error) -> bool {
e.downcast_ref::<crate::err::Error>()
.is_some_and(|err| matches!(err, crate::err::Error::ExpiredToken))
}
pub fn is_allowed_check(actor: &Actor, action: &Action, resource: &Resource) -> bool {
match action {
Action::View => resource.level().sublevel_of(actor.level()),
Action::Edit => {
if actor.has_role(Role::Owner) {
resource.level().sublevel_of(actor.level())
} else if actor.has_role(Role::Editor) {
matches!(
resource.kind(),
ResourceKind::Namespace
| ResourceKind::Database
| ResourceKind::Record
| ResourceKind::Table
| ResourceKind::Document
| ResourceKind::Option
| ResourceKind::Function
| ResourceKind::Analyzer
| ResourceKind::Parameter
| ResourceKind::Event
| ResourceKind::Field
| ResourceKind::Index
) && resource.level().sublevel_of(actor.level())
} else {
false
}
}
}
}
pub fn is_allowed(actor: &Actor, action: &Action, resource: &Resource) -> Result<(), Error> {
if !is_allowed_check(actor, action, resource) {
let err = Error::NotAllowed {
actor: actor.to_string(),
action: action.to_string(),
resource: format!("{}", resource),
};
trace!("{}", err);
return Err(err);
}
Ok(())
}