DevOps Automation

DevOps Automation

Join this online group to communicate across IBM product users and experts by sharing advice and best practices with peers and staying up to date regarding product enhancements.


#DevOps
 View Only

UrbanCode Deploy 10 Minute Tip: Documenting a UCD Permissions Model

By Laurel Dickson-Bull posted Wed December 16, 2020 07:44 AM

  

When you set out to define a permissions model for UCD, now DevOps Deploy, its often a requirement to document the model for discussions / approvals ... 

While UCD provides tools to build and maintain the permissions model it doesn't give you a 'single view' that can be used in workshops and other forums to discuss the operation of the model and the specifics of what permissions are granted to each role.

Often as part of the process of adopting UCD it is seen as desirable to have the model presented in a documented form for sign off and also for discussions around future change.  It can also be needed for training purposes so that each role in the organisation clearly knows what their responsibilities are.

It would also help if there was an easy way to spot anomalies where a permission isn't granted to any role.  This might be an oversight or perhaps intentional, but it would help if these were easy to spot and justify.

UCD has over 160 permissions at the time of this writing, spread across 18 different categories or object classes.  This also changes over time as UCD evolves and more permissions are added / old ones changed in their scope.

So, wouldn't it be handy if you could just develop the permissions model in UCD itself and then extract what you had setup for documentation and discussion purposes.  That way you would only have one place to maintain the model and, in keeping with general best practices, only have a single source of truth – UCD itself!!

What I'm presenting here is a small groovy script that will do just that for you.  It will enumerate all the permissions in UCD across the 18 different object classes and include variations in permissions for each resource role or 'Type', against all of the UCD roles that you have defined.  The output is a tab separated list which can be easily imported into a spreadsheet for analysis.

The script will also warn you if there are any permissions in your model that aren't assigned to any role / resource role combination meaning that the UCD operation it represents cannot be performed by anyone.

The tool is executed via a shell script and you will need a copy of the uDeployRestClient.jar file in the same directory.  You can find this jar in any of the UCD plugins that provide services to automate UCD itself. 

You will also need to setup the environment variables JAVA_HOME pointing at your JRE and also GROOVY_HOME pointing at a groovy installation.  (UCD agents have one of these in their opt sub-directory.)

The script takes three parameters, -user, -password and -weburl.  You should redirect standard output to a file to receive the extracted permissions information which you will later import into a spreadsheet.

./extractPermissions.sh -user admin -password admin -weburl https://localhost:8443 > permissions.out


The application title and any other information is presented on standard error.  This includes warning about permissions assigned to no role.   The following is an example output.  In my case, I've upgraded a UCD server with a version that has new permissions that have yet to be assigned to any role.

UCD Role Permissions Extractor V3.0 December 2020

 The permission 'Create External Approvals' of Class 'External Approval' was not assigned to any role

 The permission 'Delete' of Class 'External Approval' was not assigned to any role

 The permission 'Edit Basic Settings' of Class 'External Approval' was not assigned to any role

 The permission 'Manage Properties' of Class 'External Approval' was not assigned to any role

 The permission 'Manage Teams' of Class 'External Approval' was not assigned to any role

 The permission 'Manage Maintenance Mode' of Class 'Server Configuration' was not assigned to any role

 The permission 'Manage Tags' of Class 'Server Configuration' was not assigned to any role

 The permission 'Configuration Tab' of Class 'Web UI' was not assigned to any role

Extract Complete

This is the kind of thing you would see once you import the output into a spreadsheet.  Being able to generate this automatically represents a significant saving in time and improvement in accuracy not to speak of saving a lot of hair!!!


So this is the shell script to invoke the script in a nice friendly manner

    #!/bin/sh

if [ -n "$GROOVY_HOME" ]; then

    # change the dir to the root of the client directory

    SHELL_NAME="$0"

    SHELL_PATH=`dirname "${SHELL_NAME}"`

   

    if [ "." = "$SHELL_PATH" ]

    then

       SHELL_PATH=`pwd`

    fi

 

    groovycmd="$GROOVY_HOME/bin/groovy"

    jarfile="$SHELL_PATH/uDeployRestClient.jar"

   

    if [ -r "$jarfile" ]; then

        "$groovycmd" -cp "$jarfile" extractPermissions.groovy "$@"

    else

        echo "Didn't find $jarfile in directory ${SHELL_PATH}"

        exit 1

    fi

else

    echo You must have GROOVY_HOME set in your environment to use the extractPermissions tool.

    exit 1

fi

 


Finally, the most important bit, the script itself



    import java.net.URI;

import java.util.LinkedHashMap;

import com.urbancode.ud.client.UDRestClient;

import org.codehaus.jettison.json.JSONArray;

import org.codehaus.jettison.json.JSONObject;

import org.codehaus.jettison.json.JSONException;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.client.methods.HttpRequestBase;

import org.apache.http.client.methods.CloseableHttpResponse;

import org.apache.http.impl.client.DefaultHttpClient;

 

userid="";

password="";

weburl="";

 

System.err << "UCD Role Permissions Extractor V3.0 December 2020\n"

 

public class extensionsClient extends UDRestClient

{

    public extensionsClient(final URI url, final String clientUser, final String clientPassword) {

        super(url, clientUser, clientPassword);

    }

    public extensionsClient(final URI url, final String clientUser, final String clientPassword, final boolean trustAllCerts)

    {

        super(url, clientUser, clientPassword, trustAllCerts);

    }

    public extensionsClient(final URI url, final DefaultHttpClient client) {

        super(url, client);

    }

 

    // fetch the action mappings for a given role.  This maps permissions to roles including user defined types

    JSONArray getSecurityRoleActionMappings(final String roleID)

    {

        String uri = this.url.toString() + "/security/role/${roleID}/actionMappings";

        final HttpGet method = new HttpGet(uri);

        final CloseableHttpResponse response = invokeMethod((HttpRequestBase)method);

        final String body =  this.getBody(response) ;

        return new JSONArray(body);

    }

    JSONArray getSecurityRoles()

    {

        String uri = this.url.toString() + "/security/role";

        final HttpGet method = new HttpGet(uri);

        final CloseableHttpResponse response = invokeMethod((HttpRequestBase)method);

        final String body =  this.getBody(response) ;

        return new JSONArray(body);

    }

    JSONArray getSecurityClassPermissions(final String resourceClass)

    {

        String uri = this.url.toString() + "/security/resourceType/${resourceClass}/actions";

        final HttpGet method = new HttpGet(uri);

        final CloseableHttpResponse response = invokeMethod((HttpRequestBase)method);

        final String body =  this.getBody(response) ;

        return new JSONArray(body);

    }

    JSONArray getResourceRolesforClass(final String resourceClass)

    {

        String uri = this.url.toString() + "/security/resourceType/${resourceClass}/resourceRoles";

        final HttpGet method = new HttpGet(uri);

        final CloseableHttpResponse response = invokeMethod((HttpRequestBase)method);

        final String body =  this.getBody(response) ;

        return new JSONArray(body);

    }

    JSONArray getSecurityResourceTypes()

    {

        String uri = this.url.toString() + "/security/resourceType";

        final HttpGet method = new HttpGet(uri);

        final CloseableHttpResponse response = invokeMethod((HttpRequestBase)method);

        final String body =  this.getBody(response) ;

        return new JSONArray(body);

    }

}

 

ParseCommandLine(this.args);

myClient = new extensionsClient( new URI(weburl), userid , password, true);

 

// Permissions Extraction

// OK, lets start the exercise by getting the list of role permissions classifications eg, agent, agent pool, application and so on.

// Map contains the permission classification and its ID

LinkedHashMap <String, String> Role_Class_Info = getNameAndID(myClient.getSecurityResourceTypes());

Role_Class_Info.sort{it}

 

// Now build a map of the roles and ID's

LinkedHashMap <String, String> Role_Info = getNameAndID(myClient.getSecurityRoles());

 

// print static part of column header line

print "Permission Category\tPermission Name\tPermission Description\tResource Role"

NumRoles=Role_Class_Info.size();

 

// Output the names of all the roles

Role_Info.each {print "\t" + it.key };

 

// Terminate column headings line

println  "";

 

// Now get the permissions map for all of the roles against each role there is a JSON array of permissions

LinkedHashMap<String, JSONArray>  RoleMaps = getRoleMaps(Role_Info);

 

// Now process each of the role permission classifications

Role_Class_Info.each

{

        // Get values for loop

        EncodedClassName = it.key.replace(' ', '%20');

        CLASS_NAME = it.key;

        CLASS_ID = it.value;

 

        // Get the permissions details for each permission in the class category

        JSONArray ClassPermissions = myClient.getSecurityClassPermissions(EncodedClassName);

 

        // get the names of the resourcetypes available for the category

        // So we will end up with lists of all of the types for each permission category and also the IDs of those

        JSONArray resourceRoles = myClient.getResourceRolesforClass(EncodedClassName);

 

        // see if there are any additional to the standard type

        RESOURCE_ROLE_NAMES= ["standard"];

        RESOURCE_ROLE_IDS = ["NA"];

        if ( resourceRoles.length() > 0 )

        {

               for (int i= 0 ; i< resourceRoles.length() ; ++i)

               {

                       JSONObject propObject = (JSONObject)resourceRoles.get(i);

                       RESOURCE_ROLE_NAMES = RESOURCE_ROLE_NAMES << propObject.get("name");

                       RESOURCE_ROLE_IDS = RESOURCE_ROLE_IDS << propObject.get("id");

               }

        }

 

        lastPermID=""

        lastPermName=""

        permCount=0

        // Now iterate over each permission/type combination and find out what roles have it

        for (int i = 0; i < ClassPermissions.length(); ++i)

        {

               final JSONObject propObject = (JSONObject)ClassPermissions.get(i);

               PERM_NAME = propObject.get("name");

               PERM_ID = propObject.get("id");

               PERM_DESC = propObject.get("description");

 

               for (int j=0; j<RESOURCE_ROLE_NAMES.size(); ++j)

               {

                       // Print the static data for each permission

                       print "${CLASS_NAME}\t${PERM_NAME}\t${PERM_DESC}\t"+RESOURCE_ROLE_NAMES[j]

 

                       // Check to see if a permissions wasn't assigned to any role

                       if ( lastPermID != PERM_ID)

                       {

                               if (lastPermID != "")

                                      if ( permCount == 0 ) System.err << " The permission \'${lastPermName}\' of Class \'${CLASS_NAME}\' was not assigned to any role\n"

                               lastPermID= PERM_ID

                               lastPermName = PERM_NAME

                               permCount=0   

                       }

                              

                       // Add the assigned permission information to the output line

                       Role_Info.each

                       {

                               ROLE = it.key;

                               JSONArray MapForRole = RoleMaps[ROLE];

                               assigned =  searchJSONArrayforKey(MapForRole, PERM_ID, RESOURCE_ROLE_NAMES[j]);

                               if (assigned.contains("X")) permCount++

                               print assigned

                       }

                       println "";

               }

        }

}

System.err << "Extract Complete\n"

 

// This method searches the action map for a role looking to see if the role has the permission we're interested in

// It returns either an 'X' for present or blank for missing

String searchJSONArrayforKey (JSONArray actionsMap, String id , String resourceRole)

{

        // Search the JSON array given for an oject containing the specific value of a given key

        for ( int i = 0 ; i < actionsMap.length(); ++i)

        {

               JSONObject obj = actionsMap.get(i);

               try

               {

                       if ( resourceRole == "standard" )

                               if ( obj.find {it.action.id == id} != null ) return "\tX";

                       else

                               if (obj.find {it.resourceRole.name == resourceRole}  != null ) return "\tX";

               }

               catch (Exception JSONException)

               {

               }

        }

        return "\t";

}

 

// This method parses the command options to extract connection details

void ParseCommandLine(args)

{

        def idx=0

        def arg=""

        while ( idx < args.size())

        {

               arg = args[idx].toLowerCase();

               switch (arg)

               {

                       case "-user":

                       case "--user":

                               userid=args[++idx];

                               break;

                       case "-password":

                       case "--password":

                               password=args[++idx];

                               break;

                       case "-weburl":

                       case "--weburl":

                               weburl=args[++idx];

                               break;

                       default:

                               System.err << "Unexpected argument ${arg}\n";

                               System.err << "Expected command line -user <ucdd admin username> -passsword <password for admin user> -weburl https://<host>:<port> \n"

                               System.exit(1);

               }

               idx++;

        }

}

LinkedHashMap<String, String> getNameAndID(JSONArray roleInfo) throws IOException, JSONException

{

        final Map<String, String> result = new LinkedHashMap<String, String>();

        for (int i = 0; i < roleInfo.length(); ++i)

        {

            final JSONObject propObject = (JSONObject)roleInfo.get(i);

            result.put((String)propObject.get("name"), (String)propObject.get("id"));

        }

        return result;

}

LinkedHashMap<String, JSONArray> getRoleMaps(LinkedHashMap <String, String> RoleInfo )

{

        final Map<String, JSONArray> result = new LinkedHashMap<String, JSONArray>();

        RoleInfo.each

        {

               result.put((String)it.key, (JSONArray) myClient.getSecurityRoleActionMappings(it.value));

        }

        return result;

}

 

I hope you find this script useful and that it saves you some time (and hair!).

Alan Murphy

Cloud, DevOps, IT Specialist, Cloud Integration Expert Labs

IBM Cloud and Cognitive Software

Alan Murphy/UK/IBM

alan.murphy@uk.ibm.com

 

Alan Murphy is a services consultant who has worked with clients to help them adopt new tools and processes for the last 20 years. UrbanCode Deploy and DevOps has been his focus for the last 5+ years. He also develops tools to assist clients in tool adoption and blogs on an occasional basis.


#UrbanCodeDeploy
#10minutetip

0 comments
14 views

Permalink