graphql: Add a method to list groups

This commit is contained in:
Valentin Tolmer
2021-09-16 09:26:31 +02:00
committed by nitnelave
parent e4d6b122c5
commit 480f48f820
11 changed files with 147 additions and 57 deletions

View File

@@ -1,7 +1,7 @@
use crate::{
domain::{
error::DomainError,
handler::{BackendHandler, BindRequest, LoginHandler},
handler::{BackendHandler, BindRequest, GroupIdAndName, LoginHandler},
opaque_handler::OpaqueHandler,
},
infra::{
@@ -32,12 +32,12 @@ use time::ext::NumericalDuration;
type Token<S> = jwt::Token<jwt::Header, JWTClaims, S>;
type SignedToken = Token<jwt::token::Signed>;
fn create_jwt(key: &Hmac<Sha512>, user: String, groups: HashSet<String>) -> SignedToken {
fn create_jwt(key: &Hmac<Sha512>, user: String, groups: HashSet<GroupIdAndName>) -> SignedToken {
let claims = JWTClaims {
exp: Utc::now() + chrono::Duration::days(1),
iat: Utc::now(),
user,
groups,
groups: groups.into_iter().map(|g| g.1).collect(),
};
let header = jwt::Header {
algorithm: jwt::AlgorithmType::Hs512,

View File

@@ -1,10 +1,11 @@
use crate::domain::handler::BackendHandler;
use crate::domain::handler::{BackendHandler, GroupIdAndName};
use juniper::{graphql_object, FieldResult, GraphQLInputObject};
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
type DomainRequestFilter = crate::domain::handler::RequestFilter;
type DomainUser = crate::domain::handler::User;
type DomainGroup = crate::domain::handler::Group;
use super::api::Context;
#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
@@ -113,6 +114,17 @@ impl<Handler: BackendHandler + Sync> Query<Handler> {
.await
.map(|v| v.into_iter().map(Into::into).collect())?)
}
async fn groups(context: &Context<Handler>) -> FieldResult<Vec<Group<Handler>>> {
if !context.validation_result.is_admin {
return Err("Unauthorized access to group list".into());
}
Ok(context
.handler
.list_groups()
.await
.map(|v| v.into_iter().map(Into::into).collect())?)
}
}
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
@@ -179,14 +191,19 @@ impl<Handler: BackendHandler> From<DomainUser> for User<Handler> {
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize)]
/// Represents a single group.
pub struct Group<Handler: BackendHandler> {
group_id: String,
group_id: i32,
display_name: String,
members: Option<Vec<String>>,
_phantom: std::marker::PhantomData<Box<Handler>>,
}
#[graphql_object(context = Context<Handler>)]
impl<Handler: BackendHandler + Sync> Group<Handler> {
fn id(&self) -> String {
self.group_id.clone()
fn id(&self) -> i32 {
self.group_id
}
fn display_name(&self) -> String {
self.display_name.clone()
}
/// The groups to which this user belongs.
async fn users(&self, context: &Context<Handler>) -> FieldResult<Vec<User<Handler>>> {
@@ -197,10 +214,23 @@ impl<Handler: BackendHandler + Sync> Group<Handler> {
}
}
impl<Handler: BackendHandler> From<String> for Group<Handler> {
fn from(group_id: String) -> Self {
impl<Handler: BackendHandler> From<GroupIdAndName> for Group<Handler> {
fn from(group_id_and_name: GroupIdAndName) -> Self {
Self {
group_id,
group_id: group_id_and_name.0 .0,
display_name: group_id_and_name.1,
members: None,
_phantom: std::marker::PhantomData,
}
}
}
impl<Handler: BackendHandler> From<DomainGroup> for Group<Handler> {
fn from(group: DomainGroup) -> Self {
Self {
group_id: group.id.0,
display_name: group.display_name,
members: Some(group.users.into_iter().map(Into::into).collect()),
_phantom: std::marker::PhantomData,
}
}
@@ -209,7 +239,10 @@ impl<Handler: BackendHandler> From<String> for Group<Handler> {
#[cfg(test)]
mod tests {
use super::*;
use crate::{domain::handler::MockTestBackendHandler, infra::auth_service::ValidationResults};
use crate::{
domain::handler::{GroupId, GroupIdAndName, MockTestBackendHandler},
infra::auth_service::ValidationResults,
};
use juniper::{
execute, graphql_value, DefaultScalarValue, EmptyMutation, EmptySubscription, GraphQLType,
RootNode, Variables,
@@ -250,8 +283,8 @@ mod tests {
..Default::default()
})
});
let mut groups = HashSet::<String>::new();
groups.insert("Bobbersons".to_string());
let mut groups = HashSet::new();
groups.insert(GroupIdAndName(GroupId(3), "Bobbersons".to_string()));
mock.expect_get_user_groups()
.with(eq("bob"))
.return_once(|_| Ok(groups));
@@ -270,7 +303,7 @@ mod tests {
"user": {
"id": "bob",
"email": "bob@bobbers.on",
"groups": [{"id": "Bobbersons"}]
"groups": [{"id": 3}]
}
}),
vec![]

View File

@@ -29,7 +29,7 @@ mockall::mock! {
async fn list_users(&self, filters: Option<RequestFilter>) -> DomainResult<Vec<User>>;
async fn list_groups(&self) -> DomainResult<Vec<Group>>;
async fn get_user_details(&self, user_id: &str) -> DomainResult<User>;
async fn get_user_groups(&self, user: &str) -> DomainResult<HashSet<String>>;
async fn get_user_groups(&self, user: &str) -> DomainResult<HashSet<GroupIdAndName>>;
async fn create_user(&self, request: CreateUserRequest) -> DomainResult<()>;
async fn update_user(&self, request: UpdateUserRequest) -> DomainResult<()>;
async fn delete_user(&self, user_id: &str) -> DomainResult<()>;