use of org.wso2.carbon.identity.api.server.idp.v1.model.Patch in project charon by wso2.
the class PatchOperationUtil method doPatchReplaceWithFiltersForLevelOne.
/*
* This method is to do patch replace for level one attributes with a filter present.
* @param oldResource
* @param attributeParts
* @param expressionNode
* @param operation
* @param schema
* @param decoder
* @return
* @throws BadRequestException
* @throws CharonException
* @throws JSONException
* @throws InternalErrorException
*/
private static AbstractSCIMObject doPatchReplaceWithFiltersForLevelOne(AbstractSCIMObject oldResource, String[] attributeParts, ExpressionNode expressionNode, PatchOperation operation, SCIMResourceTypeSchema schema, JSONDecoder decoder) throws BadRequestException, CharonException, JSONException, InternalErrorException {
Attribute attribute = oldResource.getAttribute(attributeParts[0]);
boolean isValueFound = false;
if (attribute != null) {
if (!attribute.getType().equals(SCIMDefinitions.DataType.COMPLEX)) {
// this is multivalued primitive case
if (attribute.getMultiValued()) {
List<Object> valuesList = ((MultiValuedAttribute) (attribute)).getAttributePrimitiveValues();
for (Iterator<Object> iterator = valuesList.iterator(); iterator.hasNext(); ) {
Object item = iterator.next();
// we only support "EQ" filter
if (item.equals(expressionNode.getValue())) {
if (attribute.getMutability().equals(SCIMDefinitions.Mutability.READ_ONLY) || attribute.getMutability().equals(SCIMDefinitions.Mutability.IMMUTABLE)) {
throw new BadRequestException("Can not remove a immutable attribute or a read-only attribute", ResponseCodeConstants.MUTABILITY);
} else {
iterator.remove();
isValueFound = true;
}
}
}
if (!isValueFound) {
throw new BadRequestException("No matching filter value found.", ResponseCodeConstants.NO_TARGET);
}
valuesList.add(operation.getValues());
} else {
throw new BadRequestException("Attribute : " + expressionNode.getAttributeValue() + " " + "is not a multivalued attribute.", ResponseCodeConstants.INVALID_PATH);
}
} else {
if (attribute.getMultiValued()) {
// this is for paths value as 'emails[value EQ vindula@wso2.com]'
// this is multivalued complex case
List<Attribute> subValues = ((MultiValuedAttribute) (attribute)).getAttributeValues();
if (subValues != null) {
for (Iterator<Attribute> subValueIterator = subValues.iterator(); subValueIterator.hasNext(); ) {
Attribute subValue = subValueIterator.next();
Map<String, Attribute> subValuesSubAttribute = ((ComplexAttribute) subValue).getSubAttributesList();
for (Iterator<Attribute> iterator = subValuesSubAttribute.values().iterator(); iterator.hasNext(); ) {
Attribute subAttribute = iterator.next();
if (subAttribute.getName().equals(expressionNode.getAttributeValue())) {
if (((SimpleAttribute) (subAttribute)).getValue().equals(expressionNode.getValue())) {
if (subValue.getMutability().equals(SCIMDefinitions.Mutability.READ_ONLY) || subValue.getMutability().equals(SCIMDefinitions.Mutability.IMMUTABLE)) {
throw new BadRequestException("Can not remove a immutable attribute or a read-only attribute", ResponseCodeConstants.MUTABILITY);
} else {
subValueIterator.remove();
isValueFound = true;
}
}
}
}
}
if (!isValueFound) {
throw new BadRequestException("No matching filter value found.", ResponseCodeConstants.NO_TARGET);
}
AttributeSchema attributeSchema = SchemaUtil.getAttributeSchema(attributeParts[0], schema);
subValues.add(decoder.buildComplexAttribute(attributeSchema, (JSONObject) operation.getValues()));
}
} else {
// this is complex attribute which has multi valued primitive sub attribute.
Attribute subAttribute = attribute.getSubAttribute(expressionNode.getAttributeValue());
if (subAttribute != null) {
if (subAttribute.getMultiValued()) {
List<Object> valuesList = ((MultiValuedAttribute) (subAttribute)).getAttributePrimitiveValues();
for (Iterator<Object> iterator = valuesList.iterator(); iterator.hasNext(); ) {
Object item = iterator.next();
// we only support "EQ" filter
if (item.equals(expressionNode.getValue())) {
if (subAttribute.getMutability().equals(SCIMDefinitions.Mutability.READ_ONLY) || subAttribute.getMutability().equals(SCIMDefinitions.Mutability.IMMUTABLE)) {
throw new BadRequestException("Can not remove a immutable attribute or a read-only attribute", ResponseCodeConstants.MUTABILITY);
} else {
iterator.remove();
isValueFound = true;
}
}
}
if (!isValueFound) {
throw new BadRequestException("No matching filter value found.", ResponseCodeConstants.NO_TARGET);
}
valuesList.add(operation.getValues());
} else {
throw new BadRequestException("Sub attribute : " + expressionNode.getAttributeValue() + " " + "is not a multivalued attribute.", ResponseCodeConstants.INVALID_PATH);
}
} else {
AttributeSchema subAttributeSchema = SchemaUtil.getAttributeSchema(attributeParts[0] + "." + expressionNode.getAttributeValue(), schema);
if (subAttributeSchema.getMultiValued()) {
((ComplexAttribute) (attribute)).setSubAttribute(decoder.buildPrimitiveMultiValuedAttribute(subAttributeSchema, (JSONArray) operation.getValues()));
} else {
if (subAttributeSchema.getMultiValued()) {
((ComplexAttribute) (attribute)).setSubAttribute(decoder.buildSimpleAttribute(subAttributeSchema, operation.getValues()));
}
}
}
}
}
} else {
// add the attribute
AttributeSchema attributeSchema = SchemaUtil.getAttributeSchema(attributeParts[0], schema);
if (attributeSchema != null) {
if (attributeSchema.getType().equals(SCIMDefinitions.DataType.COMPLEX)) {
if (attributeSchema.getMultiValued()) {
MultiValuedAttribute multiValuedAttribute = new MultiValuedAttribute(attributeSchema.getName());
DefaultAttributeFactory.createAttribute(attributeSchema, multiValuedAttribute);
String complexName = attributeSchema.getName() + "_" + SCIMConstants.DEFAULT + "_" + SCIMConstants.DEFAULT;
ComplexAttribute complexAttribute = new ComplexAttribute(complexName);
DefaultAttributeFactory.createAttribute(attributeSchema, complexAttribute);
AttributeSchema subValuesSubAttributeSchema = SchemaUtil.getAttributeSchema(attributeParts[0] + "." + expressionNode.getAttributeValue(), schema);
if (subValuesSubAttributeSchema != null) {
SimpleAttribute simpleAttribute = new SimpleAttribute(subValuesSubAttributeSchema.getName(), operation.getValues());
DefaultAttributeFactory.createAttribute(subValuesSubAttributeSchema, simpleAttribute);
complexAttribute.setSubAttribute(simpleAttribute);
multiValuedAttribute.setAttributeValue(complexAttribute);
oldResource.setAttribute(multiValuedAttribute);
} else {
throw new BadRequestException("No such attribute with name : " + attributeParts[0] + "." + expressionNode.getAttributeValue(), ResponseCodeConstants.INVALID_PATH);
}
} else {
throw new BadRequestException("Attribute : " + attributeParts[0] + " " + "is not a multivalued attribute.", ResponseCodeConstants.INVALID_PATH);
}
} else {
if (attributeSchema.getMultiValued()) {
// primitive case
MultiValuedAttribute multiValuedAttribute = new MultiValuedAttribute(attributeSchema.getName());
DefaultAttributeFactory.createAttribute(attributeSchema, multiValuedAttribute);
multiValuedAttribute.setAttributePrimitiveValue(operation.getValues());
oldResource.setAttribute(multiValuedAttribute);
} else {
throw new BadRequestException("Attribute : " + attributeParts[0] + " " + "is not a multivalued attribute.", ResponseCodeConstants.INVALID_PATH);
}
}
} else {
throw new BadRequestException("No such attribute with the name : " + attributeParts[0], ResponseCodeConstants.INVALID_PATH);
}
}
return oldResource;
}
use of org.wso2.carbon.identity.api.server.idp.v1.model.Patch in project charon by wso2.
the class PatchOperationUtil method doPatchReplaceOnPathWithoutFiltersForLevelOne.
/*
* This performs patch on resource based on the path value.No filter is specified here.
* And this is for level one attributes.
* @param oldResource
* @param schema
* @param decoder
* @param operation
* @param attributeParts
* @throws BadRequestException
* @throws CharonException
* @throws JSONException
* @throws InternalErrorException
*/
private static void doPatchReplaceOnPathWithoutFiltersForLevelOne(AbstractSCIMObject oldResource, SCIMResourceTypeSchema schema, JSONDecoder decoder, PatchOperation operation, String[] attributeParts) throws BadRequestException, CharonException, InternalErrorException {
Attribute attribute = oldResource.getAttribute(attributeParts[0]);
if (attribute != null) {
if (!attribute.getType().equals(SCIMDefinitions.DataType.COMPLEX)) {
if (!attribute.getMultiValued()) {
if (attribute.getMutability().equals(SCIMDefinitions.Mutability.READ_ONLY) || attribute.getMutability().equals(SCIMDefinitions.Mutability.IMMUTABLE)) {
throw new BadRequestException("Can not replace a immutable attribute or a read-only attribute", ResponseCodeConstants.MUTABILITY);
} else {
((SimpleAttribute) attribute).setValue(operation.getValues().toString());
}
} else {
if (attribute.getMutability().equals(SCIMDefinitions.Mutability.READ_ONLY) || attribute.getMutability().equals(SCIMDefinitions.Mutability.IMMUTABLE)) {
throw new BadRequestException("Can not replace a immutable attribute or a read-only attribute", ResponseCodeConstants.MUTABILITY);
} else {
((MultiValuedAttribute) attribute).deletePrimitiveValues();
JSONArray jsonArray = null;
try {
jsonArray = new JSONArray(operation.getValues());
} catch (JSONException e) {
throw new BadRequestException(ResponseCodeConstants.INVALID_SYNTAX);
}
for (int i = 0; i < jsonArray.length(); i++) {
try {
((MultiValuedAttribute) attribute).setAttributePrimitiveValue(jsonArray.get(i));
} catch (JSONException e) {
throw new BadRequestException(ResponseCodeConstants.INVALID_SYNTAX);
}
}
}
}
} else {
if (attribute.getMultiValued()) {
if (attribute.getMutability().equals(SCIMDefinitions.Mutability.READ_ONLY) || attribute.getMutability().equals(SCIMDefinitions.Mutability.IMMUTABLE)) {
throw new BadRequestException("Can not replace a immutable attribute or a read-only attribute", ResponseCodeConstants.MUTABILITY);
} else {
JSONArray jsonArray = null;
try {
jsonArray = new JSONArray(new JSONTokener(operation.getValues().toString()));
} catch (JSONException e) {
throw new BadRequestException(ResponseCodeConstants.INVALID_SYNTAX);
}
AttributeSchema attributeSchema = SchemaUtil.getAttributeSchema(attribute.getName(), schema);
MultiValuedAttribute newMultiValuedAttribute = decoder.buildComplexMultiValuedAttribute(attributeSchema, jsonArray);
oldResource.deleteAttribute(attribute.getName());
oldResource.setAttribute(newMultiValuedAttribute);
}
} else {
if (attribute.getMutability().equals(SCIMDefinitions.Mutability.READ_ONLY) || attribute.getMutability().equals(SCIMDefinitions.Mutability.IMMUTABLE)) {
throw new BadRequestException("Can not replace a immutable attribute or a read-only attribute", ResponseCodeConstants.MUTABILITY);
} else {
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject(new JSONTokener(operation.getValues().toString()));
} catch (JSONException e) {
throw new BadRequestException(ResponseCodeConstants.INVALID_SYNTAX);
}
AttributeSchema attributeSchema = SchemaUtil.getAttributeSchema(attribute.getName(), schema);
ComplexAttribute newComplexAttribute = null;
try {
newComplexAttribute = decoder.buildComplexAttribute(attributeSchema, jsonObject);
} catch (JSONException e) {
throw new BadRequestException(ResponseCodeConstants.INVALID_SYNTAX);
}
oldResource.deleteAttribute(attribute.getName());
oldResource.setAttribute(newComplexAttribute);
}
}
}
} else {
// Check whether the patched attributes are permissions of Roles.
if (schema.isSchemaAvailable(SCIMConstants.ROLE_SCHEMA_URI) && SCIMConstants.RoleSchemaConstants.PERMISSIONS.equalsIgnoreCase(attributeParts[0])) {
JSONArray permissionsJSONArray = getJsonArray(operation);
// Assign permissions to the Role.
if (oldResource instanceof Role) {
((Role) oldResource).setPermissions(decoder.toList(permissionsJSONArray));
}
}
// Create and add the attribute.
createAttributeOnResourceWithPathWithoutFiltersForLevelOne(oldResource, schema, decoder, operation, attributeParts);
}
}
use of org.wso2.carbon.identity.api.server.idp.v1.model.Patch in project charon by wso2.
the class AbstractValidator method validatePatchOperationEffectForRequiredSubAttributes.
/**
* Validation the patch operation effect on sub attributes of a scim attribute.
*
* @param oldAttribute Old scim attribute.
* @param newAttribute Updated scim attribute.
* @param attributeSchema Attribute schema of the attribute.
* @param scimObject Scim object.
* @throws CharonException When error occurred during the validation.
* @throws BadRequestException When error occurred due to the client issues.
*/
private static void validatePatchOperationEffectForRequiredSubAttributes(AbstractAttribute oldAttribute, AbstractAttribute newAttribute, AttributeSchema attributeSchema, AbstractSCIMObject scimObject) throws CharonException, BadRequestException {
if (newAttribute == null || attributeSchema == null || oldAttribute == null) {
return;
}
List<AttributeSchema> subAttributesSchemaList = attributeSchema.getSubAttributeSchemas();
if (subAttributesSchemaList == null) {
return;
}
for (AttributeSchema subAttributeSchema : subAttributesSchemaList) {
// Nothing to validate id the attribute schema has required=false.
if (!subAttributeSchema.getRequired()) {
continue;
}
if (newAttribute instanceof ComplexAttribute) {
// If the sub attribute contained in the old attribute but not in the new attribute.
if (newAttribute.getSubAttribute(subAttributeSchema.getName()) == null && oldAttribute.getSubAttribute(subAttributeSchema.getName()) != null) {
String error = "Required sub attribute: " + subAttributeSchema.getName() + " is missing in the SCIM Attribute: " + newAttribute.getName();
throw new BadRequestException(error, ResponseCodeConstants.INVALID_VALUE);
} else if (newAttribute.getSubAttribute(subAttributeSchema.getName()) instanceof SimpleAttribute) {
// If the attributes updated with "", that check is happening here.
if (StringUtils.isEmpty(((SimpleAttribute) newAttribute.getSubAttribute(subAttributeSchema.getName())).getValue().toString())) {
String error = "Required sub attribute: " + subAttributeSchema.getName() + " is missing in the SCIM Attribute: " + newAttribute.getName();
throw new BadRequestException(error, ResponseCodeConstants.INVALID_VALUE);
}
}
} else if (newAttribute instanceof MultiValuedAttribute) {
List<Attribute> newValues = ((MultiValuedAttribute) newAttribute).getAttributeValues();
for (Attribute value : newValues) {
if (value instanceof ComplexAttribute) {
if (value.getSubAttribute(subAttributeSchema.getName()) == null) {
String error = "Required sub attribute: " + subAttributeSchema.getName() + ", is missing in the SCIM Attribute: " + newAttribute.getName();
throw new BadRequestException(error, ResponseCodeConstants.INVALID_VALUE);
}
}
}
}
// Check for canonical attributes in groups.
validateCanonicalAttributesInScimObject(newAttribute, subAttributeSchema, scimObject);
/*
Following is only applicable for extension schema validation.
Extension schema also considered as complex Attribute.
Therefore, The Complex Attributes inside the extension will validate here.
*/
AbstractAttribute newSubAttribute = null;
AbstractAttribute oldSubAttribute = null;
if (newAttribute instanceof ComplexAttribute) {
newSubAttribute = (AbstractAttribute) (newAttribute).getSubAttribute(subAttributeSchema.getName());
oldSubAttribute = (AbstractAttribute) (oldAttribute).getSubAttribute(subAttributeSchema.getName());
} else if (newAttribute instanceof MultiValuedAttribute) {
List<Attribute> subAttributeList = ((MultiValuedAttribute) newAttribute).getAttributeValues();
for (Attribute subAttrbte : subAttributeList) {
if (subAttrbte.getName().equals(subAttributeSchema.getName())) {
newSubAttribute = (AbstractAttribute) subAttrbte;
}
}
}
List<AttributeSchema> subSubAttributesSchemaList = subAttributeSchema.getSubAttributeSchemas();
if (subSubAttributesSchemaList != null) {
validatePatchOperationEffectForRequiredSubAttributes(oldSubAttribute, newSubAttribute, subAttributeSchema, scimObject);
}
}
}
use of org.wso2.carbon.identity.api.server.idp.v1.model.Patch in project charon by wso2.
the class AbstractValidator method validatePatchOperationEffectForRequiredAttributes.
/**
* Validate whether scim object updates due to one patch operation, violate the required attributes conditions.
*
* @param oldObject Scim object before update.
* @param newObject Scim object after update.
* @param resourceSchema Schema for the scim resource.
* @throws BadRequestException When error occurred due to client issue.
* @throws CharonException When error occurred due to validation failure.
*/
public static void validatePatchOperationEffectForRequiredAttributes(AbstractSCIMObject oldObject, AbstractSCIMObject newObject, ResourceTypeSchema resourceSchema) throws BadRequestException, CharonException {
// Get attributes from schema.
List<AttributeSchema> attributeSchemaList = resourceSchema.getAttributesList();
// Get attribute list from old scim object.
Map<String, Attribute> oldAttributeList = oldObject.getAttributeList();
// Get attribute list from new scim object.
Map<String, Attribute> newAttributeList = newObject.getAttributeList();
for (AttributeSchema attributeSchema : attributeSchemaList) {
// Check for required attributes.
if (attributeSchema.getRequired()) {
/*
If the attribute is not present in the updated object but included in the old object,
it means the operation has removed the required attribute.
*/
if (!newAttributeList.containsKey(attributeSchema.getName()) && oldAttributeList.containsKey(attributeSchema.getName())) {
String error = "Required attribute " + attributeSchema.getName() + " is missing in the SCIM " + "Object.";
throw new BadRequestException(error, ResponseCodeConstants.INVALID_VALUE);
}
}
// Check for required sub attributes.
AbstractAttribute newAttribute = (AbstractAttribute) newAttributeList.get(attributeSchema.getName());
AbstractAttribute oldAttribute = (AbstractAttribute) oldAttributeList.get(attributeSchema.getName());
validatePatchOperationEffectForRequiredSubAttributes(oldAttribute, newAttribute, attributeSchema, newObject);
}
}
use of org.wso2.carbon.identity.api.server.idp.v1.model.Patch in project charon by wso2.
the class MeResourceManager method updateWithPATCH.
/**
* Update the user resource by sequence of operations.
*
* @param existingId
* @param scimObjectString
* @param userManager
* @param attributes
* @param excludeAttributes
* @return
*/
public SCIMResponse updateWithPATCH(String existingId, String scimObjectString, UserManager userManager, String attributes, String excludeAttributes) {
try {
if (userManager == null) {
String error = "Provided user manager handler is null.";
throw new InternalErrorException(error);
}
// obtain the json decoder.
JSONDecoder decoder = getDecoder();
// decode the SCIM User object, encoded in the submitted payload.
List<PatchOperation> opList = decoder.decodeRequest(scimObjectString);
SCIMResourceTypeSchema schema = getSchema(userManager);
// get the user from the user core
User oldUser = userManager.getMe(existingId, ResourceManagerUtil.getAllAttributeURIs(schema));
if (oldUser == null) {
throw new NotFoundException("No associated user exits in the user store.");
}
// make a copy of the original user
User copyOfOldUser = (User) CopyUtil.deepCopy(oldUser);
// make another copy of original user.
// this will be used to restore to the original condition if failure occurs.
User originalUser = (User) CopyUtil.deepCopy(copyOfOldUser);
User newUser = null;
for (PatchOperation operation : opList) {
if (operation.getOperation().equals(SCIMConstants.OperationalConstants.ADD)) {
if (newUser == null) {
newUser = (User) PatchOperationUtil.doPatchAdd(operation, getDecoder(), oldUser, copyOfOldUser, schema);
copyOfOldUser = (User) CopyUtil.deepCopy(newUser);
} else {
newUser = (User) PatchOperationUtil.doPatchAdd(operation, getDecoder(), newUser, copyOfOldUser, schema);
copyOfOldUser = (User) CopyUtil.deepCopy(newUser);
}
} else if (operation.getOperation().equals(SCIMConstants.OperationalConstants.REMOVE)) {
if (newUser == null) {
newUser = (User) PatchOperationUtil.doPatchRemove(operation, oldUser, copyOfOldUser, schema);
copyOfOldUser = (User) CopyUtil.deepCopy(newUser);
} else {
newUser = (User) PatchOperationUtil.doPatchRemove(operation, newUser, copyOfOldUser, schema);
copyOfOldUser = (User) CopyUtil.deepCopy(newUser);
}
} else if (operation.getOperation().equals(SCIMConstants.OperationalConstants.REPLACE)) {
if (newUser == null) {
newUser = (User) PatchOperationUtil.doPatchReplace(operation, getDecoder(), oldUser, copyOfOldUser, schema);
copyOfOldUser = (User) CopyUtil.deepCopy(newUser);
} else {
newUser = (User) PatchOperationUtil.doPatchReplace(operation, getDecoder(), newUser, copyOfOldUser, schema);
copyOfOldUser = (User) CopyUtil.deepCopy(newUser);
}
} else {
throw new BadRequestException("Unknown operation.", ResponseCodeConstants.INVALID_SYNTAX);
}
}
// get the URIs of required attributes which must be given a value
Map<String, Boolean> requiredAttributes = ResourceManagerUtil.getOnlyRequiredAttributesURIs((SCIMResourceTypeSchema) CopyUtil.deepCopy(schema), attributes, excludeAttributes);
User validatedUser = (User) ServerSideValidator.validateUpdatedSCIMObject(originalUser, newUser, schema);
newUser = userManager.updateMe(validatedUser, requiredAttributes);
// encode the newly created SCIM user object and add id attribute to Location header.
String encodedUser;
Map<String, String> httpHeaders = new HashMap<String, String>();
if (newUser != null) {
// create a deep copy of the user object since we are going to change it.
User copiedUser = (User) CopyUtil.deepCopy(newUser);
// need to remove password before returning
ServerSideValidator.validateReturnedAttributes(copiedUser, attributes, excludeAttributes);
encodedUser = getEncoder().encodeSCIMObject(copiedUser);
// add location header
httpHeaders.put(SCIMConstants.LOCATION_HEADER, getResourceEndpointURL(SCIMConstants.USER_ENDPOINT) + "/" + newUser.getId());
httpHeaders.put(SCIMConstants.CONTENT_TYPE_HEADER, SCIMConstants.APPLICATION_JSON);
} else {
String error = "Updated User resource is null.";
throw new CharonException(error);
}
// put the URI of the User object in the response header parameter.
return new SCIMResponse(ResponseCodeConstants.CODE_OK, encodedUser, httpHeaders);
} catch (NotFoundException e) {
return encodeSCIMException(e);
} catch (BadRequestException e) {
return encodeSCIMException(e);
} catch (NotImplementedException e) {
return encodeSCIMException(e);
} catch (CharonException e) {
return encodeSCIMException(e);
} catch (InternalErrorException e) {
return encodeSCIMException(e);
} catch (RuntimeException e) {
CharonException e1 = new CharonException("Error in performing the patch operation on user resource.", e);
return encodeSCIMException(e1);
}
}
Aggregations