public static void ApplyPatches(this SCIMRepresentation representation, ICollection <PatchOperationParameter> patches, bool ignoreUnsupportedCanonicalValues) { var queryableRepresentationAttributes = representation.Attributes.AsQueryable(); foreach (var patch in patches) { var scimFilter = SCIMFilterParser.Parse(patch.Path, representation.Schemas); var attributeExpression = scimFilter as SCIMAttributeExpression; if (attributeExpression == null) { throw new SCIMAttributeException(string.Format(Global.InvalidPath, patch.Path)); } var fullPath = attributeExpression.GetFullPath(); var schemaAttributes = representation.Schemas.Select(s => s.GetAttribute(fullPath)).Where(s => s != null); if (!schemaAttributes.Any()) { throw new SCIMAttributeException(string.Format(Global.UnknownPath, patch.Path)); } var schemaAttribute = schemaAttributes.First(); var attributes = GetRepresentationAttributeFromPath(queryableRepresentationAttributes, scimFilter).ToList(); if (!attributes.Any() && schemaAttribute.Type != SCIMSchemaAttributeTypes.COMPLEX && !schemaAttribute.MultiValued) { throw new SCIMAttributeException(Global.PatchMissingAttribute); } var removeCallback = new Action <ICollection <SCIMRepresentationAttribute> >((attrs) => { foreach (var a in attrs) { if (a.Parent != null) { a.Parent.Values.Remove(a); } else { representation.Attributes.Remove(a); } } }); if (patch.Operation == SCIMPatchOperations.REMOVE || patch.Operation == SCIMPatchOperations.REPLACE) { removeCallback(attributes); } if (patch.Operation == SCIMPatchOperations.ADD) { removeCallback(attributes.Where(a => !a.SchemaAttribute.MultiValued).ToList()); } if (patch.Operation == SCIMPatchOperations.ADD || patch.Operation == SCIMPatchOperations.REPLACE) { var newAttributes = ExtractRepresentationAttributesFromJSON(schemaAttribute, patch.Value, ignoreUnsupportedCanonicalValues); foreach (var newAttribute in newAttributes) { var parentAttribute = representation.GetParentAttribute(fullPath); var attribute = representation.GetAttribute(fullPath); if (schemaAttribute.Type == SCIMSchemaAttributeTypes.COMPLEX && parentAttribute != null) { parentAttribute.Values.Add(newAttribute); continue; } if (attribute != null && schemaAttribute.Type != SCIMSchemaAttributeTypes.COMPLEX) { if (schemaAttribute.Type == SCIMSchemaAttributeTypes.BOOLEAN) { foreach (var b in newAttribute.ValuesBoolean) { attribute.ValuesBoolean.Add(b); } } else if (schemaAttribute.Type == SCIMSchemaAttributeTypes.DATETIME) { foreach (var d in newAttribute.ValuesDateTime) { attribute.ValuesDateTime.Add(d); } } else if (schemaAttribute.Type == SCIMSchemaAttributeTypes.INTEGER) { foreach (var i in newAttribute.ValuesInteger) { attribute.ValuesInteger.Add(i); } } else if (schemaAttribute.Type == SCIMSchemaAttributeTypes.REFERENCE) { foreach (var r in newAttribute.ValuesReference) { attribute.ValuesReference.Add(r); } } else if (schemaAttribute.Type == SCIMSchemaAttributeTypes.STRING) { foreach (var s in newAttribute.ValuesString) { attribute.ValuesString.Add(s); } } else if (schemaAttribute.Type == SCIMSchemaAttributeTypes.DECIMAL) { foreach (var d in newAttribute.ValuesDecimal) { attribute.ValuesDecimal.Add(d); } } else if (schemaAttribute.Type == SCIMSchemaAttributeTypes.BINARY) { foreach (var b in newAttribute.ValuesBinary) { attribute.ValuesBinary.Add(b); } } } else { representation.Attributes.Add(newAttribute); } } } } }
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); }
public static void ApplyPatches(this SCIMRepresentation representation, ICollection <PatchOperationParameter> patches, bool ignoreUnsupportedCanonicalValues) { var queryableRepresentationAttributes = representation.Attributes.AsQueryable(); foreach (var patch in patches) { var scimFilter = SCIMFilterParser.Parse(patch.Path, representation.Schemas); var attributeExpression = scimFilter as SCIMAttributeExpression; var schemaAttributes = representation.Schemas.SelectMany(_ => _.Attributes); List <SCIMRepresentationAttribute> attributes = null; string fullPath = null; if (attributeExpression != null) { fullPath = attributeExpression.GetFullPath(); schemaAttributes = representation.Schemas .Select(s => s.GetAttribute(fullPath)) .Where(s => s != null); attributes = GetRepresentationAttributeFromPath(queryableRepresentationAttributes, scimFilter).ToList(); } else { attributes = queryableRepresentationAttributes.ToList(); } var removeCallback = new Func <ICollection <SCIMRepresentationAttribute>, List <SCIMRepresentationAttribute> >((attrs) => { var result = new List <SCIMRepresentationAttribute>(); foreach (var a in attrs) { if (a.Parent != null) { a.Parent.Values.Remove(a); result.Add(a.Parent); } else { representation.Attributes.Remove(a); } } return(result); }); switch (patch.Operation) { case SCIMPatchOperations.ADD: try { // If the target location already contains the value then do nothing ... if (schemaAttributes == null || !schemaAttributes.Any()) { throw new SCIMNoTargetException(string.Format(Global.AttributeIsNotRecognirzed, patch.Path)); } var newAttributes = ExtractRepresentationAttributesFromJSON(representation.Schemas, schemaAttributes.ToList(), patch.Value, ignoreUnsupportedCanonicalValues); var parentAttrs = removeCallback(attributes.Where(a => !a.SchemaAttribute.MultiValued && newAttributes.Any(_ => _.SchemaAttribute.Name == a.SchemaAttribute.Name)).ToList()); foreach (var newAttribute in newAttributes.Where(x => x.HasValue)) { var path = string.IsNullOrWhiteSpace(fullPath) ? newAttribute.SchemaAttribute.Name : fullPath; var schemaAttr = newAttribute.SchemaAttribute; var parentAttribute = representation.GetParentAttribute(path); if (schemaAttr.MultiValued && schemaAttr.Type != SCIMSchemaAttributeTypes.COMPLEX) { var filteredAttributes = attributes.Where(_ => _.GetFullPath() == path); foreach (var attribute in filteredAttributes) { if (schemaAttr.Type == SCIMSchemaAttributeTypes.BOOLEAN) { foreach (var b in newAttribute.ValuesBoolean) { attribute.ValuesBoolean.Add(b); } } else if (schemaAttr.Type == SCIMSchemaAttributeTypes.DATETIME) { foreach (var d in newAttribute.ValuesDateTime) { attribute.ValuesDateTime.Add(d); } } else if (schemaAttr.Type == SCIMSchemaAttributeTypes.INTEGER) { foreach (var i in newAttribute.ValuesInteger) { attribute.ValuesInteger.Add(i); } } else if (schemaAttr.Type == SCIMSchemaAttributeTypes.REFERENCE) { foreach (var r in newAttribute.ValuesReference) { attribute.ValuesReference.Add(r); } } else if (schemaAttr.Type == SCIMSchemaAttributeTypes.STRING) { foreach (var s in newAttribute.ValuesString) { attribute.ValuesString.Add(s); } } else if (schemaAttr.Type == SCIMSchemaAttributeTypes.DECIMAL) { foreach (var d in newAttribute.ValuesDecimal) { attribute.ValuesDecimal.Add(d); } } else if (schemaAttr.Type == SCIMSchemaAttributeTypes.BINARY) { foreach (var b in newAttribute.ValuesBinary) { attribute.ValuesBinary.Add(b); } } } } else if (parentAttribute != null) { if (parentAttrs.Any()) { foreach (var parentAttr in parentAttrs) { parentAttr.Add(newAttribute); } } else { parentAttribute.Add(newAttribute); } } else { if (!representation.ContainsAttribute(newAttribute)) { representation.Attributes.Add(newAttribute); } } } } catch (SCIMSchemaViolatedException) { continue; } break; case SCIMPatchOperations.REMOVE: { if (attributeExpression == null) { throw new SCIMNoTargetException(string.Format(Global.InvalidPath, patch.Path)); } removeCallback(attributes); } break; case SCIMPatchOperations.REPLACE: { if (schemaAttributes == null || !schemaAttributes.Any()) { throw new SCIMNoTargetException(string.Format(Global.AttributeIsNotRecognirzed, patch.Path)); } var complexAttr = attributeExpression as SCIMComplexAttributeExpression; if (complexAttr != null && !attributes.Any() && complexAttr.GroupingFilter != null) { throw new SCIMNoTargetException(Global.PatchMissingAttribute); } try { var newAttributes = ExtractRepresentationAttributesFromJSON(representation.Schemas, schemaAttributes.ToList(), patch.Value, ignoreUnsupportedCanonicalValues); var parentFullPath = attributes.First().Parent?.GetFullPath(); if (!string.IsNullOrWhiteSpace(parentFullPath)) { scimFilter = SCIMFilterParser.Parse(parentFullPath, representation.Schemas); var allAttrs = GetRepresentationAttributeFromPath(queryableRepresentationAttributes, scimFilter); foreach (var attr in allAttrs) { if (!attr.Values.Any(_ => attributes.Any(a => a.SchemaAttribute.Name == _.SchemaAttribute.Name))) { attr.Add(attributes.First()); } } } Merge(attributes, newAttributes); } catch (SCIMSchemaViolatedException) { continue; } } break; } } }