openauth-plugins 0.0.3

Official OpenAuth plugin modules.
Documentation
use ::http::StatusCode;
use openauth_core::error::OpenAuthError;

use crate::organization::http;
use crate::organization::models::Member;
use crate::organization::options::OrganizationOptions;
use crate::organization::permissions::is_known_static_role;
use crate::organization::store::OrganizationStore;

pub(super) async fn require_session(
    context: &openauth_core::context::AuthContext,
    request: &openauth_core::api::ApiRequest,
    store: &OrganizationStore<'_>,
) -> Result<crate::organization::http::CurrentSession, OpenAuthError> {
    match http::current_session(context, request, store).await? {
        Some(session) => Ok(session),
        None => Err(OpenAuthError::Api("UNAUTHORIZED".to_owned())),
    }
}

pub(super) fn query_param(request: &openauth_core::api::ApiRequest, name: &str) -> Option<String> {
    request.uri().query().and_then(|query| {
        query.split('&').find_map(|pair| {
            let (key, value) = pair.split_once('=').unwrap_or((pair, ""));
            (key == name).then(|| value.to_owned())
        })
    })
}

pub(super) fn valid_email(email: &str) -> bool {
    let Some((local, domain)) = email.split_once('@') else {
        return false;
    };
    !local.is_empty()
        && !domain.is_empty()
        && domain.contains('.')
        && !domain.starts_with('.')
        && !domain.ends_with('.')
        && !email.contains(char::is_whitespace)
}

pub(super) async fn roles_exist(
    store: &OrganizationStore<'_>,
    organization_id: &str,
    roles: &str,
    options: &OrganizationOptions,
) -> Result<bool, OpenAuthError> {
    for role in roles
        .split(',')
        .map(str::trim)
        .filter(|role| !role.is_empty())
    {
        if is_known_static_role(role, options) {
            continue;
        }
        if !options.dynamic_access_control.enabled {
            return Ok(false);
        }
        if store
            .organization_role_by_name(organization_id, role)
            .await?
            .is_none()
        {
            return Ok(false);
        }
    }
    Ok(true)
}

pub(super) async fn is_last_owner(
    store: &OrganizationStore<'_>,
    organization_id: &str,
    member: &Member,
    options: &OrganizationOptions,
) -> Result<bool, OpenAuthError> {
    Ok(member
        .role
        .split(',')
        .any(|role| role.trim() == options.creator_role)
        && owners(store, organization_id, options).await? <= 1)
}

pub(super) async fn owners(
    store: &OrganizationStore<'_>,
    organization_id: &str,
    options: &OrganizationOptions,
) -> Result<usize, OpenAuthError> {
    Ok(store
        .members(organization_id)
        .await?
        .iter()
        .filter(|member| {
            member
                .role
                .split(',')
                .any(|role| role.trim() == options.creator_role)
        })
        .count())
}

pub(super) fn invalid_body() -> Result<openauth_core::api::ApiResponse, OpenAuthError> {
    http::error(
        StatusCode::BAD_REQUEST,
        "INVALID_REQUEST_BODY",
        "Invalid request body",
    )
}