mirror of
https://github.com/lldap/lldap.git
synced 2026-03-31 15:07:48 +01:00
server: Add graphql support for setting attributes
This commit is contained in:
committed by
nitnelave
parent
9e88bfe6b4
commit
c6ecf8d58a
@@ -90,6 +90,7 @@ impl CommonComponent<CreateUserForm> for CreateUserForm {
|
|||||||
firstName: to_option(model.first_name),
|
firstName: to_option(model.first_name),
|
||||||
lastName: to_option(model.last_name),
|
lastName: to_option(model.last_name),
|
||||||
avatar: None,
|
avatar: None,
|
||||||
|
attributes: None,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
self.common.call_graphql::<CreateUser, _>(
|
self.common.call_graphql::<CreateUser, _>(
|
||||||
|
|||||||
@@ -391,6 +391,8 @@ impl UserDetailsForm {
|
|||||||
firstName: None,
|
firstName: None,
|
||||||
lastName: None,
|
lastName: None,
|
||||||
avatar: None,
|
avatar: None,
|
||||||
|
removeAttributes: None,
|
||||||
|
insertAttributes: None,
|
||||||
};
|
};
|
||||||
let default_user_input = user_input.clone();
|
let default_user_input = user_input.clone();
|
||||||
let model = self.form.model();
|
let model = self.form.model();
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ js = []
|
|||||||
rust-argon2 = "0.8"
|
rust-argon2 = "0.8"
|
||||||
curve25519-dalek = "3"
|
curve25519-dalek = "3"
|
||||||
digest = "0.9"
|
digest = "0.9"
|
||||||
generic-array = "*"
|
generic-array = "0.14"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
serde = "*"
|
serde = "*"
|
||||||
sha2 = "0.9"
|
sha2 = "0.9"
|
||||||
|
|||||||
@@ -194,6 +194,7 @@ impl TryFrom<ResultEntry> for User {
|
|||||||
first_name,
|
first_name,
|
||||||
last_name,
|
last_name,
|
||||||
avatar: avatar.map(base64::encode),
|
avatar: avatar.map(base64::encode),
|
||||||
|
attributes: None,
|
||||||
},
|
},
|
||||||
password,
|
password,
|
||||||
entry.dn,
|
entry.dn,
|
||||||
|
|||||||
Generated
+42
-4
@@ -6,6 +6,7 @@ type AttributeValue {
|
|||||||
type Mutation {
|
type Mutation {
|
||||||
createUser(user: CreateUserInput!): User!
|
createUser(user: CreateUserInput!): User!
|
||||||
createGroup(name: String!): Group!
|
createGroup(name: String!): Group!
|
||||||
|
createGroupWithDetails(request: CreateGroupInput!): Group!
|
||||||
updateUser(user: UpdateUserInput!): Success!
|
updateUser(user: UpdateUserInput!): Success!
|
||||||
updateGroup(group: UpdateGroupInput!): Success!
|
updateGroup(group: UpdateGroupInput!): Success!
|
||||||
addUserToGroup(userId: String!, groupId: Int!): Success!
|
addUserToGroup(userId: String!, groupId: Int!): Success!
|
||||||
@@ -61,7 +62,8 @@ input CreateUserInput {
|
|||||||
displayName: String
|
displayName: String
|
||||||
firstName: String
|
firstName: String
|
||||||
lastName: String
|
lastName: String
|
||||||
avatar: String
|
"Base64 encoded JpegPhoto." avatar: String
|
||||||
|
"User-defined attributes." attributes: [AttributeValueInput!]
|
||||||
}
|
}
|
||||||
|
|
||||||
type AttributeSchema {
|
type AttributeSchema {
|
||||||
@@ -80,7 +82,15 @@ input UpdateUserInput {
|
|||||||
displayName: String
|
displayName: String
|
||||||
firstName: String
|
firstName: String
|
||||||
lastName: String
|
lastName: String
|
||||||
avatar: String
|
"Base64 encoded JpegPhoto." avatar: String
|
||||||
|
"""
|
||||||
|
Attribute names to remove.
|
||||||
|
They are processed before insertions.
|
||||||
|
""" removeAttributes: [String!]
|
||||||
|
"""
|
||||||
|
Inserts or updates the given attributes.
|
||||||
|
For lists, the entire list must be provided.
|
||||||
|
""" insertAttributes: [AttributeValueInput!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input EqualityConstraint {
|
input EqualityConstraint {
|
||||||
@@ -95,8 +105,36 @@ type Schema {
|
|||||||
|
|
||||||
"The fields that can be updated for a group."
|
"The fields that can be updated for a group."
|
||||||
input UpdateGroupInput {
|
input UpdateGroupInput {
|
||||||
id: Int!
|
"The group ID." id: Int!
|
||||||
displayName: String
|
"The new display name." displayName: String
|
||||||
|
"""
|
||||||
|
Attribute names to remove.
|
||||||
|
They are processed before insertions.
|
||||||
|
""" removeAttributes: [String!]
|
||||||
|
"""
|
||||||
|
Inserts or updates the given attributes.
|
||||||
|
For lists, the entire list must be provided.
|
||||||
|
""" insertAttributes: [AttributeValueInput!]
|
||||||
|
}
|
||||||
|
|
||||||
|
input AttributeValueInput {
|
||||||
|
"""
|
||||||
|
The name of the attribute. It must be present in the schema, and the type informs how
|
||||||
|
to interpret the values.
|
||||||
|
""" name: String!
|
||||||
|
"""
|
||||||
|
The values of the attribute.
|
||||||
|
If the attribute is not a list, the vector must contain exactly one element.
|
||||||
|
Integers (signed 64 bits) are represented as strings.
|
||||||
|
Dates are represented as strings in RFC3339 format, e.g. "2019-10-12T07:20:50.52Z".
|
||||||
|
JpegPhotos are represented as base64 encoded strings. They must be valid JPEGs.
|
||||||
|
""" value: [String!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
"The details required to create a group."
|
||||||
|
input CreateGroupInput {
|
||||||
|
displayName: String!
|
||||||
|
"User-defined attributes." attributes: [AttributeValueInput!]
|
||||||
}
|
}
|
||||||
|
|
||||||
type User {
|
type User {
|
||||||
|
|||||||
@@ -205,13 +205,11 @@ impl TryFrom<Vec<u8>> for JpegPhoto {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<String> for JpegPhoto {
|
impl TryFrom<&str> for JpegPhoto {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
fn try_from(string: String) -> anyhow::Result<Self> {
|
fn try_from(string: &str) -> anyhow::Result<Self> {
|
||||||
// The String format is in base64.
|
// The String format is in base64.
|
||||||
<Self as TryFrom<_>>::try_from(
|
<Self as TryFrom<_>>::try_from(base64::engine::general_purpose::STANDARD.decode(string)?)
|
||||||
base64::engine::general_purpose::STANDARD.decode(string.as_str())?,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use crate::domain::{
|
|||||||
ReadSchemaBackendHandler, Schema, SchemaBackendHandler, UpdateGroupRequest,
|
ReadSchemaBackendHandler, Schema, SchemaBackendHandler, UpdateGroupRequest,
|
||||||
UpdateUserRequest, UserBackendHandler, UserListerBackendHandler, UserRequestFilter,
|
UpdateUserRequest, UserBackendHandler, UserListerBackendHandler, UserRequestFilter,
|
||||||
},
|
},
|
||||||
|
schema::PublicSchema,
|
||||||
types::{Group, GroupDetails, GroupId, User, UserAndGroups, UserId},
|
types::{Group, GroupDetails, GroupId, User, UserAndGroups, UserId},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,9 +72,10 @@ impl ValidationResults {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait UserReadableBackendHandler {
|
pub trait UserReadableBackendHandler: ReadSchemaBackendHandler {
|
||||||
async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
|
async fn get_user_details(&self, user_id: &UserId) -> Result<User>;
|
||||||
async fn get_user_groups(&self, user_id: &UserId) -> Result<HashSet<GroupDetails>>;
|
async fn get_user_groups(&self, user_id: &UserId) -> Result<HashSet<GroupDetails>>;
|
||||||
|
async fn get_schema(&self) -> Result<PublicSchema>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -120,6 +122,11 @@ impl<Handler: BackendHandler> UserReadableBackendHandler for Handler {
|
|||||||
async fn get_user_groups(&self, user_id: &UserId) -> Result<HashSet<GroupDetails>> {
|
async fn get_user_groups(&self, user_id: &UserId) -> Result<HashSet<GroupDetails>> {
|
||||||
<Handler as UserBackendHandler>::get_user_groups(self, user_id).await
|
<Handler as UserBackendHandler>::get_user_groups(self, user_id).await
|
||||||
}
|
}
|
||||||
|
async fn get_schema(&self) -> Result<PublicSchema> {
|
||||||
|
Ok(PublicSchema::from(
|
||||||
|
<Handler as ReadSchemaBackendHandler>::get_schema(self).await?,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
domain::{
|
domain::{
|
||||||
handler::{
|
handler::{
|
||||||
BackendHandler, CreateAttributeRequest, CreateGroupRequest, CreateUserRequest,
|
AttributeList, BackendHandler, CreateAttributeRequest, CreateGroupRequest,
|
||||||
UpdateGroupRequest, UpdateUserRequest,
|
CreateUserRequest, UpdateGroupRequest, UpdateUserRequest,
|
||||||
|
},
|
||||||
|
types::{
|
||||||
|
AttributeType, AttributeValue as DomainAttributeValue, GroupId, JpegPhoto, Serialized,
|
||||||
|
UserId,
|
||||||
},
|
},
|
||||||
types::{AttributeType, GroupId, JpegPhoto, UserId},
|
|
||||||
},
|
},
|
||||||
infra::{
|
infra::{
|
||||||
access_control::{
|
access_control::{
|
||||||
@@ -14,10 +17,10 @@ use crate::{
|
|||||||
graphql::api::{field_error_callback, Context},
|
graphql::api::{field_error_callback, Context},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use anyhow::Context as AnyhowContext;
|
use anyhow::{anyhow, Context as AnyhowContext};
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use juniper::{graphql_object, FieldResult, GraphQLInputObject, GraphQLObject};
|
use juniper::{graphql_object, FieldResult, GraphQLInputObject, GraphQLObject};
|
||||||
use tracing::{debug, debug_span, Instrument};
|
use tracing::{debug, debug_span, Instrument, Span};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
/// The top-level GraphQL mutation type.
|
/// The top-level GraphQL mutation type.
|
||||||
@@ -33,6 +36,21 @@ impl<Handler: BackendHandler> Mutation<Handler> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
|
||||||
|
// This conflicts with the attribute values returned by the user/group queries.
|
||||||
|
#[graphql(name = "AttributeValueInput")]
|
||||||
|
struct AttributeValue {
|
||||||
|
/// The name of the attribute. It must be present in the schema, and the type informs how
|
||||||
|
/// to interpret the values.
|
||||||
|
name: String,
|
||||||
|
/// The values of the attribute.
|
||||||
|
/// If the attribute is not a list, the vector must contain exactly one element.
|
||||||
|
/// Integers (signed 64 bits) are represented as strings.
|
||||||
|
/// Dates are represented as strings in RFC3339 format, e.g. "2019-10-12T07:20:50.52Z".
|
||||||
|
/// JpegPhotos are represented as base64 encoded strings. They must be valid JPEGs.
|
||||||
|
value: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
|
#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
|
||||||
/// The details required to create a user.
|
/// The details required to create a user.
|
||||||
pub struct CreateUserInput {
|
pub struct CreateUserInput {
|
||||||
@@ -41,8 +59,18 @@ pub struct CreateUserInput {
|
|||||||
display_name: Option<String>,
|
display_name: Option<String>,
|
||||||
first_name: Option<String>,
|
first_name: Option<String>,
|
||||||
last_name: Option<String>,
|
last_name: Option<String>,
|
||||||
// Base64 encoded JpegPhoto.
|
/// Base64 encoded JpegPhoto.
|
||||||
avatar: Option<String>,
|
avatar: Option<String>,
|
||||||
|
/// User-defined attributes.
|
||||||
|
attributes: Option<Vec<AttributeValue>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
|
||||||
|
/// The details required to create a group.
|
||||||
|
pub struct CreateGroupInput {
|
||||||
|
display_name: String,
|
||||||
|
/// User-defined attributes.
|
||||||
|
attributes: Option<Vec<AttributeValue>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
|
#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
|
||||||
@@ -53,15 +81,29 @@ pub struct UpdateUserInput {
|
|||||||
display_name: Option<String>,
|
display_name: Option<String>,
|
||||||
first_name: Option<String>,
|
first_name: Option<String>,
|
||||||
last_name: Option<String>,
|
last_name: Option<String>,
|
||||||
// Base64 encoded JpegPhoto.
|
/// Base64 encoded JpegPhoto.
|
||||||
avatar: Option<String>,
|
avatar: Option<String>,
|
||||||
|
/// Attribute names to remove.
|
||||||
|
/// They are processed before insertions.
|
||||||
|
remove_attributes: Option<Vec<String>>,
|
||||||
|
/// Inserts or updates the given attributes.
|
||||||
|
/// For lists, the entire list must be provided.
|
||||||
|
insert_attributes: Option<Vec<AttributeValue>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
|
#[derive(PartialEq, Eq, Debug, GraphQLInputObject)]
|
||||||
/// The fields that can be updated for a group.
|
/// The fields that can be updated for a group.
|
||||||
pub struct UpdateGroupInput {
|
pub struct UpdateGroupInput {
|
||||||
|
/// The group ID.
|
||||||
id: i32,
|
id: i32,
|
||||||
|
/// The new display name.
|
||||||
display_name: Option<String>,
|
display_name: Option<String>,
|
||||||
|
/// Attribute names to remove.
|
||||||
|
/// They are processed before insertions.
|
||||||
|
remove_attributes: Option<Vec<String>>,
|
||||||
|
/// Inserts or updates the given attributes.
|
||||||
|
/// For lists, the entire list must be provided.
|
||||||
|
insert_attributes: Option<Vec<AttributeValue>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, GraphQLObject)]
|
#[derive(PartialEq, Eq, Debug, GraphQLObject)]
|
||||||
@@ -97,6 +139,13 @@ impl<Handler: BackendHandler> Mutation<Handler> {
|
|||||||
.map(JpegPhoto::try_from)
|
.map(JpegPhoto::try_from)
|
||||||
.transpose()
|
.transpose()
|
||||||
.context("Provided image is not a valid JPEG")?;
|
.context("Provided image is not a valid JPEG")?;
|
||||||
|
let schema = handler.get_schema().await?;
|
||||||
|
let attributes = user
|
||||||
|
.attributes
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.map(|attr| deserialize_attribute(&schema.get_schema().user_attributes, attr))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
handler
|
handler
|
||||||
.create_user(CreateUserRequest {
|
.create_user(CreateUserRequest {
|
||||||
user_id: user_id.clone(),
|
user_id: user_id.clone(),
|
||||||
@@ -105,7 +154,7 @@ impl<Handler: BackendHandler> Mutation<Handler> {
|
|||||||
first_name: user.first_name,
|
first_name: user.first_name,
|
||||||
last_name: user.last_name,
|
last_name: user.last_name,
|
||||||
avatar,
|
avatar,
|
||||||
..Default::default()
|
attributes,
|
||||||
})
|
})
|
||||||
.instrument(span.clone())
|
.instrument(span.clone())
|
||||||
.await?;
|
.await?;
|
||||||
@@ -124,19 +173,25 @@ impl<Handler: BackendHandler> Mutation<Handler> {
|
|||||||
span.in_scope(|| {
|
span.in_scope(|| {
|
||||||
debug!(?name);
|
debug!(?name);
|
||||||
});
|
});
|
||||||
let handler = context
|
create_group_with_details(
|
||||||
.get_admin_handler()
|
context,
|
||||||
.ok_or_else(field_error_callback(&span, "Unauthorized group creation"))?;
|
CreateGroupInput {
|
||||||
let request = CreateGroupRequest {
|
display_name: name,
|
||||||
display_name: name,
|
attributes: Some(Vec::new()),
|
||||||
..Default::default()
|
},
|
||||||
};
|
span,
|
||||||
let group_id = handler.create_group(request).await?;
|
)
|
||||||
Ok(handler
|
.await
|
||||||
.get_group_details(group_id)
|
}
|
||||||
.instrument(span)
|
async fn create_group_with_details(
|
||||||
.await
|
context: &Context<Handler>,
|
||||||
.map(Into::into)?)
|
request: CreateGroupInput,
|
||||||
|
) -> FieldResult<super::query::Group<Handler>> {
|
||||||
|
let span = debug_span!("[GraphQL mutation] create_group_with_details");
|
||||||
|
span.in_scope(|| {
|
||||||
|
debug!(?request);
|
||||||
|
});
|
||||||
|
create_group_with_details(context, request, span).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_user(
|
async fn update_user(
|
||||||
@@ -159,6 +214,13 @@ impl<Handler: BackendHandler> Mutation<Handler> {
|
|||||||
.map(JpegPhoto::try_from)
|
.map(JpegPhoto::try_from)
|
||||||
.transpose()
|
.transpose()
|
||||||
.context("Provided image is not a valid JPEG")?;
|
.context("Provided image is not a valid JPEG")?;
|
||||||
|
let schema = handler.get_schema().await?;
|
||||||
|
let insert_attributes = user
|
||||||
|
.insert_attributes
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.map(|attr| deserialize_attribute(&schema.get_schema().user_attributes, attr))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
handler
|
handler
|
||||||
.update_user(UpdateUserRequest {
|
.update_user(UpdateUserRequest {
|
||||||
user_id,
|
user_id,
|
||||||
@@ -167,7 +229,8 @@ impl<Handler: BackendHandler> Mutation<Handler> {
|
|||||||
first_name: user.first_name,
|
first_name: user.first_name,
|
||||||
last_name: user.last_name,
|
last_name: user.last_name,
|
||||||
avatar,
|
avatar,
|
||||||
..Default::default()
|
delete_attributes: user.remove_attributes.unwrap_or_default(),
|
||||||
|
insert_attributes,
|
||||||
})
|
})
|
||||||
.instrument(span)
|
.instrument(span)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -185,16 +248,23 @@ impl<Handler: BackendHandler> Mutation<Handler> {
|
|||||||
let handler = context
|
let handler = context
|
||||||
.get_admin_handler()
|
.get_admin_handler()
|
||||||
.ok_or_else(field_error_callback(&span, "Unauthorized group update"))?;
|
.ok_or_else(field_error_callback(&span, "Unauthorized group update"))?;
|
||||||
if group.id == 1 {
|
if group.id == 1 && group.display_name.is_some() {
|
||||||
span.in_scope(|| debug!("Cannot change admin group details"));
|
span.in_scope(|| debug!("Cannot change lldap_admin group name"));
|
||||||
return Err("Cannot change admin group details".into());
|
return Err("Cannot change lldap_admin group name".into());
|
||||||
}
|
}
|
||||||
|
let schema = handler.get_schema().await?;
|
||||||
|
let insert_attributes = group
|
||||||
|
.insert_attributes
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.map(|attr| deserialize_attribute(&schema.get_schema().group_attributes, attr))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
handler
|
handler
|
||||||
.update_group(UpdateGroupRequest {
|
.update_group(UpdateGroupRequest {
|
||||||
group_id: GroupId(group.id),
|
group_id: GroupId(group.id),
|
||||||
display_name: group.display_name,
|
display_name: group.display_name,
|
||||||
delete_attributes: Vec::new(),
|
delete_attributes: group.remove_attributes.unwrap_or_default(),
|
||||||
insert_attributes: Vec::new(),
|
insert_attributes,
|
||||||
})
|
})
|
||||||
.instrument(span)
|
.instrument(span)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -390,3 +460,91 @@ impl<Handler: BackendHandler> Mutation<Handler> {
|
|||||||
Ok(Success::new())
|
Ok(Success::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn create_group_with_details<Handler: BackendHandler>(
|
||||||
|
context: &Context<Handler>,
|
||||||
|
request: CreateGroupInput,
|
||||||
|
span: Span,
|
||||||
|
) -> FieldResult<super::query::Group<Handler>> {
|
||||||
|
let handler = context
|
||||||
|
.get_admin_handler()
|
||||||
|
.ok_or_else(field_error_callback(&span, "Unauthorized group creation"))?;
|
||||||
|
let schema = handler.get_schema().await?;
|
||||||
|
let attributes = request
|
||||||
|
.attributes
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.map(|attr| deserialize_attribute(&schema.get_schema().group_attributes, attr))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
let request = CreateGroupRequest {
|
||||||
|
display_name: request.display_name,
|
||||||
|
attributes,
|
||||||
|
};
|
||||||
|
let group_id = handler.create_group(request).await?;
|
||||||
|
Ok(handler
|
||||||
|
.get_group_details(group_id)
|
||||||
|
.instrument(span)
|
||||||
|
.await
|
||||||
|
.map(Into::into)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_attribute(
|
||||||
|
attribute_schema: &AttributeList,
|
||||||
|
attribute: AttributeValue,
|
||||||
|
) -> FieldResult<DomainAttributeValue> {
|
||||||
|
let attribute_type = attribute_schema
|
||||||
|
.get_attribute_type(&attribute.name)
|
||||||
|
.ok_or_else(|| anyhow!("Attribute {} is not defined in the schema", attribute.name))?;
|
||||||
|
if !attribute_type.1 && attribute.value.len() != 1 {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Attribute {} is not a list, but multiple values were provided",
|
||||||
|
attribute.name
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
let parse_int = |value: &String| -> FieldResult<i64> {
|
||||||
|
Ok(value
|
||||||
|
.parse::<i64>()
|
||||||
|
.with_context(|| format!("Invalid integer value {}", value))?)
|
||||||
|
};
|
||||||
|
let parse_date = |value: &String| -> FieldResult<chrono::NaiveDateTime> {
|
||||||
|
Ok(chrono::DateTime::parse_from_rfc3339(value)
|
||||||
|
.with_context(|| format!("Invalid date value {}", value))?
|
||||||
|
.naive_utc())
|
||||||
|
};
|
||||||
|
let parse_photo = |value: &String| -> FieldResult<JpegPhoto> {
|
||||||
|
Ok(JpegPhoto::try_from(value.as_str()).context("Provided image is not a valid JPEG")?)
|
||||||
|
};
|
||||||
|
let deserialized_values = match attribute_type {
|
||||||
|
(AttributeType::String, false) => Serialized::from(&attribute.value[0]),
|
||||||
|
(AttributeType::String, true) => Serialized::from(&attribute.value),
|
||||||
|
(AttributeType::Integer, false) => Serialized::from(&parse_int(&attribute.value[0])?),
|
||||||
|
(AttributeType::Integer, true) => Serialized::from(
|
||||||
|
&attribute
|
||||||
|
.value
|
||||||
|
.iter()
|
||||||
|
.map(parse_int)
|
||||||
|
.collect::<FieldResult<Vec<_>>>()?,
|
||||||
|
),
|
||||||
|
(AttributeType::DateTime, false) => Serialized::from(&parse_date(&attribute.value[0])?),
|
||||||
|
(AttributeType::DateTime, true) => Serialized::from(
|
||||||
|
&attribute
|
||||||
|
.value
|
||||||
|
.iter()
|
||||||
|
.map(parse_date)
|
||||||
|
.collect::<FieldResult<Vec<_>>>()?,
|
||||||
|
),
|
||||||
|
(AttributeType::JpegPhoto, false) => Serialized::from(&parse_photo(&attribute.value[0])?),
|
||||||
|
(AttributeType::JpegPhoto, true) => Serialized::from(
|
||||||
|
&attribute
|
||||||
|
.value
|
||||||
|
.iter()
|
||||||
|
.map(parse_photo)
|
||||||
|
.collect::<FieldResult<Vec<_>>>()?,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
Ok(DomainAttributeValue {
|
||||||
|
name: attribute.name,
|
||||||
|
value: deserialized_values,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ impl LLDAPFixture {
|
|||||||
display_name: None,
|
display_name: None,
|
||||||
first_name: None,
|
first_name: None,
|
||||||
last_name: None,
|
last_name: None,
|
||||||
|
attributes: None,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user