Skip to content

Commit

Permalink
add scim system schema
Browse files Browse the repository at this point in the history
address review comments
  • Loading branch information
amanda-ariyaratne committed Jan 21, 2025
1 parent 60f178d commit 97e9ab2
Show file tree
Hide file tree
Showing 15 changed files with 1,095 additions and 112 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
*
* WSO2 LLC. 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.charon3.core.config;

import org.apache.commons.lang.StringUtils;
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.AttributeSchema;

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

import static org.wso2.charon3.core.schema.SCIMConstants.SYSTEM_USER_SCHEMA_URI;

/**
* This class is to build the extension system schema though the config file.
*/
public class SCIMSystemSchemaExtensionBuilder extends ExtensionBuilder {

private static final SCIMSystemSchemaExtensionBuilder instance = new SCIMSystemSchemaExtensionBuilder();
private static final Map<String, ExtensionAttributeSchemaConfig> extensionConfig = new HashMap<>();
private static final Map<String, AttributeSchema> attributeSchemas = new HashMap<>();
private AttributeSchema extensionSchema = null;
String extensionRootAttributeName = null;
String extensionRootAttributeURI;

/**
* Get the instance of the SCIMSystemSchemaExtensionBuilder.
*
* @return The instance of the SCIMSystemSchemaExtensionBuilder.
*/
public static SCIMSystemSchemaExtensionBuilder getInstance() {

return instance;
}

/**
* Constructor of the SCIMSystemSchemaExtensionBuilder.
*/
private SCIMSystemSchemaExtensionBuilder() {

extensionRootAttributeURI = SYSTEM_USER_SCHEMA_URI;
}

/**
* Get the extension schema.
*
* @return The extension schema.
*/
public AttributeSchema getExtensionSchema() {

return extensionSchema;
}

/**
* Build the system schema extension from the config file.
*
* @param configFilePath Path to the config file.
* @throws CharonException If an error occurred while reading the config file.
* @throws InternalErrorException If an error occurred while building the schema.
*/
public void buildSystemSchemaExtension(String configFilePath) throws CharonException, InternalErrorException {

File provisioningConfig = new File(configFilePath);
try (InputStream configFilePathInputStream = new FileInputStream(provisioningConfig)) {
buildSystemSchemaExtension(configFilePathInputStream);
} catch (FileNotFoundException e) {
throw new CharonException(configFilePath + " file not found!", e);
} catch (JSONException e) {
throw new CharonException("Error while parsing " + configFilePath + " file!", e);
} catch (IOException e) {
throw new CharonException("Error while closing " + configFilePath + " file!", e);
}
}

/**
* Build the system schema extension from the input stream.
*
* @param inputStream The input stream.
* @throws CharonException If an error occurred while reading the configuration.
* @throws InternalErrorException If an error occurred while building the schema.
*/
public void buildSystemSchemaExtension(InputStream inputStream) throws CharonException, InternalErrorException {

readConfiguration(inputStream);
for (Map.Entry<String, ExtensionAttributeSchemaConfig> attributeSchemaConfig : extensionConfig.entrySet()) {
// If there are no children it is a simple attribute, build it.
if (!attributeSchemaConfig.getValue().hasChildren()) {
buildSimpleAttributeSchema(attributeSchemaConfig.getValue(), attributeSchemas);
} else {
// Need to build child schemas first.
buildComplexAttributeSchema(attributeSchemaConfig.getValue(), attributeSchemas, extensionConfig);
}
}

extensionSchema = attributeSchemas.get(extensionRootAttributeURI);
}

/**
* Read the configuration from the input stream.
*
* @param inputStream The input stream.
* @throws CharonException If an error occurred while reading the configuration.
*/
public void readConfiguration(InputStream inputStream) throws CharonException {

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 rawAttributeConfig = attributeConfigArray.getJSONObject(index);
ExtensionAttributeSchemaConfig schemaAttributeConfig =
new ExtensionAttributeSchemaConfig(rawAttributeConfig);
if (schemaAttributeConfig.getURI().startsWith(extensionRootAttributeURI)) {
extensionConfig.put(schemaAttributeConfig.getURI(), schemaAttributeConfig);
}

if (extensionRootAttributeURI.equals(schemaAttributeConfig.getURI())) {
extensionRootAttributeName = schemaAttributeConfig.getName();
}
}
}

@Override
public String getURI() {

return extensionRootAttributeURI;
}

@Override
protected boolean isRootConfig(ExtensionAttributeSchemaConfig config) {

return StringUtils.isNotBlank(extensionRootAttributeName)
&& extensionRootAttributeName.equals(config.getName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import java.util.Map;
import java.util.Scanner;

import static org.wso2.charon3.core.schema.SCIMConstants.ENTERPRISE_USER_SCHEMA_URI;

/**
* This class is to build the extension user schema though the config file.
*/
Expand All @@ -42,16 +44,24 @@ public class SCIMUserSchemaExtensionBuilder extends ExtensionBuilder {
private static Map<String, ExtensionAttributeSchemaConfig> extensionConfig = new HashMap<>();
// Extension root attribute name.
String extensionRootAttributeName = null;
String extensionRootAttributeURI = null;
String extensionRootAttributeURI;
// built schema map
private static Map<String, AttributeSchema> attributeSchemas = new HashMap<String, AttributeSchema>();
private static Map<String, AttributeSchema> attributeSchemas = new HashMap<>();
// extension root attribute schema
private AttributeSchema extensionSchema = null;

public static SCIMUserSchemaExtensionBuilder getInstance() {
return configReader;
}

/**
* Constructor to initialize the SCIMUserSchemaExtensionBuilder.
*/
public SCIMUserSchemaExtensionBuilder() {

this.extensionRootAttributeURI = ENTERPRISE_USER_SCHEMA_URI;
}

public AttributeSchema getExtensionSchema() {
return extensionSchema;
}
Expand Down Expand Up @@ -80,24 +90,20 @@ public void buildUserSchemaExtension(InputStream inputStream) throws CharonExcep

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

extensionSchema = attributeSchemas.get(extensionRootAttributeURI);
}

public void readConfiguration(InputStream inputStream) throws CharonException {
//Scanner scanner = new Scanner(new FileInputStream(provisioningConfig));

Scanner scanner = new Scanner(inputStream, "utf-8").useDelimiter("\\A");
String jsonString = scanner.hasNext() ? scanner.next() : "";

Expand All @@ -107,13 +113,11 @@ public void readConfiguration(InputStream inputStream) throws CharonException {
JSONObject rawAttributeConfig = attributeConfigArray.getJSONObject(index);
ExtensionAttributeSchemaConfig schemaAttributeConfig =
new ExtensionAttributeSchemaConfig(rawAttributeConfig);
extensionConfig.put(schemaAttributeConfig.getURI(), schemaAttributeConfig);
if (schemaAttributeConfig.getURI().startsWith(extensionRootAttributeURI)) {
extensionConfig.put(schemaAttributeConfig.getURI(), schemaAttributeConfig);
}

/**
* NOTE: Assume last config is the root config
*/
if (index == attributeConfigArray.length() - 1) {
extensionRootAttributeURI = schemaAttributeConfig.getURI();
if (extensionRootAttributeURI.equals(schemaAttributeConfig.getURI())) {
extensionRootAttributeName = schemaAttributeConfig.getName();
}
}
Expand All @@ -126,6 +130,7 @@ public String getURI() {
return extensionRootAttributeURI;
}

@Override
protected boolean isRootConfig(ExtensionAttributeSchemaConfig config) {

return StringUtils.isNotBlank(extensionRootAttributeName) &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -536,11 +536,12 @@ public ComplexAttribute buildComplexAttribute(AttributeSchema complexAttributeSc
List<AttributeSchema> subAttributeSchemas =
((AttributeSchema) complexAttributeSchema).getSubAttributeSchemas();
String userExtensionName = SCIMResourceSchemaManager.getInstance().getExtensionName();
String systemExtensionName = SCIMResourceSchemaManager.getInstance().getSystemSchemaExtensionName();
String customExtensionName = SCIMResourceSchemaManager.getInstance().getCustomSchemaExtensionURI();

//iterate through the complex attribute schema and extract the sub attributes.
for (AttributeSchema subAttributeSchema : subAttributeSchemas) {
//obtain the user defined value for given key- attribute schema name
//obtain the user defined value for given key-attribute schema name
Object attributeValObj = jsonObject.opt(subAttributeSchema.getName());
SCIMDefinitions.DataType subAttributeSchemaType = subAttributeSchema.getType();
if (subAttributeSchemaType.equals(STRING) || subAttributeSchemaType.equals(BINARY) ||
Expand Down Expand Up @@ -579,8 +580,9 @@ public ComplexAttribute buildComplexAttribute(AttributeSchema complexAttributeSc
//this case is only valid for the extension schema
//As according to the spec we have complex attribute inside complex attribute only for extension,
//we need to treat it separately
} else if ((complexAttributeSchema.getName().equals(userExtensionName)) ||
(complexAttributeSchema.getName().equals(customExtensionName))) {
} else if ((complexAttributeSchema.getName().equals(userExtensionName))
|| (complexAttributeSchema.getName().equals(customExtensionName))
|| (complexAttributeSchema.getName().equals(systemExtensionName))) {
if (subAttributeSchemaType.equals(COMPLEX)) {
//check for user defined extension's schema violation
List<AttributeSchema> subList = subAttributeSchema.getSubAttributeSchemas();
Expand Down Expand Up @@ -656,7 +658,6 @@ public ComplexAttribute buildComplexAttribute(AttributeSchema complexAttributeSc
return (ComplexAttribute) DefaultAttributeFactory.createAttribute(complexAttributeSchema, complexAttribute);
}


/*
* To build a complex type value of a Multi Valued Attribute. (eg. Email with value,type,primary as sub attributes
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -467,13 +467,13 @@ public String buildServiceProviderConfigJsonBody(HashMap<String, Object> config)

}

/*
* Build the user resource type json representation.
* @return
/**
* Build the user resource type json representation.
* @return json representation of user resource type.
*/
public String buildUserResourceTypeJsonBody() throws JSONException {
JSONObject userResourceTypeObject = new JSONObject();

JSONObject userResourceTypeObject = new JSONObject();
userResourceTypeObject.put(
SCIMConstants.CommonSchemaConstants.SCHEMAS, SCIMConstants.RESOURCE_TYPE_SCHEMA_URI);
userResourceTypeObject.put(
Expand All @@ -499,6 +499,15 @@ public String buildUserResourceTypeJsonBody() throws JSONException {
SCIMResourceSchemaManager.getInstance().getExtensionRequired());
schemaExtensions.put(extensionSchemaObject);

JSONObject systemSchemaObject = new JSONObject();
systemSchemaObject.put(
SCIMConstants.ResourceTypeSchemaConstants.SCHEMA_EXTENSIONS_SCHEMA,
SCIMResourceSchemaManager.getInstance().getSystemSchemaExtensionURI());
systemSchemaObject.put(
SCIMConstants.ResourceTypeSchemaConstants.SCHEMA_EXTENSIONS_REQUIRED,
SCIMResourceSchemaManager.getInstance().getSystemSchemaExtensionRequired());
schemaExtensions.put(systemSchemaObject);

// Add custom user schema as a schema extension.
if (StringUtils.isNotBlank(SCIMResourceSchemaManager.getInstance().getCustomSchemaExtensionURI())) {
JSONObject customSchemaObject = new JSONObject();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
import java.util.Map;

/**
* This is the interface for usermanager extension.
* An implementation can plugin their own user manager-(either LDAP based, DB based etc)
* This is the interface for user manager extension.
* An implementation can plug in their own user manager-(either LDAP based, DB based etc.)
* by implementing this interface and mentioning it in configuration.
*/
public interface UserManager {
Expand Down Expand Up @@ -265,6 +265,20 @@ default List<Attribute> getEnterpriseUserSchema() throws CharonException, NotImp
throw new NotImplementedException();
}

/**
* Retrieve schema of the system user.
*
* @return List of attributes of system user schema.
* @throws CharonException Charon exception.
* @throws NotImplementedException Functionality no implemented exception.
* @throws BadRequestException Bad request exception.
*/
default List<Attribute> getSystemUserSchema() throws CharonException, NotImplementedException,
BadRequestException {

throw new NotImplementedException();
}

/**
* Return Custom schema.
* @return Custom schema.
Expand Down
Loading

0 comments on commit 97e9ab2

Please sign in to comment.