/*
* Copyright (c) 2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.identity.scim.common.utils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.charon.core.attributes.Attribute;
import org.wso2.charon.core.attributes.ComplexAttribute;
import org.wso2.charon.core.attributes.DefaultAttributeFactory;
import org.wso2.charon.core.attributes.MultiValuedAttribute;
import org.wso2.charon.core.attributes.SimpleAttribute;
import org.wso2.charon.core.exceptions.CharonException;
import org.wso2.charon.core.exceptions.NotFoundException;
import org.wso2.charon.core.objects.AbstractSCIMObject;
import org.wso2.charon.core.objects.Group;
import org.wso2.charon.core.objects.SCIMObject;
import org.wso2.charon.core.objects.User;
import org.wso2.charon.core.schema.AttributeSchema;
import org.wso2.charon.core.schema.ResourceSchema;
import org.wso2.charon.core.schema.SCIMAttributeSchema;
import org.wso2.charon.core.schema.SCIMConstants;
import org.wso2.charon.core.schema.SCIMResourceSchemaManager;
import org.wso2.charon.core.schema.SCIMSchemaDefinitions;
import org.wso2.charon.core.schema.SCIMSubAttributeSchema;
import org.wso2.charon.core.util.AttributeUtil;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* This class is responsible for converting SCIM attributes in a SCIM object to
* carbon claims and vice versa
*/
public class AttributeMapper {
private static Log log = LogFactory.getLog(AttributeMapper.class);
private static final boolean debug = log.isDebugEnabled();
/**
* Return claims as a map of <ClaimUri (which is mapped to SCIM attribute uri),ClaimValue>
* TODO : This method should be broken into smaller methods for code reuse
* TODO : This method SHOULD be recoded. MUST implement loops
*
* @param scimObject
* @return
*/
public static Map<String, String> getClaimsMap(AbstractSCIMObject scimObject) throws CharonException {
Map<String, String> claimsMap = new HashMap<>();
Map<String, Attribute> attributeList = scimObject.getAttributeList();
for (Map.Entry<String, Attribute> attributeEntry : attributeList.entrySet()) {
Attribute attribute = attributeEntry.getValue();
// if the attribute is password, skip it
if (SCIMConstants.UserSchemaConstants.PASSWORD.equals(attribute.getName())) {
continue;
}
if (attribute instanceof SimpleAttribute) {
String attributeURI = attribute.getAttributeURI();
if (((SimpleAttribute) attribute).getValue() != null) {
String attributeValue =
AttributeUtil.getStringValueOfAttribute(((SimpleAttribute) attribute).getValue(),
((SimpleAttribute) attribute).getDataType());
// set attribute URI as the claim URI
claimsMap.put(attributeURI, attributeValue);
}
} else if (attribute instanceof MultiValuedAttribute) {
MultiValuedAttribute multiValAttribute = (MultiValuedAttribute) attribute;
// get the URI of root attribute
String attributeURI = multiValAttribute.getAttributeURI();
// check if values are set as simple attributes
List<String> attributeValues = multiValAttribute.getValuesAsStrings();
if (CollectionUtils.isNotEmpty(attributeValues)) {
String values = null;
for (String attributeValue : attributeValues) {
if (values != null) {
values += attributeValue + ",";
} else {
values = attributeValue + ",";
}
}
claimsMap.put(attributeURI, values);
}
// check if values are set as complex values
// NOTE: in carbon, we only support storing of type and value of
// a multi-valued attribute
List<Attribute> complexAttributeList = multiValAttribute.getValuesAsSubAttributes();
for (Attribute complexAttribute : complexAttributeList) {
Map<String, Attribute> subAttributes =
((ComplexAttribute) complexAttribute).getSubAttributes();
SimpleAttribute typeAttribute =
(SimpleAttribute) subAttributes.get(SCIMConstants.CommonSchemaConstants.TYPE);
String valueAttriubuteURI;
// construct attribute URI
if (typeAttribute != null) {
String typeValue = (String) typeAttribute.getValue();
valueAttriubuteURI = attributeURI + "." + typeValue;
} else {
valueAttriubuteURI = attributeURI;
}
SimpleAttribute valueAttribute =
(SimpleAttribute) subAttributes.get(SCIMConstants.CommonSchemaConstants.VALUE);
if (valueAttribute != null && valueAttribute.getValue() != null) {
// put it in claims
claimsMap.put(valueAttriubuteURI,
AttributeUtil.getStringValueOfAttribute(valueAttribute.getValue(),
valueAttribute.getDataType()));
}
}
} else if (attribute instanceof ComplexAttribute) {
// reading attributes list of the complex attribute
ComplexAttribute complexAttribute = (ComplexAttribute) attribute;
Map<String, Attribute> attributes = null;
if (complexAttribute.getSubAttributes() != null &&
MapUtils.isNotEmpty(complexAttribute.getSubAttributes())) {
attributes = complexAttribute.getSubAttributes();
} else if (complexAttribute.getAttributes() != null &&
MapUtils.isNotEmpty(complexAttribute.getAttributes())) {
attributes = complexAttribute.getAttributes();
}
if (attributes != null) {
for (Attribute entry : attributes.values()) {
// if the attribute a simple attribute
if (entry instanceof SimpleAttribute) {
SimpleAttribute simpleAttribute = (SimpleAttribute) entry;
if (simpleAttribute != null && simpleAttribute.getValue() != null) {
claimsMap.put(entry.getAttributeURI(),
AttributeUtil.getStringValueOfAttribute(simpleAttribute.getValue(),
simpleAttribute.getDataType()));
}
} else if (entry instanceof MultiValuedAttribute) {
MultiValuedAttribute multiValAttribute = (MultiValuedAttribute) entry;
// get the URI of root attribute
String attributeURI = multiValAttribute.getAttributeURI();
// check if values are set as simple attributes
List<String> attributeValues = multiValAttribute.getValuesAsStrings();
if (CollectionUtils.isNotEmpty(attributeValues)) {
String values = null;
for (String attributeValue : attributeValues) {
if (values != null) {
values += attributeValue + ",";
} else {
values = attributeValue + ",";
}
}
claimsMap.put(attributeURI, values);
}
// check if values are set as complex values
// NOTE: in carbon, we only support storing of type and
// value of a multi-valued attribute
List<Attribute> complexAttributeList = multiValAttribute.getValuesAsSubAttributes();
for (Attribute complexAttrib : complexAttributeList) {
Map<String, Attribute> subAttributes =
((ComplexAttribute) complexAttrib).getSubAttributes();
SimpleAttribute typeAttribute =
(SimpleAttribute) subAttributes.get(SCIMConstants.CommonSchemaConstants.TYPE);
String valueAttriubuteURI;
// construct attribute URI
if (typeAttribute != null) {
String typeValue = (String) typeAttribute.getValue();
valueAttriubuteURI = attributeURI + "." + typeValue;
} else {
valueAttriubuteURI = attributeURI;
}
SimpleAttribute valueAttribute =
(SimpleAttribute) subAttributes.get(SCIMConstants.CommonSchemaConstants.VALUE);
if (valueAttribute != null && valueAttribute.getValue() != null) {
// put it in claims
claimsMap.put(valueAttriubuteURI,
AttributeUtil.getStringValueOfAttribute(valueAttribute.getValue(),
valueAttribute.getDataType()));
}
}
} else if (entry instanceof ComplexAttribute) {
// reading attributes list of the complex attribute
ComplexAttribute entryOfComplexAttribute = (ComplexAttribute) entry;
Map<String, Attribute> entryAttributes = null;
if (entryOfComplexAttribute.getSubAttributes() != null &&
MapUtils.isNotEmpty(entryOfComplexAttribute.getSubAttributes())) {
entryAttributes = entryOfComplexAttribute.getSubAttributes();
} else if (entryOfComplexAttribute.getAttributes() != null &&
MapUtils.isNotEmpty(entryOfComplexAttribute.getAttributes())) {
entryAttributes = entryOfComplexAttribute.getAttributes();
}
for (Attribute subEntry : entryAttributes.values()) {
// if the attribute a simple attribute
if (subEntry instanceof SimpleAttribute) {
SimpleAttribute simpleAttribute = (SimpleAttribute) subEntry;
if (simpleAttribute != null && simpleAttribute.getValue() != null) {
claimsMap.put(subEntry.getAttributeURI(),
AttributeUtil.getStringValueOfAttribute(simpleAttribute.getValue(),
simpleAttribute.getDataType()));
}
}
}
}
}
}
}
}
return claimsMap;
}
/**
* Construct the SCIM Object given the attribute URIs and attribute values of the object.
*
* @param attributes
* @param scimObjectType
* @return
*/
public static SCIMObject constructSCIMObjectFromAttributes(Map<String, String> attributes,
int scimObjectType)
throws CharonException, NotFoundException {
SCIMObject scimObject = null;
switch (scimObjectType) {
case SCIMConstants.GROUP_INT:
scimObject = new Group();
log.debug("Building Group Object");
break;
case SCIMConstants.USER_INT:
scimObject = new User();
log.debug("Building User Object");
break;
default:
break;
}
for (Map.Entry<String, String> attributeEntry : attributes.entrySet()) {
if (debug) {
log.debug("AttributeKey: " + attributeEntry.getKey() + " AttributeValue:" +
attributeEntry.getValue());
}
String attributeURI = attributeEntry.getKey();
String[] attributeURIParts = attributeURI.split(":");
String attributeNameString = attributeURIParts[attributeURIParts.length - 1];
String[] attributeNames = attributeNameString.split("\\.");
if (attributeNames.length == 1) {
//get attribute schema
AttributeSchema attributeSchema = getAttributeSchema(attributeNames[0], scimObjectType);
if (attributeSchema != null) {
//either simple valued or multi-valued with simple attributes
if (isMultivalued(attributeNames[0], scimObjectType)) {
//see whether multiple values are there
String value = attributeEntry.getValue();
String[] values = value.split(",");
//create attribute
MultiValuedAttribute multiValuedAttribute = new MultiValuedAttribute(
attributeSchema.getName());
//set values
multiValuedAttribute.setValuesAsStrings(Arrays.asList(values));
//set attribute in scim object
DefaultAttributeFactory.createAttribute(attributeSchema, multiValuedAttribute);
((AbstractSCIMObject) scimObject).setAttribute(multiValuedAttribute);
} else {
//convert attribute to relevant type
Object attributeValueObject = AttributeUtil.getAttributeValueFromString(
attributeEntry.getValue(), attributeSchema.getType());
//create attribute
SimpleAttribute simpleAttribute = new SimpleAttribute(attributeNames[0],
attributeValueObject);
DefaultAttributeFactory.createAttribute(attributeSchema, simpleAttribute);
//set attribute in the SCIM object
((AbstractSCIMObject) scimObject).setAttribute(simpleAttribute);
}
}
} else if (attributeNames.length == 2) {
//get parent attribute name
String parentAttributeName = attributeNames[0];
//get parent attribute schema
AttributeSchema parentAttributeSchema = getAttributeSchema(parentAttributeName,
scimObjectType);
/*differenciate between sub attribute of Complex attribute and a Multivalued attribute
with complex value*/
if (isMultivalued(parentAttributeName, scimObjectType)) {
//create map with complex value
Map<String, Object> complexValue = new HashMap<>();
complexValue.put(SCIMConstants.CommonSchemaConstants.TYPE, attributeNames[1]);
complexValue.put(SCIMConstants.CommonSchemaConstants.VALUE,
AttributeUtil.getAttributeValueFromString(attributeEntry.getValue(),
parentAttributeSchema.getType()));
//check whether parent multivalued attribute already exists
if (((AbstractSCIMObject) scimObject).isAttributeExist(parentAttributeName)) {
//create attribute value as complex value
MultiValuedAttribute multiValuedAttribute =
(MultiValuedAttribute) scimObject.getAttribute(parentAttributeName);
multiValuedAttribute.setComplexValue(complexValue);
} else {
//create the attribute and set it in the scim object
MultiValuedAttribute multivaluedAttribute = new MultiValuedAttribute(
parentAttributeName);
multivaluedAttribute.setComplexValue(complexValue);
DefaultAttributeFactory.createAttribute(parentAttributeSchema, multivaluedAttribute);
((AbstractSCIMObject) scimObject).setAttribute(multivaluedAttribute);
}
} else {
//sub attribute of a complex attribute
AttributeSchema subAttributeSchema = getAttributeSchema(attributeNames[1], scimObjectType);
//we assume sub attribute is simple attribute
SimpleAttribute simpleAttribute =
new SimpleAttribute(attributeNames[1],
AttributeUtil.getAttributeValueFromString(attributeEntry.getValue(),
subAttributeSchema.getType()));
DefaultAttributeFactory.createAttribute(subAttributeSchema, simpleAttribute);
//check whether parent attribute exists.
if (((AbstractSCIMObject) scimObject).isAttributeExist(parentAttributeName)) {
ComplexAttribute complexAttribute =
(ComplexAttribute) scimObject.getAttribute(parentAttributeName);
complexAttribute.setSubAttribute(simpleAttribute);
} else {
//create parent attribute and set sub attribute
ComplexAttribute complexAttribute = new ComplexAttribute(parentAttributeName);
complexAttribute.setSubAttribute(simpleAttribute);
DefaultAttributeFactory.createAttribute(parentAttributeSchema, complexAttribute);
((AbstractSCIMObject) scimObject).setAttribute(complexAttribute);
}
}
} else if (attributeNames.length == 3) {
//get immediate parent attribute name
String immediateParentAttributeName = attributeNames[1];
AttributeSchema immediateParentAttributeSchema = getAttributeSchema(immediateParentAttributeName,
scimObjectType);
/*differenciate between sub attribute of Complex attribute and a Multivalued attribute
with complex value*/
if (isMultivalued(immediateParentAttributeName, scimObjectType)) {
//create map with complex value
Map<String, Object> complexValue = new HashMap<>();
complexValue.put(SCIMConstants.CommonSchemaConstants.TYPE, attributeNames[1]);
complexValue.put(SCIMConstants.CommonSchemaConstants.VALUE,
AttributeUtil.getAttributeValueFromString(attributeEntry.getValue(),
immediateParentAttributeSchema.getType()));
//check whether parent multivalued attribute already exists
if (((AbstractSCIMObject) scimObject).isAttributeExist(immediateParentAttributeName)) {
//create attribute value as complex value
MultiValuedAttribute multiValuedAttribute =
(MultiValuedAttribute) scimObject.getAttribute(immediateParentAttributeName);
multiValuedAttribute.setComplexValue(complexValue);
} else {
//create the attribute and set it in the scim object
MultiValuedAttribute multivaluedAttribute = new MultiValuedAttribute(
immediateParentAttributeName);
multivaluedAttribute.setComplexValue(complexValue);
DefaultAttributeFactory.createAttribute(immediateParentAttributeSchema, multivaluedAttribute);
((AbstractSCIMObject) scimObject).setAttribute(multivaluedAttribute);
}
} else {
//sub attribute of a complex attribute
AttributeSchema subAttributeSchema = getAttributeSchema(attributeNames[2], attributeNames[1], scimObjectType);
//we assume sub attribute is simple attribute
SimpleAttribute simpleAttribute = new SimpleAttribute(attributeNames[2],
AttributeUtil.getAttributeValueFromString(attributeEntry.getValue(),
subAttributeSchema.getType()));
DefaultAttributeFactory.createAttribute(subAttributeSchema, simpleAttribute);
// check if the super parent exist
boolean superParentExist = ((AbstractSCIMObject) scimObject).isAttributeExist(attributeNames[0]);
if (superParentExist) {
ComplexAttribute superParentAttribute = (ComplexAttribute) ((AbstractSCIMObject) scimObject).getAttribute(attributeNames[0]);
// check if the immediate parent exist
boolean immediateParentExist = superParentAttribute.isSubAttributeExist(immediateParentAttributeName);
if (immediateParentExist) {
// both the parent and super parent exists
ComplexAttribute immediateParentAttribute = (ComplexAttribute) superParentAttribute.getSubAttribute(immediateParentAttributeName);
immediateParentAttribute.setSubAttribute(simpleAttribute);
} else { // immediate parent does not exist
ComplexAttribute immediateParentAttribute = new ComplexAttribute(immediateParentAttributeName);
immediateParentAttribute.setSubAttribute(simpleAttribute);
DefaultAttributeFactory.createAttribute(immediateParentAttributeSchema, immediateParentAttribute);
// created the immediate parent and now set to super
superParentAttribute.setSubAttribute(immediateParentAttribute);
}
} else { // now have to create both the super parent and immediate parent
// immediate first
ComplexAttribute immediateParentAttribute = new ComplexAttribute(immediateParentAttributeName);
immediateParentAttribute.setSubAttribute(simpleAttribute);
DefaultAttributeFactory.createAttribute(immediateParentAttributeSchema, immediateParentAttribute);
// now super parent
ComplexAttribute superParentAttribute = new ComplexAttribute(attributeNames[0]);
superParentAttribute.setSubAttribute(immediateParentAttribute);
AttributeSchema superParentAttributeSchema = getAttributeSchema(attributeNames[0], scimObjectType);
DefaultAttributeFactory.createAttribute(superParentAttributeSchema, superParentAttribute);
// now add the super to the scim object
((AbstractSCIMObject) scimObject).setAttribute(superParentAttribute);
}
}
}
}
return scimObject;
}
private static boolean isMultivalued(String attributeName, int scimObjectType) {
AttributeSchema attributeSchema = getAttributeSchema(attributeName, scimObjectType);
if (attributeSchema != null) {
return attributeSchema.getMultiValued();
}
return false;
}
private static AttributeSchema getAttributeSchema(String attributeName, int scimObjectType) {
return getAttributeSchema(attributeName, null, scimObjectType);
}
private static AttributeSchema getAttributeSchema(String attributeName, String parentAttributeName, int scimObjectType) {
ResourceSchema resourceSchema = getResourceSchema(scimObjectType);
if (resourceSchema != null) {
List<AttributeSchema> attributeSchemas = resourceSchema.getAttributesList();
for (AttributeSchema attributeSchema : attributeSchemas) {
if (attributeName.equals(attributeSchema.getName())) {
if (parentAttributeName == null ||
attributeSchema.getURI().contains(parentAttributeName)) {
return attributeSchema;
}
}
//check for sub attributes
List<SCIMSubAttributeSchema> subAttributeSchemas =
((SCIMAttributeSchema) attributeSchema).getSubAttributes();
if (CollectionUtils.isNotEmpty(subAttributeSchemas)) {
for (SCIMSubAttributeSchema subAttributeSchema : subAttributeSchemas) {
if (attributeName.equals(subAttributeSchema.getName())) {
if (parentAttributeName == null ||
subAttributeSchema.getURI().contains(parentAttributeName)) {
return subAttributeSchema;
}
}
}
}
// check for attributes of the attribute
List<SCIMAttributeSchema> attribSchemas = ((SCIMAttributeSchema) attributeSchema).getAttributes();
if (CollectionUtils.isNotEmpty(attribSchemas)) {
for (SCIMAttributeSchema attribSchema : attribSchemas) {
// if the attribute a simple attribute
if (attributeName.equals(attribSchema.getName())) {
return attribSchema;
}
// if the attribute a complex attribute having sub attributes
//check for sub attributes
List<SCIMSubAttributeSchema> subSubAttribSchemas =
((SCIMAttributeSchema) attribSchema).getSubAttributes();
if (CollectionUtils.isNotEmpty(subSubAttribSchemas)) {
for (SCIMSubAttributeSchema subSubAttribSchema : subSubAttribSchemas) {
if (attributeName.equals(subSubAttribSchema.getName())) {
if (parentAttributeName == null ||
subSubAttribSchema.getURI().contains(parentAttributeName)) {
return subSubAttribSchema;
}
}
}
}
// check for attributes
List<SCIMAttributeSchema> attributSchemas = ((SCIMAttributeSchema) attribSchema).getAttributes();
if (CollectionUtils.isNotEmpty(attributSchemas)) {
for (SCIMAttributeSchema atttribSchema : attributSchemas) {
if (attributeName.equals(atttribSchema.getName())) {
if (parentAttributeName == null ||
atttribSchema.getURI().contains(parentAttributeName)) {
return atttribSchema;
}
}
}
}
}
}
}
}
return null;
}
private static ResourceSchema getResourceSchema(int scimObjectType) {
ResourceSchema resourceSchema = null;
switch (scimObjectType) {
case SCIMConstants.USER_INT:
resourceSchema = SCIMResourceSchemaManager.getInstance().getUserResourceSchema();
break;
case SCIMConstants.GROUP_INT:
resourceSchema = SCIMSchemaDefinitions.SCIM_GROUP_SCHEMA;
break;
default:
break;
}
return resourceSchema;
}
//TODO: get the role list as well.
}