private static void CheckDuplicate(IEnumerable <SCIMRepresentationAttribute> existingAttributes, ICollection <SCIMRepresentationAttribute> newFlatAttributes) { var rootAttributes = SCIMRepresentation.BuildHierarchicalAttributes(newFlatAttributes); foreach (var newAttribute in rootAttributes) { if (existingAttributes.Any(a => a.IsSimilar(newAttribute, true))) { throw new SCIMDuplicateAttributeException(string.Format(Global.AttributeMustBeUnique, newAttribute.FullPath)); } } }
public static List <SCIMPatchResult> ApplyPatches(this SCIMRepresentation representation, ICollection <PatchOperationParameter> patches, bool ignoreUnsupportedCanonicalValues) { var result = new List <SCIMPatchResult>(); foreach (var patch in patches) { var scimFilter = SCIMFilterParser.Parse(patch.Path, representation.Schemas); var schemaAttributes = representation.Schemas.SelectMany(_ => _.Attributes); List <SCIMRepresentationAttribute> attributes = null; string fullPath = null; if (scimFilter != null) { var scimAttributeExpression = scimFilter as SCIMAttributeExpression; if (scimAttributeExpression == null) { throw new SCIMAttributeException(Global.InvalidAttributeExpression); } fullPath = scimAttributeExpression.GetFullPath(); schemaAttributes = representation.Schemas .Select(s => s.GetAttribute(fullPath)) .Where(s => s != null); attributes = scimAttributeExpression.EvaluateAttributes(representation.HierarchicalAttributes.AsQueryable(), true).ToList(); } else { attributes = representation.FlatAttributes.Where(h => h.IsLeaf()).ToList(); } var removeCallback = new Action <ICollection <SCIMRepresentationAttribute> >((attrs) => { foreach (var a in attrs) { var removedAttrs = representation.RemoveAttributeById(a); foreach (var removedAttr in removedAttrs) { result.Add(new SCIMPatchResult { Attr = removedAttr, Operation = SCIMPatchOperations.REMOVE, Path = removedAttr.FullPath }); } } }); switch (patch.Operation) { case SCIMPatchOperations.ADD: try { if (schemaAttributes == null || !schemaAttributes.Any()) { throw new SCIMNoTargetException(string.Format(Global.AttributeIsNotRecognirzed, patch.Path)); } var newAttributes = ExtractRepresentationAttributesFromJSON(representation.Schemas, schemaAttributes.ToList(), patch.Value, ignoreUnsupportedCanonicalValues); CheckDuplicate(attributes, newAttributes); removeCallback(attributes.Where(a => !a.SchemaAttribute.MultiValued && a.FullPath == fullPath).ToList()); var isAttributeExits = !string.IsNullOrWhiteSpace(fullPath) && attributes.Any(a => a.FullPath == fullPath); foreach (var newAttribute in newAttributes.OrderBy(a => a.GetLevel())) { var path = newAttribute.FullPath; var schemaAttr = newAttribute.SchemaAttribute; IEnumerable <SCIMRepresentationAttribute> parentAttributes = null; if (fullPath == path) { var attr = attributes.FirstOrDefault(a => a.FullPath == fullPath); if (attr != null) { var parent = representation.GetParentAttribute(attr); if (parent != null) { parentAttributes = new[] { parent }; } } else { parentAttributes = representation.GetParentAttributesByPath(fullPath).ToList(); } } if (schemaAttr.MultiValued && schemaAttr.Type != SCIMSchemaAttributeTypes.COMPLEX) { var filteredAttribute = attributes.FirstOrDefault(_ => _.FullPath == path); if (filteredAttribute != null) { newAttribute.AttributeId = filteredAttribute.AttributeId; } representation.AddAttribute(newAttribute); } else if (parentAttributes != null && parentAttributes.Any()) { foreach (var parentAttribute in parentAttributes) { representation.AddAttribute(parentAttribute, newAttribute); } } else { representation.FlatAttributes.Add(newAttribute); } attributes.Add(newAttribute); result.Add(new SCIMPatchResult { Attr = newAttribute, Operation = SCIMPatchOperations.ADD, Path = fullPath }); } if (TryGetExternalId(patch, out string externalId)) { representation.ExternalId = externalId; } } catch (SCIMSchemaViolatedException) { continue; } break; case SCIMPatchOperations.REMOVE: { if (scimFilter == null) { throw new SCIMNoTargetException(string.Format(Global.InvalidPath, patch.Path)); } if (SCIMFilterParser.DontContainsFilter(patch.Path) && patch.Value != null) { var excludedAttributes = ExtractRepresentationAttributesFromJSON(representation.Schemas, schemaAttributes.ToList(), patch.Value, ignoreUnsupportedCanonicalValues); excludedAttributes = SCIMRepresentation.BuildHierarchicalAttributes(excludedAttributes); attributes = attributes.Where(a => excludedAttributes.Any(ea => ea.IsSimilar(a, true))).ToList(); } removeCallback(attributes); } break; case SCIMPatchOperations.REPLACE: { if (TryGetExternalId(patch, out string externalId)) { representation.ExternalId = externalId; continue; } if (schemaAttributes == null || !schemaAttributes.Any()) { throw new SCIMNoTargetException(string.Format(Global.AttributeIsNotRecognirzed, patch.Path)); } var complexAttr = scimFilter as SCIMComplexAttributeExpression; if (complexAttr != null && !attributes.Any() && complexAttr.GroupingFilter != null) { throw new SCIMNoTargetException(Global.PatchMissingAttribute); } try { List <SCIMRepresentationAttribute> parents = new List <SCIMRepresentationAttribute>(); if (complexAttr != null) { var attr = attributes.First(a => a.FullPath == fullPath); var parent = string.IsNullOrWhiteSpace(attr.ParentAttributeId) ? attr : representation.GetParentAttribute(attributes.First(a => a.FullPath == fullPath)); if (parent != null) { parents = new List <SCIMRepresentationAttribute> { parent }; } } else { parents = representation.GetParentAttributesByPath(fullPath).ToList(); } if (scimFilter != null && parents.Any()) { foreach (var parent in parents) { var flatHiearchy = representation.GetFlatHierarchicalChildren(parent).ToList(); var scimAttributeExpression = scimFilter as SCIMAttributeExpression; var newAttributes = ExtractRepresentationAttributesFromJSON(representation.Schemas, schemaAttributes.ToList(), patch.Value, ignoreUnsupportedCanonicalValues); foreach (var newAttribute in newAttributes.OrderBy(l => l.GetLevel())) { if (!flatHiearchy.Any(a => a.FullPath == newAttribute.FullPath)) { var parentPath = SCIMRepresentation.GetParentPath(newAttribute.FullPath); var p = flatHiearchy.FirstOrDefault(a => a.FullPath == parentPath); if (p != null) { representation.AddAttribute(p, newAttribute); } else { representation.AddAttribute(newAttribute); } result.Add(new SCIMPatchResult { Attr = newAttribute, Operation = SCIMPatchOperations.ADD, Path = fullPath }); } } result.AddRange(Merge(flatHiearchy, newAttributes, fullPath)); } } else { var newAttributes = ExtractRepresentationAttributesFromJSON(representation.Schemas, schemaAttributes.ToList(), patch.Value, ignoreUnsupportedCanonicalValues); var flatHiearchy = representation.FlatAttributes.ToList(); var missingAttributes = newAttributes.Where(na => string.IsNullOrEmpty(na.ParentAttributeId) && !flatHiearchy.Any(fh => string.IsNullOrWhiteSpace(fh.ParentAttributeId) && fh.SchemaAttributeId == na.SchemaAttributeId)).ToList(); missingAttributes.ForEach((ma) => { representation.AddAttribute(ma); result.Add(new SCIMPatchResult { Attr = ma, Operation = SCIMPatchOperations.ADD, Path = ma.FullPath }); }); result.AddRange(Merge(flatHiearchy, newAttributes, fullPath)); } } catch (SCIMSchemaViolatedException) { continue; } } break; } } var displayNameAttr = representation.FlatAttributes.FirstOrDefault(a => a.FullPath == "displayName"); if (displayNameAttr != null) { representation.SetDisplayName(displayNameAttr.ValueString); } return(result); }