org.wso2.charon3.core.config.SCIMUserSchemaExtensionBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.charon3.core.config.SCIMUserSchemaExtensionBuilder.java

Source

/*
 * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * Licensed 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.charon3.core.config;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.wso2.charon3.core.exceptions.CharonException;
import org.wso2.charon3.core.exceptions.InternalErrorException;
import org.wso2.charon3.core.schema.SCIMAttributeSchema;
import org.wso2.charon3.core.schema.SCIMDefinitions;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/**
 * This class is to build the extension user schema though the config file.
 */
public class SCIMUserSchemaExtensionBuilder {

    private static SCIMUserSchemaExtensionBuilder configReader = new SCIMUserSchemaExtensionBuilder();
    // configuration map
    private static Map<String, ExtensionAttributeSchemaConfig> extensionConfig = new HashMap<String, ExtensionAttributeSchemaConfig>();
    // extension root attribute name
    String extensionRootAttributeName = null;
    // built schema map
    private static Map<String, SCIMAttributeSchema> attributeSchemas = new HashMap<String, SCIMAttributeSchema>();
    // extension root attribute schema
    private SCIMAttributeSchema extensionSchema = null;

    public static SCIMUserSchemaExtensionBuilder getInstance() {
        return configReader;
    }

    public SCIMAttributeSchema getExtensionSchema() {
        return extensionSchema;
    }

    /*
     * Logic goes here
     * @throws CharonException
     */
    public void buildUserSchemaExtension(String configFilePath) throws CharonException, InternalErrorException {
        readConfiguration(configFilePath);

        for (Map.Entry<String, ExtensionAttributeSchemaConfig> attributeSchemaConfig : extensionConfig.entrySet()) {
            // if there are no children its a simple attribute, build it
            if (!attributeSchemaConfig.getValue().hasChildren()) {
                buildSimpleAttributeSchema(attributeSchemaConfig.getValue());
            } else {
                // need to build child schemas first
                buildComplexAttributeSchema(attributeSchemaConfig.getValue());
            }
        }
        // now get the extension schema
        /*
         * Assumption : Final config in the configuration file is the extension
         * root attribute
         */
        extensionSchema = attributeSchemas.get(extensionRootAttributeName);
    }

    /*
     * This method reads configuration file and stores in the memory as an
     * configuration map
     *
     * @param configFilePath
     * @throws CharonException
     */
    private void readConfiguration(String configFilePath) throws CharonException {
        File provisioningConfig = new File(configFilePath);
        try {
            InputStream inputStream = new FileInputStream(provisioningConfig);
            //Scanner scanner = new Scanner(new FileInputStream(provisioningConfig));
            Scanner scanner = new Scanner(inputStream, "utf-8").useDelimiter("\\A");
            String jsonString = scanner.hasNext() ? scanner.next() : "";

            JSONArray attributeConfigArray = new JSONArray(jsonString);

            for (int index = 0; index < attributeConfigArray.length(); ++index) {
                JSONObject attributeConfig = attributeConfigArray.getJSONObject(index);
                ExtensionAttributeSchemaConfig attrubteConfig = new ExtensionAttributeSchemaConfig(attributeConfig);
                extensionConfig.put(attrubteConfig.getName(), attrubteConfig);

                /**
                 * NOTE: Assume last config is the root config
                 */
                if (index == attributeConfigArray.length() - 1) {
                    extensionRootAttributeName = attrubteConfig.getName();
                }
            }
            inputStream.close();
        } catch (FileNotFoundException e) {
            throw new CharonException(SCIMConfigConstants.SCIM_SCHEMA_EXTENSION_CONFIG + " file not found!", e);
        } catch (JSONException e) {
            throw new CharonException(
                    "Error while parsing " + SCIMConfigConstants.SCIM_SCHEMA_EXTENSION_CONFIG + " file!", e);
        } catch (IOException e) {
            throw new CharonException(
                    "Error while closing " + SCIMConfigConstants.SCIM_SCHEMA_EXTENSION_CONFIG + " file!", e);
        }
    }

    /*
     * Knows how to build a complex attribute
     *
     * @param config
     */
    private void buildComplexAttributeSchema(ExtensionAttributeSchemaConfig config) throws InternalErrorException {
        if (!attributeSchemas.containsKey(config.getName())) {
            String[] subAttributes = config.getSubAttributes();
            for (String subAttribute : subAttributes) {
                ExtensionAttributeSchemaConfig subAttribConfig = extensionConfig.get(subAttribute);
                if (!subAttribConfig.getType().equals(SCIMDefinitions.DataType.COMPLEX)) {
                    if (subAttribConfig.hasChildren()) {
                        String error = "A attribute of primitive type can not have sub attributes";
                        throw new InternalErrorException(error);
                    } else {
                        buildSimpleAttributeSchema(subAttribConfig);
                    }
                } else {
                    if (!(subAttribConfig.hasChildren())) {
                        String error = "A attribute of complex type should have sub attributes";
                        throw new InternalErrorException(error);
                    } else {
                        // need to build child schemas first
                        buildComplexAttributeSchema(subAttribConfig);
                    }
                }
            }
            // now all sub attributes must be already built
            buildComplexSchema(config);
        }
    }

    /*
     * Has the logic to iterate through child attributes
     *
     * @param config
     */
    private void buildComplexSchema(ExtensionAttributeSchemaConfig config) {
        String[] subAttributeNames = config.getSubAttributes();
        ArrayList<SCIMAttributeSchema> subAttributes = new ArrayList<SCIMAttributeSchema>();
        for (String subAttributeName : subAttributeNames) {
            subAttributes.add(attributeSchemas.get(subAttributeName));
        }
        SCIMAttributeSchema complexAttribute = createSCIMAttributeSchema(config, subAttributes);
        attributeSchemas.put(config.getName(), complexAttribute);
    }

    /*
     * Builds simple attribute schema
     *
     * @param config
     */
    private void buildSimpleAttributeSchema(ExtensionAttributeSchemaConfig config) {
        ArrayList<SCIMAttributeSchema> subAttributeList = new ArrayList<SCIMAttributeSchema>();
        if (!attributeSchemas.containsKey(config.getName())) {
            SCIMAttributeSchema attributeSchema = createSCIMAttributeSchema(config, subAttributeList);
            attributeSchemas.put(config.getName(), attributeSchema);
        }

    }

    /*
     * create SCIM Attribute Schema
     * @param attribute
     * @param subAttributeList
     * @return
     */
    public SCIMAttributeSchema createSCIMAttributeSchema(ExtensionAttributeSchemaConfig attribute,
            ArrayList<SCIMAttributeSchema> subAttributeList) {

        return SCIMAttributeSchema.createSCIMAttributeSchema(attribute.getURI(), attribute.getName(),
                attribute.getType(), attribute.getMultiValued(), attribute.description, attribute.required,
                attribute.caseExact, attribute.mutability, attribute.returned, attribute.uniqueness,
                attribute.canonicalValues, attribute.referenceTypes, subAttributeList);
    }

    static class ExtensionAttributeSchemaConfig {
        //unique identifier for the attribute
        private String uri;
        //name of the attribute
        private String name;
        //data type of the attribute
        private SCIMDefinitions.DataType type;
        //Boolean value indicating the attribute's plurality.
        private Boolean multiValued;
        //The attribute's human readable description
        private String description;
        //A Boolean value that specifies if the attribute is required
        private Boolean required;
        //A Boolean value that specifies if the String attribute is case sensitive
        private Boolean caseExact;
        //A SCIM defined value that specifies if the attribute's mutability.
        private SCIMDefinitions.Mutability mutability;
        //A SCIM defined value that specifies when the attribute's value need to be returned.
        private SCIMDefinitions.Returned returned;
        //A SCIM defined value that specifies the uniqueness level of an attribute.
        private SCIMDefinitions.Uniqueness uniqueness;
        //A list specifying the contained attributes. OPTIONAL.
        private String[] subAttributes;
        //A collection of suggested canonical values that MAY be used -OPTIONAL
        private ArrayList<String> canonicalValues;
        //A multi-valued array of JSON strings that indicate the SCIM resource types that may be referenced
        //only applicable for attributes that are of type "reference"
        private ArrayList<SCIMDefinitions.ReferenceType> referenceTypes;

        public String[] getSubAttributes() {
            return subAttributes;
        }

        public String getURI() {
            return uri;
        }

        public void setURI(String uri) {
            this.uri = uri;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public SCIMDefinitions.DataType getType() {
            return type;
        }

        public void setType(SCIMDefinitions.DataType type) {
            this.type = type;
        }

        public Boolean getMultiValued() {
            return multiValued;
        }

        public void setMultiValued(Boolean multiValued) {
            this.multiValued = multiValued;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

        public Boolean getRequired() {
            return required;
        }

        public void setRequired(Boolean required) {
            this.required = required;
        }

        public Boolean getCaseExact() {
            return caseExact;
        }

        public void setCaseExact(Boolean caseExact) {
            this.caseExact = caseExact;
        }

        public SCIMDefinitions.Mutability getMutability() {
            return mutability;
        }

        public void setMutability(SCIMDefinitions.Mutability mutability) {
            this.mutability = mutability;
        }

        public SCIMDefinitions.Returned getReturned() {
            return returned;
        }

        public void setReturned(SCIMDefinitions.Returned returned) {
            this.returned = returned;
        }

        public SCIMDefinitions.Uniqueness getUniqueness() {
            return uniqueness;
        }

        public void setUniqueness(SCIMDefinitions.Uniqueness uniqueness) {
            this.uniqueness = uniqueness;
        }

        public ArrayList<String> getCanonicalValues() {
            return canonicalValues;
        }

        public ArrayList<SCIMDefinitions.ReferenceType> getReferenceTypes() {
            return referenceTypes;
        }

        public boolean hasChildren() {
            return subAttributes != null ? true : false;
        }

        public ExtensionAttributeSchemaConfig(JSONObject attributeConfigJSON) throws CharonException {
            try {
                uri = attributeConfigJSON.getString(SCIMConfigConstants.ATTRIBUTE_URI);
                name = attributeConfigJSON.getString(SCIMConfigConstants.ATTRIBUTE_NAME);
                type = getDefinedDataType(attributeConfigJSON.getString(SCIMConfigConstants.DATA_TYPE));
                multiValued = attributeConfigJSON.getBoolean(SCIMConfigConstants.MULTIVALUED);
                description = attributeConfigJSON.getString(SCIMConfigConstants.DESCRIPTION);
                required = Boolean.parseBoolean(attributeConfigJSON.getString(SCIMConfigConstants.REQUIRED));
                caseExact = Boolean.parseBoolean(attributeConfigJSON.getString(SCIMConfigConstants.CASE_EXACT));
                mutability = getDefinedMutability(attributeConfigJSON.getString(SCIMConfigConstants.MUTABILITY));
                returned = getDefinedReturned(attributeConfigJSON.getString(SCIMConfigConstants.RETURNED));
                uniqueness = getDefinedUniqueness(attributeConfigJSON.getString(SCIMConfigConstants.UNIQUENESS));
                String subAttributesString = attributeConfigJSON.getString(SCIMConfigConstants.SUB_ATTRIBUTES);
                if (!"null".equalsIgnoreCase(subAttributesString)) {
                    subAttributes = subAttributesString.split(" ");
                }
                canonicalValues = setCanonicalValues(
                        attributeConfigJSON.getJSONArray(SCIMConfigConstants.CANONICAL_VALUES));
                referenceTypes = setReferenceTypes(
                        attributeConfigJSON.getJSONArray(SCIMConfigConstants.REFERENCE_TYPES));

            } catch (JSONException e) {
                throw new CharonException("Error while parsing extension configuration", e);
            }

        }

        /*
         * this builds the relevant data types according to what has configured in config file
         * @param input
         * @return
         */
        private SCIMDefinitions.DataType getDefinedDataType(String input) {
            SCIMDefinitions.DataType type = null;
            if ("STRING".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.DataType.STRING;
            } else if ("INTEGER".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.DataType.INTEGER;
            } else if ("DECIMAL".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.DataType.DECIMAL;
            } else if ("BOOLEAN".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.DataType.BOOLEAN;
            } else if ("DATETIME".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.DataType.DATE_TIME;
            } else if ("BINARY".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.DataType.BINARY;
            } else if ("REFERENCE".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.DataType.REFERENCE;
            } else if ("COMPLEX".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.DataType.COMPLEX;
            }
            return type;
        }

        /*
         * this builds the relevant mutability according to what has configured in config file
         * @param input
         * @return
         */
        private SCIMDefinitions.Mutability getDefinedMutability(String input) {
            SCIMDefinitions.Mutability type = null;
            if ("readWrite".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.Mutability.READ_WRITE;
            } else if ("readOnly".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.Mutability.READ_ONLY;
            } else if ("immutable".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.Mutability.IMMUTABLE;
            } else if ("writeOnly".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.Mutability.WRITE_ONLY;
            }
            return type;
        }

        /*
         * this builds the relevant returned type according to what has configured in config file
         * @param input
         * @return
         */
        private SCIMDefinitions.Returned getDefinedReturned(String input) {
            SCIMDefinitions.Returned type = null;
            if ("always".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.Returned.ALWAYS;
            } else if ("never".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.Returned.NEVER;
            } else if ("default".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.Returned.DEFAULT;
            } else if ("request".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.Returned.REQUEST;
            }
            return type;
        }

        /*
         * this builds the relevant uniqueness according to what has configured in config file
         * @param input
         * @return
         */
        private SCIMDefinitions.Uniqueness getDefinedUniqueness(String input) {
            SCIMDefinitions.Uniqueness type = null;
            if ("none".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.Uniqueness.NONE;
            } else if ("server".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.Uniqueness.SERVER;
            } else if ("global".equalsIgnoreCase(input)) {
                type = SCIMDefinitions.Uniqueness.GLOBAL;
            }
            return type;
        }

        /*
         * this builds the relevant canonical values according to what has configured in config file
         * @param input
         * @return
         */
        private ArrayList<String> setCanonicalValues(JSONArray input) throws JSONException {
            ArrayList<String> canonicalValues = new ArrayList<String>();
            JSONArray canonicalValuesList = input;
            for (int index = 0; index < canonicalValuesList.length(); ++index) {
                canonicalValues.add((String) canonicalValuesList.get(index));
            }
            return canonicalValues;
        }

        /*
         * this builds the relevant reference types according to what has configured in config file
         * @param input
         * @return
         */
        private ArrayList<SCIMDefinitions.ReferenceType> setReferenceTypes(JSONArray input) throws JSONException {
            ArrayList<SCIMDefinitions.ReferenceType> referenceTypes = new ArrayList<SCIMDefinitions.ReferenceType>();
            JSONArray referenceTypesList = input;

            for (int index = 0; index < referenceTypesList.length(); ++index) {
                String referenceValue = (String) referenceTypesList.get(index);

                if (referenceValue.equalsIgnoreCase("external")) {
                    referenceTypes.add(SCIMDefinitions.ReferenceType.EXTERNAL);
                } else if (referenceValue.equalsIgnoreCase("user")) {
                    referenceTypes.add(SCIMDefinitions.ReferenceType.USER);
                } else if (referenceValue.equalsIgnoreCase("group")) {
                    referenceTypes.add(SCIMDefinitions.ReferenceType.GROUP);
                } else if (referenceValue.equalsIgnoreCase("uri")) {
                    referenceTypes.add(SCIMDefinitions.ReferenceType.URI);
                }
            }
            return referenceTypes;
        }
    }

}