From 829c3f2bb16d324c5ddc1864069a68f2a8badc59 Mon Sep 17 00:00:00 2001 From: Valentin Tolmer Date: Sun, 5 Nov 2023 16:01:25 +0100 Subject: [PATCH] server: Prevent regular users from modifying non-editable attributes --- server/src/domain/handler.rs | 8 +++++--- server/src/infra/graphql/mutation.rs | 25 +++++++++++++++++-------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/server/src/domain/handler.rs b/server/src/domain/handler.rs index d0ca4e3..5f753e9 100644 --- a/server/src/domain/handler.rs +++ b/server/src/domain/handler.rs @@ -160,10 +160,12 @@ pub struct AttributeList { } impl AttributeList { + pub fn get_attribute_schema(&self, name: &str) -> Option<&AttributeSchema> { + self.attributes.iter().find(|a| a.name == name) + } + pub fn get_attribute_type(&self, name: &str) -> Option<(AttributeType, bool)> { - self.attributes - .iter() - .find(|a| a.name == name) + self.get_attribute_schema(name) .map(|a| (a.attribute_type, a.is_list)) } } diff --git a/server/src/infra/graphql/mutation.rs b/server/src/infra/graphql/mutation.rs index c584c76..cf4b76a 100644 --- a/server/src/infra/graphql/mutation.rs +++ b/server/src/infra/graphql/mutation.rs @@ -144,7 +144,7 @@ impl Mutation { .attributes .unwrap_or_default() .into_iter() - .map(|attr| deserialize_attribute(&schema.get_schema().user_attributes, attr)) + .map(|attr| deserialize_attribute(&schema.get_schema().user_attributes, attr, true)) .collect::, _>>()?; handler .create_user(CreateUserRequest { @@ -206,6 +206,7 @@ impl Mutation { let handler = context .get_writeable_handler(&user_id) .ok_or_else(field_error_callback(&span, "Unauthorized user update"))?; + let is_admin = context.validation_result.is_admin(); let avatar = user .avatar .map(|bytes| base64::engine::general_purpose::STANDARD.decode(bytes)) @@ -219,7 +220,7 @@ impl Mutation { .insert_attributes .unwrap_or_default() .into_iter() - .map(|attr| deserialize_attribute(&schema.get_schema().user_attributes, attr)) + .map(|attr| deserialize_attribute(&schema.get_schema().user_attributes, attr, is_admin)) .collect::, _>>()?; handler .update_user(UpdateUserRequest { @@ -257,7 +258,7 @@ impl Mutation { .insert_attributes .unwrap_or_default() .into_iter() - .map(|attr| deserialize_attribute(&schema.get_schema().group_attributes, attr)) + .map(|attr| deserialize_attribute(&schema.get_schema().group_attributes, attr, true)) .collect::, _>>()?; handler .update_group(UpdateGroupRequest { @@ -474,7 +475,7 @@ async fn create_group_with_details( .attributes .unwrap_or_default() .into_iter() - .map(|attr| deserialize_attribute(&schema.get_schema().group_attributes, attr)) + .map(|attr| deserialize_attribute(&schema.get_schema().group_attributes, attr, true)) .collect::, _>>()?; let request = CreateGroupRequest { display_name: request.display_name, @@ -491,11 +492,19 @@ async fn create_group_with_details( fn deserialize_attribute( attribute_schema: &AttributeList, attribute: AttributeValue, + is_admin: bool, ) -> FieldResult { - let attribute_type = attribute_schema - .get_attribute_type(&attribute.name) + let attribute_schema = attribute_schema + .get_attribute_schema(&attribute.name) .ok_or_else(|| anyhow!("Attribute {} is not defined in the schema", attribute.name))?; - if !attribute_type.1 && attribute.value.len() != 1 { + if !is_admin && !attribute_schema.is_editable { + return Err(anyhow!( + "Permission denied: Attribute {} is not editable by regular users", + attribute.name + ) + .into()); + } + if !attribute_schema.is_list && attribute.value.len() != 1 { return Err(anyhow!( "Attribute {} is not a list, but multiple values were provided", attribute.name @@ -515,7 +524,7 @@ fn deserialize_attribute( let parse_photo = |value: &String| -> FieldResult { Ok(JpegPhoto::try_from(value.as_str()).context("Provided image is not a valid JPEG")?) }; - let deserialized_values = match attribute_type { + let deserialized_values = match (attribute_schema.attribute_type, attribute_schema.is_list) { (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])?),