rustauth-plugins 0.2.0

Official RustAuth plugin modules.
Documentation
use http::{Method, StatusCode};
use serde::{Deserialize, Serialize};

use super::{
    body, config_id_matches, current_identity, endpoint, error, json, SharedConfigurations,
};
use crate::api_key::errors;
use crate::api_key::options::ApiKeyReference;
use crate::api_key::organization::{ensure_organization_permission, owns_user_key, ApiKeyAction};
use crate::api_key::storage::ApiKeyStore;

#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct DeleteApiKeyRequest {
    pub key_id: String,
    pub config_id: Option<String>,
}

#[derive(Debug, Clone, Serialize)]
struct DeleteApiKeyResponse {
    success: bool,
}

pub fn delete_endpoint(
    configurations: SharedConfigurations,
) -> rustauth_core::api::AsyncAuthEndpoint {
    endpoint(
        "/api-key/delete",
        Method::POST,
        configurations,
        |context, request, configurations| async move {
            let input: DeleteApiKeyRequest = body(&request)?;
            let options = configurations.resolve(input.config_id.as_deref())?;
            let Some(identity) = current_identity(&context, &request).await? else {
                return error(StatusCode::UNAUTHORIZED, errors::UNAUTHORIZED_SESSION);
            };
            let store = ApiKeyStore::new(&context, &options);
            let Some(api_key) = store.get_by_id(&input.key_id).await? else {
                return error(StatusCode::NOT_FOUND, errors::KEY_NOT_FOUND);
            };
            let expected_config_id = options.config_id.as_deref().unwrap_or("default");
            if !config_id_matches(&api_key.config_id, expected_config_id) {
                return error(StatusCode::NOT_FOUND, errors::KEY_NOT_FOUND);
            }
            match options.reference {
                ApiKeyReference::User
                    if owns_user_key(
                        options.reference,
                        &api_key.reference_id,
                        &identity.user.id,
                    ) => {}
                ApiKeyReference::User => {
                    return error(StatusCode::NOT_FOUND, errors::KEY_NOT_FOUND)
                }
                ApiKeyReference::Organization => {
                    if let Err(error) = ensure_organization_permission(
                        &context,
                        &identity.user.id,
                        &api_key.reference_id,
                        ApiKeyAction::Delete,
                    )
                    .await
                    {
                        return error_response_from_rustauth(error);
                    }
                }
            }
            store.delete(&api_key).await?;
            json(StatusCode::OK, &DeleteApiKeyResponse { success: true })
        },
    )
}

fn error_response_from_rustauth(
    error: rustauth_core::error::RustAuthError,
) -> Result<rustauth_core::api::ApiResponse, rustauth_core::error::RustAuthError> {
    let message = error.to_string();
    if message.contains(errors::message(errors::USER_NOT_MEMBER_OF_ORGANIZATION)) {
        return super::error(
            StatusCode::FORBIDDEN,
            errors::USER_NOT_MEMBER_OF_ORGANIZATION,
        );
    }
    if message.contains(errors::message(errors::ORGANIZATION_PLUGIN_REQUIRED)) {
        return super::error(
            StatusCode::INTERNAL_SERVER_ERROR,
            errors::ORGANIZATION_PLUGIN_REQUIRED,
        );
    }
    super::error(
        StatusCode::FORBIDDEN,
        errors::INSUFFICIENT_API_KEY_PERMISSIONS,
    )
}