7 Extending Security

This chapter provides information on extending Oracle Communications Unified Inventory Management (UIM) security to include APIs and entity data.

Security for other parts of UIM is handled by external systems, such as the Oracle WebLogic Server Administration Console and Oracle Enterprise Manager. See UIM System Administrator's Guide for more information.

Note:

For information on securing web services, see UIM Web Services Developer's Guide.

Securing APIs

By default, UIM APIs are not secured. To secure an API, you must extend UIM security to include the APIs. This can be done by:

Securing APIs through the SecurityValidation Aspect

You can secure access to an API by adding the API method to the UIM-provided security extension point (securityExtensionPoint) definition, which is defined within the SecurityValidation aspect in the aop.xml file. See Extending UIM Through Rulesets for more information about aspects and the aop.xml file.

At the framework level, security is automatically enforced at the security extension point for any methods that the extension point defines. For example, if no API methods are defined for the security extension point within the SecurityValidation aspect, then no APIs are secured. If 20 API methods are defined for the security extension point within the SecurityValidation aspect, then those 20 API methods are validated/secured.

Example 7-1 shows API security definitions that are provided as a comment in the aop.xml file. If uncommented, these definitions would secure the createConditions, updateConditions, and deleteConditions APIs using the SecurityValidation aspect through the specified extension point (securityExtensionPoint). The result of this entry in the aop.xml file is that security validations are run prior to every call to the createConditions, updateConditions, and deleteConditions APIs.

You can use this example as a starting point by modifying it and uncommenting it in the aop.xml file to secure any API.

Example 7-1 SecurityValidation Aspect

<concrete-aspect
    name="oracle.communications.extensibility.extension.SecurityValidation"
    extends=
    "oracle.communications.extensibility.extension.SecurityValidationExtension" >
    <pointcut name="securityExtensionPoint" expression="
          call(public *
               oracle.communications.inventory.api.consumer.ConditionManager.
                   createConditions(java.util.Collection))
          call(public *
               oracle.communications.inventory.api.consumer.ConditionManager.
                   updateConditions(java.util.Collection))
          call(public *
               oracle.communications.inventory.api.consumer.ConditionManager.
                   deleteConditions(java.util.Collection))"/>
</concrete-aspect>

Creating the Global Extension Point

Global extension points are created in Oracle Communications Design Studio. For information on global extension points, see Extending UIM Through Rulesets. For instructions on how to create a global extension point, see the Design Studio Help.

When using this approach to secure APIs, you must also create one global extension point that defines the handleSecurityViolation API, which enables the rulesets to generate errors. The handleSecurityViolation API is located in the oracle.communications.inventory.api.admin.SecurityManager package. Example 7-2 shows the API method signature to use when defining the global extension point for the handleSecurityViolation API.

Example 7-2 Custom Global Extension Point Signature

public void oracle.communications.inventory.api.admin.SecurityManager.
handleSecurityViolation([])

Creating the Global Ruleset Extension Point

Global ruleset extension points are created in Design Studio. For information on global ruleset extension points, see Extending UIM Through Rulesets. For instructions on how to create a global ruleset extension point, see the Design Studio Help.

After you have created the ruleset and global extension point in Design Studio, you must also create the corresponding global ruleset extension point in Design Studio. A global ruleset extension point associates a ruleset with a global extension point, so the global extension point knows which ruleset to run.

Securing APIs through Rulesets and Extension Points

You can also secure access to an API by creating custom rulesets that run at specified extension points. The custom rulesets set permissions for an API, enforces any permissions that are set for an API, and logs error messages whenever a security violation is detected.

Setting and enforcing API permissions through rulesets is done in the same manner as setting and enforcing entity data permissions. See "Securing Entity Data through Rulesets and Extension Points" for more information.

Securing Entity Data

By default, UIM entity data is not secured. To secure entity data, you must extend UIM security to control data access to individual entities. This is done by creating custom rulesets that run at specified extension points. The custom rulesets set permissions or partitions for an entity, enforces any permissions or partitions that are set for an entity, and logs error messages whenever a security violation is detected.

About Entity Access Control

To configure access control for an entity, the entity must be declared as access-controlled in the metadata. For example, the following is an excerpt from the metadata that shows the Equipment entity definition, which is declared as access-controlled:

<entity type="ocim:Equipment"
interface="oracle.communications.inventory.api.entity.Equipment"
accessControlled="true">

Most, but not all, entities are declared as access-controlled. If you want to configure access control for an entity that is not declared as access-controlled in the metadata, you must first extend the data model to declare the entity as access-controlled. See "Extending the Data Model" for more information.

Access-controlled entities define additional attributes that contain security-specific data. For example, access-controlled entities define the owner, permissions, and partition attributes. Access-controlled entities also extend the AccessControlled class, so each entity class has access to the setOwner(), setPermissions(), and setPartition() methods defined in the AccessControlled parent class. The value of these attributes can be set by custom rulesets that call these methods.

Note:

When controlling access to a range of entities, the ruleset custom code must iterate through the range and call the method for each entity in the range. See "Securing Entity Data for a Range of Entities Example" for more information.

Securing Entity Data through Rulesets and Extension Points

You can secure entity data through rulesets and extension points by:

Setting Permissions in a Custom Ruleset

Note:

This section also applies to securing APIs through permissions.

To control data access to an entity through permissions, set the permissions attribute for the entity through custom code that calls the setPermissions() method, which is defined as:

public void setPermissions(String acl);

This method is defined in the oracle.communications.inventory.api.AccessControlled class, which is the parent class of all entities that are declared as access-controlled in the metadata. In the custom ruleset, you can call this method on the parent class (AccessControlled) or on the child class (EntityName, such as TelephoneNumber, Equipment, and so forth).

See "Creating Custom Rulesets and Extension Points" for examples of setPermissions() method calls.

Understanding ACL

The permissions are defined as an access control list (ACL). The ACL is a Java string that specifies who is allowed to access an object and what operations they can perform on an object.

An ACL consists of one or more entry statements separated by semicolons. Each statement includes the type of permission (allow or deny), the permission (r for read or w for write), and a principal or role to whom the permission is granted. (A principal is a user or group. It is easier to manage permissions at the level of roles, however.)

The syntax is as follows:

allow|deny r|w = principal|roles[role1,role2,role3...];
 

where principal is the name of any user or group and role is the name of any role.

Example 7-3 shows the ACL syntax in Extended Backus-Naur Format (EBNF).

Example 7-3 ACL Syntax

acl:= acl_entry (';'acl_entry)*
acl_entry:=('allow'|'deny')permission? target_list
permission:= ('r'|'w')'='
target_list:= target (','target)*
target:= principal|'roles' '['role_list']'
role_list:= role(','role)*

Note the following about the ACL:

  • The ACL is evaluated left to right until a security decision of allow or deny is enforced.

  • If no permission is stated, allow is implied.

    Allowing write access implies allowing read access.

  • Denying read access also implies denying write access.

  • Any user having the uimuser role is permitted full access to an entity, regardless of the permissions set for the entity. This role exists by default and is defined as a superuser.

Table 7-1 lists examples of permissions and how they work together.

Table 7-1 Examples of Permissions

Permissions Explanation

Allow roles[billing_admin]; deny all

Anyone assigned to the billing_admin role can read or write the entity, but no one else.

Allow all

Everyone can read or write the entity. The same can be achieved by simply not defining permissions for the entity.

Allow r=all,w=roles[location_admin]

Everyone can read the entity; anyone having the location_admin role can write the entity.

Deny all

No one may can the entity except superusers.

Deny w=all

No one can write the entity except superusers, but everyone may read the entity.

Deny roles[OrderEntryUser,GeoMapAdmin User]

Anyone having either the OrderEntryUser or the GeoMapAdminUser role is denied access. Everyone else has full access.

Setting Partitions in a Custom Ruleset

To control data access to an entity through partitions, set the partition attribute for the entity through custom code that calls the setPartition() method, which is defined as:

public void setPartition(String partition);

This method is defined in the oracle.communications.inventory.api.AccessControlled class, which is the parent class of all entities that are declared as access-controlled in the metadata. In the custom ruleset, you can call this method on the parent class (AccessControlled) or on the child class (EntityName, such as TelephoneNumber, Equipment, and so forth).

See "Creating Custom Rulesets and Extension Points" for examples of setPartition() method calls.

Configuring Partitions

To control data access to an entity through partitions, some additional configuration is required:

  1. In the WebLogic Server Administration Console, you must define a user group within a security realm. The group you define represents a data partition in UIM. For instructions on how to do this, see UIM System Administrator's Guide.

    Caution:

    The group name must begin with ora_uim_partition# to be recognized by UIM. For example, if you define a group name of ora_uim_partition#myPartition, then the custom ruleset would set the partition to /myPartition.

  2. In the UIM_Home/config/system-config.properties file, set the uim.security.filter.enabled property to true, as shown here:

    uim.security.filter.enabled=true
    

Enforcing Security in a Custom Ruleset

Note:

This section also applies to enforcing security permissions set for APIs.

API access that is controlled through set permissions, and entity data access that is controlled through set permissions and partitions is enforced through custom code that calls the checkPermissions() method, which is defined as:

public void checkPermissions(String perm, AccessControlled instance);

This method is defined in the oracle.communications.inventory.api.framework.security.UserEnvironment class. The checkPermissions() method calls the hasAccessToPartition() method, so the checkPermissions verifies access for both permissions and partitions.

If a security violation is detected, the application throws a java.security.AccessControlException. The custom code catches and logs the AccessControlException by calling the error() method, which is defined as:

public void error(String s, Throwable t);

This method is defined in the oracle.communications.inventory.api.framework.logging.Log class.

See "Creating Custom Rulesets and Extension Points" for examples of error() method calls.

Creating Custom Rulesets and Extension Points

When using custom rulesets to secure an API or entity data, you must also create an extension point or global extension point to run the ruleset. The following sections provide additional information and examples for creating the ruleset and extension point. If creating a global extension point, see "Creating the Global Extension Point" for more information.

Creating Custom Rulesets

Rulesets are created in Oracle Communications Design Studio. Rulesets can be written using Drools or Groovy. This section provides several custom ruleset examples, and each example is shown twice; once using Drools and once using Groovy. For information on rulesets, and using Drools and Groovy, see "Extending UIM Through Rulesets". For instructions on how to create a ruleset, see the Design Studio Help.

Note:

In the following custom ruleset examples, all import statements are omitted.

Securing APIs Example

Example 7-4 shows a custom ruleset that secures access to the createConditions, updateConditions, and deleteConditions APIs by setting permissions. The ruleset defines four rules:

  • Default Condition Validation Rule

    This rule always runs and calls the validate() method, which simply logs the method name and logs the user that is calling the method.

  • Create Condition Validation Rule

    This rule runs only when the ruleset is called from an extension point that defines the createConditions API. This rule calls the setConditionsOwner() method, which sets permissions.

  • Update Condition Validation Rule

    This rule runs only when the ruleset is called from an extension point that defines the updateConditions API. This rule calls the validateConditionsOwner() method, which enforces security and logs an error if a security violation is detected.

  • Delete Condition Validation Rule

    This rule runs only when the ruleset is called from an extension point that defines the deleteConditions API. This rule also calls the validateConditionsOwner() method, which enforces security and logs an error if a security violation is detected.

Note:

Example 7-4 uses the context.getArguments() method. However, depending on how you configure your custom ruleset to run (before, after, or instead of the method your extension point defines), you may need to use the context.getReturnValue() method instead.

For example, when the ruleset runs before the method the extension point defines, use context.getArguments() because the return value is always empty in this scenario. When the ruleset runs after the method the extension point defines, use context.getReturnValue() because the data in the context argument that was passed to the ruleset may have changed through the use of context.setArguments().

See "ExtensionPointRuleContext.returnValue" for more information.

Example 7-4 Custom Ruleset Using Drools

package oracle.communications.rules;
  .
  .
  .
global Log log;

//--------------------------------------------------------------------
// FUNCTIONS
//--------------------------------------------------------------------
function void validate(ExtensionPointRuleContext context, Log logger, UserEnvironment env) {
    logger.info("", new String[]{"********"});
    logger.info("", new String[]{"method: ", context.getMethodName()});
    logger.info("", new String[]{"user: ", env.getUserName()});
    logger.info("", new String[]{"********"});
}

function void setConditionsOwner(ExtensionPointRuleContext context, Log logger, UserEnvironment env) {
    logger.info("", new String[]{"********"});
    logger.info("", new String[]{"setConditionsOwner"});
    Collection conditions = (Collection) context.getArguments()[0];
    if (conditions != null && !conditions.isEmpty()) 
    {
        String owner = env.getUserName();
        for (Iterator itr = conditions.iterator(); itr.hasNext();) {
            Condition cond = (Condition) itr.next();
            if (cond instanceof AccessControlled) {
                ((AccessControlled)cond).setOwner( owner );
                ((AccessControlled)cond).setPermissions("deny contractEmployees");
            }
        }
    }
    logger.info("", new String[]{"********"});
}

function void validateConditionsOwner(ExtensionPointRuleContext context, Log logger, UserEnvironment env) {
    logger.info("", new String[]{"********"});
    logger.info("", new String[]{"validateConditionsOwner"});
    Collection conditions = (Collection) context.getArguments()[0];
    
    String methodName = context.getMethodName();
    String targetName = context.getDeclaringTargetType().getSimpleName();
    String policyName = targetName + "." + methodName;
    logger.info("", new String[]{"policyName: ", policyName});
    
    if (conditions != null && !conditions.isEmpty()) {
        for (Iterator itr = conditions.iterator(); itr.hasNext();) {
            Condition cond = (Condition) itr.next();
            if (cond instanceof AccessControlled) {
                try {
                    env.checkPermissions( policyName, (AccessControlled) cond );
                }
                catch (java.security.AccessControlException ace) {
                    logger.error("", new String[] {ace.getMessage()});
                    logger.error("", new String[] {"My error message for: " + cond.toString()});
                }
            }
        }
    }
    logger.info("", new String[]{"********"});
}
 
//--------------------------------------------------------------------
// RULES
//--------------------------------------------------------------------
rule "Default Condition Validation Rule"
    salience 10
    when
        context: ExtensionPointRuleContext()
    then 
        UserEnvironment env = UserEnvironmentFactory.getUserEnvironment();
        RuleDebug.breakPoint(context);
        RuleDebug.breakPoint(env);
        validate(context, log, env); 
end

rule "Create Condition Validation Rule"
    salience 1
    when
        context: ExtensionPointRuleContext(methodName == "createConditions")
    then
        UserEnvironment env = UserEnvironmentFactory.getUserEnvironment();
        setConditionsOwner(context, log, env);
end

rule "Update Condition Validation Rule"
    salience 1
    when
        context: ExtensionPointRuleContext(methodName == "updateConditions")
    then
        UserEnvironment env = UserEnvironmentFactory.getUserEnvironment();
        validateConditionsOwner(context, log, env);
end


rule "Delete Condition Validation Rule"
    salience 1
    when
        context: ExtensionPointRuleContext(methodName == "deleteConditions")
    then
        UserEnvironment env = UserEnvironmentFactory.getUserEnvironment();
        validateConditionsOwner(context, log, env);
end
 

Example 7-5 shows the same custom ruleset content-wise, but using Groovy instead of Drools. Only the rules section is shown. (The functions section content is the same; the only difference between Drools and Groovy is the syntax of the function definition itself: Drools uses the key word function and Groovy uses the key word def.)

Example 7-5 Custom Ruleset Using Groovy

//--------------------------------------------------------------------
// RULES
//--------------------------------------------------------------------
 
UserEnvironment env = UserEnvironmentFactory.getUserEnvironment();
String methodName = ExtensionPointRuleContext.getMethodName();
 
if (methodName == "createConditions") {
    setConditionsOwner(context, log, env); }
else {
    if (methodName == "updateConditions") {
        validateConditionsOwner(context, log, env); }
    else {
        if (methodName == "deleteConditions") {
            validateConditionsOwner(context, log, env); }
        else {
            validate(context, log, env); 
        }
    }
}

Securing Entity Data through Permissions Example

Example 7-6 shows a custom ruleset that secures access to party entities by setting permissions. The ruleset name implies that it is intended to run when a party is created.

Example 7-6 Custom Ruleset Using Drools

package oracle.communications.inventory.rules; 
  .
  .
  .
global Log log;
 
rule "Create Party with Permissions"
salience 2
when
    partyList : Collection()
then 
    UserEnvironment environment = UserEnvironmentFactory.getUserEnvironment();
    if ((partyList != null) && !(partyList.isEmpty())) { 
        for (Object partyO : partyList ) {
            Party party = (Party)partyO;
            party.setOwner("inv");
            party.setPermissions("allow inv; deny all");
        }
    }
end
 

Example 7-7 shows the same custom ruleset content-wise, but using Groovy instead of Drools. Only the rules section is different, so that is all that is shown.

Example 7-7 Custom Ruleset Using Groovy

UserEnvironment environment = UserEnvironmentFactory.getUserEnvironment();
if ((partyList != null) && !(partyList.isEmpty())) { 
    for (Object partyO : partyList ) {
        Party party = (Party)partyO;
        party.setOwner("inv");
        party.setPermissions("allow inv; deny all");
    }
}

Retrieving Permissions Information Example

Example 7-8 shows a custom ruleset that retrieves user and role information so you can view the permissions that are set for a user through roles.

Example 7-8 Custom Ruleset Using Drools

package oracle.communications.inventory.rules; 
  .
  .
  .
rule "Get Permissions Info"
salience 2
when
    true
then 
    UserEnvironment environment = UserEnvironmentFactory.getUserEnvironment();
    String user = env.getUser();
    String userName = env.getUserName();
    Collection roles = env.getRoles();
end
 

Example 7-9 shows the same custom ruleset content-wise, but using Groovy instead of Drools. Only the rules section is different, so that is all that is shown.

Example 7-9 Custom Ruleset Using Groovy

UserEnvironment environment = UserEnvironmentFactory.getUserEnvironment();
String user = env.getUser();
String userName = env.getUserName();
Collection roles = env.getRoles();

Securing Entity Data through Partitions Example

Example 7-10 shows a custom ruleset that secures access to logical device entities by setting a partition. The ruleset name implies that it is intended to run when a logical device is created.

Example 7-10 Custom Ruleset Using Drools

package oracle.communications.inventory.rules;
  .
  .
  .
global Log log;
rule "Create LogicalDevice with Partitions"
salience 2
when
    ldList : Collection()
then 
    UserEnvironment environment = UserEnvironmentFactory.getUserEnvironment();
    if ((ldList != null) && !(ldList.isEmpty())) {
        for (Object ld : ldList ) {
            ((LogicalDevice)ld).setPartition("/US_PARTITION/NY_PARTITION");
        }
    }
end

Example 7-11 shows the same custom ruleset content-wise, but using Groovy instead of Drools. Only the rules section is different, so that is all that is shown.

Example 7-11 Custom Ruleset Using Groovy

UserEnvironment environment = UserEnvironmentFactory.getUserEnvironment();
if ((ldList != null) && !(ldList.isEmpty())) {
    for (Object ld : ldList ) {
            ((LogicalDevice)ld).setPartition("/US_PARTITION/NY_PARTITION");
    }
}

Securing Entity Data for a Range of Entities Example

When securing entity data for a range of entities, the ruleset custom code must iterate through the range and call the access control method for each entity in the range. To do this, you must configure your custom ruleset to run After the API call.

Example 7-12 shows a custom ruleset that secures access to a range of logical devices by iterating through the range of logical devices, and setting a partition for each logical device in the range. The ruleset name implies that it is intended to run when a range of logical devices are created.

Note:

Example 7-12 shows the use of the setPartition() method to secure entity data for a range, but the same concept applies when using the setOwner() or setPermissions() methods to secure entity data for a range.

Example 7-12 Custom Ruleset Using Drools

package oracle.communications.inventory.rules;
  .
  .
  .
global Log log;
 
rule "Create Range of LogicalDevices with Partitions"
salience 2
when
    ldList : Collection()
then 
    UserEnvironment environment = UserEnvironmentFactory.getUserEnvironment();
    if ((ldList != null) && !(ldList.isEmpty())) {
        for (Object obj : ldList ) {
            LogicalDevice ld = (LogicalDevice)obj;
            ld.setPartition("/US_PARTITION/NY_PARTITION");
        }
    }
end
 

Example 7-13 shows the same custom ruleset content-wise, but using Groovy instead of Drools. Only the rules section is different, so that is all that is shown.

Example 7-13 Custom Ruleset Using Groovy

UserEnvironment environment = UserEnvironmentFactory.getUserEnvironment();
if ((ldList != null) && !(ldList.isEmpty())) {
    for (Object obj : ldList ) {
        LogicalDevice ld = (LogicalDevice)obj;
        ld.setPartition("/US_PARTITION/NY_PARTITION");
    }
}

Enforcing Security Example

Example 7-14 shows a custom ruleset that enforces security access to a party. The ruleset name implies that it is intended to run when a party is updated.

Example 7-14 Custom Ruleset Using Drools

package oracle.communications.inventory.rules;
  .
  .
  .
global Log log;
 
rule "Secure Update Party"
salience 2
when
    partyList : Collection()
then 
    UserEnvironment environment = UserEnvironmentFactory.getUserEnvironment();
    if ((partyList != null) && !(partyList.isEmpty())) 
    {
        for (Object partyO : partyList ) {
            Party party = (Party)partyO;
            try {
                environment.checkPermissions
                    (WritePermission.getInstance().toString(), party);
            }
            catch(Throwable t){ 
                log.error("", t);
            }
        }
    }
end
 

Example 7-15 shows the same custom ruleset content-wise, but using Groovy instead of Drools. Only the rules section is different, so that is all that is shown.

Example 7-15 Custom Ruleset Using Groovy

UserEnvironment environment = UserEnvironmentFactory.getUserEnvironment();
if ((partyList != null) && !(partyList.isEmpty())) 
{
    for (Object partyO : partyList ) {
        Party party = (Party)partyO;
        try {
           environment.checkPermissions
              (WritePermission.getInstance().toString(), party);
        }
        catch(Throwable t){ 
            log.error("", t);
        }
    }
}

Creating Extension Points

Note:

Check the ora_uim_baseextpts cartridge to determine if any extension points you may need are already defined. Depending on what you are securing, you may or may not need to create new extension points.

Extension points are created in Design Studio. For information on extension points, see "Extending UIM Through Rulesets". For instructions on how to create an extension point, see the Design Studio Help.

When securing APIs, you must create one extension point per API to secure, where each extension point defines the specific API method to secure. In the same vein, when securing entity data, you must create one extension point per entity to secure, where each extension point defines the specific entity method to secure.The same ruleset can be called from multiple extension points. Example 7-16 shows the API method signatures to use when defining the extension point for each API secured by the custom ruleset shown in Example 7-4.

Example 7-16 Custom Extension Point Signatures

public void  oracle.communications.inventory.api.consumer.ConditionManager.createConditions
(java.util.Collection)
 
public void  oracle.communications.inventory.api.consumer.ConditionManager.updateConditons
(java.util.Collection)
 
public void  oracle.communications.inventory.api.consumer.ConditionManager.deleteConditions
(java.util.Collection)

Creating the Ruleset Extension Point

Ruleset extension points are created in Design Studio. For information on ruleset extension points, see "Extending UIM Through Rulesets". For instructions on how to create a ruleset extension point, see the Design Studio Help.

After you have created the ruleset and extension point in Design Studio, you must also create the corresponding ruleset extension point in Design Studio. A ruleset extension point associates a ruleset with an extension point, so the extension point knows which ruleset to run.