quokka-admin 0.1.0

An admin panel for quokka
Documentation
use axum::Extension;
use quokka::{extract::Session, state::FromState};

use crate::{
    entity::{group::GroupRepository, permission::PermissionRepository, user::UserRepository},
    middleware::{AdminAuthProvider, AuthenticatedUser, LoginResult},
};

use super::page_loader::ADMIN_USER_SESSION_KEY;

#[derive(Clone, FromState)]
pub struct DbAuthProvider {
    user_repository: UserRepository,
    group_repository: GroupRepository,
    permission_repository: PermissionRepository,
}

impl<S: Send + Sync + 'static> AdminAuthProvider<S> for DbAuthProvider {
    type AuthParams = Extension<Session>;

    async fn authenticate(
        &self,
        params: Self::AuthParams,
    ) -> quokka::Result<Option<crate::middleware::AuthenticatedUser>> {
        let Ok(username) = params
            .0
            .get_extension::<LoginResult>(ADMIN_USER_SESSION_KEY)
            .map(|val| val.user_identifier)
        else {
            return Ok(None);
        };

        let (Some(user), groups) = futures::try_join!(
            self.user_repository.get_user_by_name(&username),
            self.group_repository.get_group_of_user(&username)
        )?
        else {
            return Ok(None);
        };

        Ok(Some(AuthenticatedUser {
            name: user.username,
            groups: groups.into_iter().map(|group| group.name).collect(),
            context: Default::default(),
        }))
    }

    async fn authorize(
        &self,
        user: &crate::middleware::AuthenticatedUser,
        permission: &crate::middleware::PermissionContext,
    ) -> quokka::Result<bool> {
        if self
            .permission_repository
            .get_of_user(&user.name, &permission.verb, &permission.resource)
            .await?
            .is_some()
        {
            return Ok(true);
        }

        if futures::future::try_join_all(user.groups.iter().map(|group| {
            self.permission_repository
                .get_of_group(&group, &permission.verb, &permission.resource)
        }))
        .await?
        .iter()
        .any(|permission| permission.is_some())
        {
            return Ok(true);
        }

        Ok(false)
    }
}