mirror of
https://github.com/lldap/lldap.git
synced 2024-11-25 09:06:03 +00:00
server: Add graphql support for creating/deleting attributes
This commit is contained in:
parent
2a5fd01439
commit
439fde434b
7 changed files with 208 additions and 57 deletions
77
schema.graphql
generated
77
schema.graphql
generated
|
@ -3,11 +3,6 @@ type AttributeValue {
|
|||
value: [String!]!
|
||||
}
|
||||
|
||||
input EqualityConstraint {
|
||||
field: String!
|
||||
value: String!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createUser(user: CreateUserInput!): User!
|
||||
createGroup(name: String!): Group!
|
||||
|
@ -17,6 +12,10 @@ type Mutation {
|
|||
removeUserFromGroup(userId: String!, groupId: Int!): Success!
|
||||
deleteUser(userId: String!): Success!
|
||||
deleteGroup(groupId: Int!): Success!
|
||||
addUserAttribute(name: String!, attributeType: AttributeType!, isList: Boolean!, isVisible: Boolean!, isEditable: Boolean!): Success!
|
||||
addGroupAttribute(name: String!, attributeType: AttributeType!, isList: Boolean!, isVisible: Boolean!, isEditable: Boolean!): Success!
|
||||
deleteUserAttribute(name: String!): Success!
|
||||
deleteGroupAttribute(name: String!): Success!
|
||||
}
|
||||
|
||||
type Group {
|
||||
|
@ -46,17 +45,6 @@ input RequestFilter {
|
|||
"DateTime"
|
||||
scalar DateTimeUtc
|
||||
|
||||
type Schema {
|
||||
userSchema: AttributeList!
|
||||
groupSchema: AttributeList!
|
||||
}
|
||||
|
||||
"The fields that can be updated for a group."
|
||||
input UpdateGroupInput {
|
||||
id: Int!
|
||||
displayName: String
|
||||
}
|
||||
|
||||
type Query {
|
||||
apiVersion: String!
|
||||
user(userId: String!): User!
|
||||
|
@ -76,6 +64,41 @@ input CreateUserInput {
|
|||
avatar: String
|
||||
}
|
||||
|
||||
type AttributeSchema {
|
||||
name: String!
|
||||
attributeType: AttributeType!
|
||||
isList: Boolean!
|
||||
isVisible: Boolean!
|
||||
isEditable: Boolean!
|
||||
isHardcoded: Boolean!
|
||||
}
|
||||
|
||||
"The fields that can be updated for a user."
|
||||
input UpdateUserInput {
|
||||
id: String!
|
||||
email: String
|
||||
displayName: String
|
||||
firstName: String
|
||||
lastName: String
|
||||
avatar: String
|
||||
}
|
||||
|
||||
input EqualityConstraint {
|
||||
field: String!
|
||||
value: String!
|
||||
}
|
||||
|
||||
type Schema {
|
||||
userSchema: AttributeList!
|
||||
groupSchema: AttributeList!
|
||||
}
|
||||
|
||||
"The fields that can be updated for a group."
|
||||
input UpdateGroupInput {
|
||||
id: Int!
|
||||
displayName: String
|
||||
}
|
||||
|
||||
type User {
|
||||
id: String!
|
||||
email: String!
|
||||
|
@ -95,29 +118,17 @@ type AttributeList {
|
|||
attributes: [AttributeSchema!]!
|
||||
}
|
||||
|
||||
type AttributeSchema {
|
||||
name: String!
|
||||
attributeType: String!
|
||||
isList: Boolean!
|
||||
isVisible: Boolean!
|
||||
isEditable: Boolean!
|
||||
isHardcoded: Boolean!
|
||||
enum AttributeType {
|
||||
STRING
|
||||
INTEGER
|
||||
JPEG_PHOTO
|
||||
DATE_TIME
|
||||
}
|
||||
|
||||
type Success {
|
||||
ok: Boolean!
|
||||
}
|
||||
|
||||
"The fields that can be updated for a user."
|
||||
input UpdateUserInput {
|
||||
id: String!
|
||||
email: String
|
||||
displayName: String
|
||||
firstName: String
|
||||
lastName: String
|
||||
avatar: String
|
||||
}
|
||||
|
||||
schema {
|
||||
query: Query
|
||||
mutation: Mutation
|
||||
|
|
|
@ -235,6 +235,7 @@ pub trait BackendHandler:
|
|||
+ UserListerBackendHandler
|
||||
+ GroupListerBackendHandler
|
||||
+ ReadSchemaBackendHandler
|
||||
+ SchemaBackendHandler
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -342,7 +342,17 @@ impl From<&GroupId> for Value {
|
|||
}
|
||||
|
||||
#[derive(
|
||||
Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString, IntoStaticStr,
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
EnumString,
|
||||
IntoStaticStr,
|
||||
juniper::GraphQLEnum,
|
||||
)]
|
||||
pub enum AttributeType {
|
||||
String,
|
||||
|
|
|
@ -6,10 +6,10 @@ use tracing::info;
|
|||
use crate::domain::{
|
||||
error::Result,
|
||||
handler::{
|
||||
AttributeSchema, BackendHandler, CreateGroupRequest, CreateUserRequest,
|
||||
GroupBackendHandler, GroupListerBackendHandler, GroupRequestFilter,
|
||||
ReadSchemaBackendHandler, Schema, UpdateGroupRequest, UpdateUserRequest,
|
||||
UserBackendHandler, UserListerBackendHandler, UserRequestFilter,
|
||||
AttributeSchema, BackendHandler, CreateAttributeRequest, CreateGroupRequest,
|
||||
CreateUserRequest, GroupBackendHandler, GroupListerBackendHandler, GroupRequestFilter,
|
||||
ReadSchemaBackendHandler, Schema, SchemaBackendHandler, UpdateGroupRequest,
|
||||
UpdateUserRequest, UserBackendHandler, UserListerBackendHandler, UserRequestFilter,
|
||||
},
|
||||
types::{Group, GroupDetails, GroupId, User, UserAndGroups, UserId},
|
||||
};
|
||||
|
@ -94,7 +94,10 @@ pub trait UserWriteableBackendHandler: UserReadableBackendHandler {
|
|||
|
||||
#[async_trait]
|
||||
pub trait AdminBackendHandler:
|
||||
UserWriteableBackendHandler + ReadonlyBackendHandler + UserWriteableBackendHandler
|
||||
UserWriteableBackendHandler
|
||||
+ ReadonlyBackendHandler
|
||||
+ UserWriteableBackendHandler
|
||||
+ SchemaBackendHandler
|
||||
{
|
||||
async fn create_user(&self, request: CreateUserRequest) -> Result<()>;
|
||||
async fn delete_user(&self, user_id: &UserId) -> Result<()>;
|
||||
|
@ -103,6 +106,10 @@ pub trait AdminBackendHandler:
|
|||
async fn update_group(&self, request: UpdateGroupRequest) -> Result<()>;
|
||||
async fn create_group(&self, request: CreateGroupRequest) -> Result<GroupId>;
|
||||
async fn delete_group(&self, group_id: GroupId) -> Result<()>;
|
||||
async fn add_user_attribute(&self, request: CreateAttributeRequest) -> Result<()>;
|
||||
async fn add_group_attribute(&self, request: CreateAttributeRequest) -> Result<()>;
|
||||
async fn delete_user_attribute(&self, name: &str) -> Result<()>;
|
||||
async fn delete_group_attribute(&self, name: &str) -> Result<()>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -161,6 +168,18 @@ impl<Handler: BackendHandler> AdminBackendHandler for Handler {
|
|||
async fn delete_group(&self, group_id: GroupId) -> Result<()> {
|
||||
<Handler as GroupBackendHandler>::delete_group(self, group_id).await
|
||||
}
|
||||
async fn add_user_attribute(&self, request: CreateAttributeRequest) -> Result<()> {
|
||||
<Handler as SchemaBackendHandler>::add_user_attribute(self, request).await
|
||||
}
|
||||
async fn add_group_attribute(&self, request: CreateAttributeRequest) -> Result<()> {
|
||||
<Handler as SchemaBackendHandler>::add_group_attribute(self, request).await
|
||||
}
|
||||
async fn delete_user_attribute(&self, name: &str) -> Result<()> {
|
||||
<Handler as SchemaBackendHandler>::delete_user_attribute(self, name).await
|
||||
}
|
||||
async fn delete_group_attribute(&self, name: &str) -> Result<()> {
|
||||
<Handler as SchemaBackendHandler>::delete_group_attribute(self, name).await
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AccessControlledBackendHandler<Handler> {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::{
|
||||
domain::{
|
||||
handler::{
|
||||
BackendHandler, CreateGroupRequest, CreateUserRequest, UpdateGroupRequest,
|
||||
UpdateUserRequest,
|
||||
BackendHandler, CreateAttributeRequest, CreateGroupRequest, CreateUserRequest,
|
||||
UpdateGroupRequest, UpdateUserRequest,
|
||||
},
|
||||
types::{GroupId, JpegPhoto, UserId},
|
||||
types::{AttributeType, GroupId, JpegPhoto, UserId},
|
||||
},
|
||||
infra::{
|
||||
access_control::{
|
||||
|
@ -285,4 +285,108 @@ impl<Handler: BackendHandler> Mutation<Handler> {
|
|||
.await?;
|
||||
Ok(Success::new())
|
||||
}
|
||||
|
||||
async fn add_user_attribute(
|
||||
context: &Context<Handler>,
|
||||
name: String,
|
||||
attribute_type: AttributeType,
|
||||
is_list: bool,
|
||||
is_visible: bool,
|
||||
is_editable: bool,
|
||||
) -> FieldResult<Success> {
|
||||
let span = debug_span!("[GraphQL mutation] add_user_attribute");
|
||||
span.in_scope(|| {
|
||||
debug!(?name, ?attribute_type, is_list, is_visible, is_editable);
|
||||
});
|
||||
let handler = context
|
||||
.get_admin_handler()
|
||||
.ok_or_else(field_error_callback(
|
||||
&span,
|
||||
"Unauthorized attribute creation",
|
||||
))?;
|
||||
handler
|
||||
.add_user_attribute(CreateAttributeRequest {
|
||||
name,
|
||||
attribute_type,
|
||||
is_list,
|
||||
is_visible,
|
||||
is_editable,
|
||||
})
|
||||
.instrument(span)
|
||||
.await?;
|
||||
Ok(Success::new())
|
||||
}
|
||||
|
||||
async fn add_group_attribute(
|
||||
context: &Context<Handler>,
|
||||
name: String,
|
||||
attribute_type: AttributeType,
|
||||
is_list: bool,
|
||||
is_visible: bool,
|
||||
is_editable: bool,
|
||||
) -> FieldResult<Success> {
|
||||
let span = debug_span!("[GraphQL mutation] add_group_attribute");
|
||||
span.in_scope(|| {
|
||||
debug!(?name, ?attribute_type, is_list, is_visible, is_editable);
|
||||
});
|
||||
let handler = context
|
||||
.get_admin_handler()
|
||||
.ok_or_else(field_error_callback(
|
||||
&span,
|
||||
"Unauthorized attribute creation",
|
||||
))?;
|
||||
handler
|
||||
.add_group_attribute(CreateAttributeRequest {
|
||||
name,
|
||||
attribute_type,
|
||||
is_list,
|
||||
is_visible,
|
||||
is_editable,
|
||||
})
|
||||
.instrument(span)
|
||||
.await?;
|
||||
Ok(Success::new())
|
||||
}
|
||||
|
||||
async fn delete_user_attribute(
|
||||
context: &Context<Handler>,
|
||||
name: String,
|
||||
) -> FieldResult<Success> {
|
||||
let span = debug_span!("[GraphQL mutation] delete_user_attribute");
|
||||
span.in_scope(|| {
|
||||
debug!(?name);
|
||||
});
|
||||
let handler = context
|
||||
.get_admin_handler()
|
||||
.ok_or_else(field_error_callback(
|
||||
&span,
|
||||
"Unauthorized attribute deletion",
|
||||
))?;
|
||||
handler
|
||||
.delete_user_attribute(&name)
|
||||
.instrument(span)
|
||||
.await?;
|
||||
Ok(Success::new())
|
||||
}
|
||||
|
||||
async fn delete_group_attribute(
|
||||
context: &Context<Handler>,
|
||||
name: String,
|
||||
) -> FieldResult<Success> {
|
||||
let span = debug_span!("[GraphQL mutation] delete_group_attribute");
|
||||
span.in_scope(|| {
|
||||
debug!(?name);
|
||||
});
|
||||
let handler = context
|
||||
.get_admin_handler()
|
||||
.ok_or_else(field_error_callback(
|
||||
&span,
|
||||
"Unauthorized attribute deletion",
|
||||
))?;
|
||||
handler
|
||||
.delete_group_attribute(&name)
|
||||
.instrument(span)
|
||||
.await?;
|
||||
Ok(Success::new())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -438,9 +438,8 @@ impl<Handler: BackendHandler> AttributeSchema<Handler> {
|
|||
fn name(&self) -> String {
|
||||
self.schema.name.clone()
|
||||
}
|
||||
fn attribute_type(&self) -> String {
|
||||
let name: &'static str = self.schema.attribute_type.into();
|
||||
name.to_owned()
|
||||
fn attribute_type(&self) -> AttributeType {
|
||||
self.schema.attribute_type
|
||||
}
|
||||
fn is_list(&self) -> bool {
|
||||
self.schema.is_list
|
||||
|
@ -917,7 +916,7 @@ mod tests {
|
|||
"attributes": [
|
||||
{
|
||||
"name": "avatar",
|
||||
"attributeType": "JpegPhoto",
|
||||
"attributeType": "JPEG_PHOTO",
|
||||
"isList": false,
|
||||
"isVisible": true,
|
||||
"isEditable": true,
|
||||
|
@ -925,7 +924,7 @@ mod tests {
|
|||
},
|
||||
{
|
||||
"name": "creation_date",
|
||||
"attributeType": "DateTime",
|
||||
"attributeType": "DATE_TIME",
|
||||
"isList": false,
|
||||
"isVisible": true,
|
||||
"isEditable": false,
|
||||
|
@ -933,7 +932,7 @@ mod tests {
|
|||
},
|
||||
{
|
||||
"name": "display_name",
|
||||
"attributeType": "String",
|
||||
"attributeType": "STRING",
|
||||
"isList": false,
|
||||
"isVisible": true,
|
||||
"isEditable": true,
|
||||
|
@ -941,7 +940,7 @@ mod tests {
|
|||
},
|
||||
{
|
||||
"name": "first_name",
|
||||
"attributeType": "String",
|
||||
"attributeType": "STRING",
|
||||
"isList": false,
|
||||
"isVisible": true,
|
||||
"isEditable": true,
|
||||
|
@ -949,7 +948,7 @@ mod tests {
|
|||
},
|
||||
{
|
||||
"name": "last_name",
|
||||
"attributeType": "String",
|
||||
"attributeType": "STRING",
|
||||
"isList": false,
|
||||
"isVisible": true,
|
||||
"isEditable": true,
|
||||
|
@ -957,7 +956,7 @@ mod tests {
|
|||
},
|
||||
{
|
||||
"name": "mail",
|
||||
"attributeType": "String",
|
||||
"attributeType": "STRING",
|
||||
"isList": false,
|
||||
"isVisible": true,
|
||||
"isEditable": true,
|
||||
|
@ -965,7 +964,7 @@ mod tests {
|
|||
},
|
||||
{
|
||||
"name": "user_id",
|
||||
"attributeType": "String",
|
||||
"attributeType": "STRING",
|
||||
"isList": false,
|
||||
"isVisible": true,
|
||||
"isEditable": false,
|
||||
|
@ -973,7 +972,7 @@ mod tests {
|
|||
},
|
||||
{
|
||||
"name": "uuid",
|
||||
"attributeType": "String",
|
||||
"attributeType": "STRING",
|
||||
"isList": false,
|
||||
"isVisible": true,
|
||||
"isEditable": false,
|
||||
|
@ -985,7 +984,7 @@ mod tests {
|
|||
"attributes": [
|
||||
{
|
||||
"name": "creation_date",
|
||||
"attributeType": "DateTime",
|
||||
"attributeType": "DATE_TIME",
|
||||
"isList": false,
|
||||
"isVisible": true,
|
||||
"isEditable": false,
|
||||
|
@ -993,7 +992,7 @@ mod tests {
|
|||
},
|
||||
{
|
||||
"name": "display_name",
|
||||
"attributeType": "String",
|
||||
"attributeType": "STRING",
|
||||
"isList": false,
|
||||
"isVisible": true,
|
||||
"isEditable": true,
|
||||
|
@ -1001,7 +1000,7 @@ mod tests {
|
|||
},
|
||||
{
|
||||
"name": "group_id",
|
||||
"attributeType": "Integer",
|
||||
"attributeType": "INTEGER",
|
||||
"isList": false,
|
||||
"isVisible": true,
|
||||
"isEditable": false,
|
||||
|
@ -1009,7 +1008,7 @@ mod tests {
|
|||
},
|
||||
{
|
||||
"name": "uuid",
|
||||
"attributeType": "String",
|
||||
"attributeType": "STRING",
|
||||
"isList": false,
|
||||
"isVisible": true,
|
||||
"isEditable": false,
|
||||
|
|
|
@ -42,6 +42,13 @@ mockall::mock! {
|
|||
async fn get_schema(&self) -> Result<Schema>;
|
||||
}
|
||||
#[async_trait]
|
||||
impl SchemaBackendHandler for TestBackendHandler {
|
||||
async fn add_user_attribute(&self, request: CreateAttributeRequest) -> Result<()>;
|
||||
async fn add_group_attribute(&self, request: CreateAttributeRequest) -> Result<()>;
|
||||
async fn delete_user_attribute(&self, name: &str) -> Result<()>;
|
||||
async fn delete_group_attribute(&self, name: &str) -> Result<()>;
|
||||
}
|
||||
#[async_trait]
|
||||
impl BackendHandler for TestBackendHandler {}
|
||||
#[async_trait]
|
||||
impl OpaqueHandler for TestBackendHandler {
|
||||
|
|
Loading…
Reference in a new issue