Wednesday, March 24, 2021

Sailpoint IIQ Service Now Integration - Sample Integration Config and Integration Rule

Below is the Customized Integration Config for Service Now Integration , Tested on orlando version

Service Now Integration Config

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE IntegrationConfig PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<IntegrationConfig executor="sailpoint.integration.servicenow.ServiceNowIntegrationExecutor" name="ServiceNow IntegrationConfig Vishal Demo Application" roleSyncStyle="none">
  <Attributes>
    <Map>
      <entry key="alias"/>
      <entry key="authType"/>
      <entry key="catalogItem">
        <value>
          <Map>
            <entry key="Vishal Demo Application" value="IdentityIQ Access Request"/>
          </Map>
        </value>
      </entry>
      <entry key="dateFormat" value="yyyy-MM-dd&apos;T&apos;HH:mm:ss"/>
      <entry key="visDefaultAssignmentQueue" value="IAM Requests Queue"/>
      <entry key="visServiceAcount" value="iiq.integration"/>
      <entry key="getRequestStatus">
        <value>
          <Map>
            <entry key="CONNECTION_TIMEOUT" value="30000"/>
            <entry key="SO_TIMEOUT" value="30000"/>
            <entry key="endpoint" value="https://sailpointdev.service-now.com/sc_req_item.do?SOAP"/>
            <entry key="prefix" value="sc"/>
            <entry key="responseElement" value="state"/>
			<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sc="http://www.service-now.com/sc_req_item">        
			<soapenv:Header></soapenv:Header>                        
			<soapenv:Body>                          
			<sc:getRecords>                            
			<number>$requestID</number>                          
			</sc:getRecords>                        
			</soapenv:Body>                      
			</soapenv:Envelope>                    
			"/>
			</Map>
        </value>
      </entry>
      <entry key="keyPass"/>
      <entry key="keystorePass"/>
      <entry key="keystorePath"/>
      <entry key="keystoreType"/>
      <entry key="noProvisioningRequests" value="false"/>
      <entry key="operations" value="Create,Modify,Delete,Enable,Unlock,SetPassword"/>
      <entry key="password" value="<password>"/>
	  <entry key="provision">
        <value>
          <Map>
            <entry key="CONNECTION_TIMEOUT" value="30000"/>
            <entry key="SOAPAction" value="https://sailpointdev.service-now.com/ScRequestGenerator/insert"/>
            <entry key="SO_TIMEOUT" value="30000"/>
            <entry key="endpoint" value="https://sailpointdev.service-now.com/ScRequestGenerator.do?SOAP"/>
            <entry key="groupItemBy" value="application"/>
            <entry key="multipleItem" value="true"/>
            <entry key="multipleTicket" value="true"/>
            <entry key="prefix" value="sc"/>
            <entry key="responseElement" value="scResult"/>
            <entry key="soapMessage" value="<?xml version="1.0" encoding="UTF-8"?>
			<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:scr="http://www.service-now.com/ScRequestGenerator">
			<soapenv:Header></soapenv:Header>
			<soapenv:Body>
			<scr:insert>
			<screquest>
			<opened_by>$!provisioningPlan.integrationData.openedBy</opened_by>
			<requested_for>$!provisioningPlan.integrationData.requestedFor</requested_for>
			<assignment_group>$!provisioningPlan.integrationData.assignmentGroup</assignment_group>
			<short_description>IIQ $!provisioningPlan.integrationData.identityRequestId</short_description>
			<req_description>This request item has been generated for an access request initiated in IIQ the requestor are in the Requested Item Description field.</req_description>
			<screqitems>
				#if ($config.multipleItem == &apos;true&apos;)
					#if (($config.groupItemBy) &amp;&amp; ($config.groupItemBy.toLowerCase() == &apos;application&apos;))               
						#set ($requestGroupByApp = {})
						#foreach ($request in $provisioningPlan.accountRequests)
							#if($requestGroupByApp.containsKey($request.application))
								$requestList = $!requestGroupByApp.get($request.application)
								$!requestList.add($request)
								$!requestGroupByApp.put($request.application, $requestList)
							#else
								#set ($requestList = [])
								$!requestList.add($request)
								$!requestGroupByApp.put($request.application, $requestList)
							#end
						#end
						#foreach ($mapEntry in $requestGroupByApp.entrySet())
						<item>
							<tracking_id>$!mapEntry.value.get(0).trackingId</tracking_id>
							<cat_item>$!catalogItem.get($!mapEntry.value.get(0).application)</cat_item>
							<description>
								#foreach ($request in $mapEntry.value)
									#if ($request.attributeRequests)
										#if($request.operation == &apos;Create&apos;)
											Lan ID: $provisioningPlan.integrationData.lanID
											Employee Number: $provisioningPlan.integrationData.empNumber
											Email Address: $provisioningPlan.integrationData.email
										#else
											Lan ID: $provisioningPlan.integrationData.lanID
											Employee Number: $provisioningPlan.integrationData.empNumber
											Email Address: $provisioningPlan.integrationData.email
										#end
									Application Name: $request.application
									#foreach ($att in $request.attributeRequests)
										Entitlement/Group/Role/Permission: $att.value
										#if($request.operation != &apos;Create&apos;)
											Account Id : $request.nativeIdentity
										#end
										Action needed: $att.operation access
									#end
									#elseif ($request.permissionRequests)
										For $request.nativeIdentity in application $request.application
										#foreach ($att in $request.permissionRequests)
											$att.operation Right $att.rights on $att.target
										#end
									#else
										$request.operation $request.application nativeIdentity $request.nativeIdentity
									#end
								#end
							</description>
						</item>
						#end
					#end
				#end
			</screqitems>
			</screquest>
			</scr:insert>
			</soapenv:Body>
			</soapenv:Envelope>
			"/>
          </Map>
        </value>
      </entry>
      <entry key="roleSyncHistory"/>
      <entry key="statusMap">
        <value>
          <Map>
            <entry key="-5" value="inProcess"/>
            <entry key="1" value="inProcess"/>
            <entry key="16" value="inProcess"/>
            <entry key="2" value="inProcess"/>
            <entry key="3" value="committed"/>
            <entry key="7" value="failure"/>
            <entry key="8" value="failure"/>
            <entry key="9" value="failure"/>
          </Map>
        </value>
      </entry>
      <entry key="username" value="iiq.integration"/>
    </Map>
  </Attributes>
  <PlanInitializer>
    <Reference class="sailpoint.object.Rule" name="VIS ServiceNow Service Integration Rule"/>
  </PlanInitializer>
  <ManagedResources>
    <ManagedResource>
      <ApplicationRef>
        <Reference class="sailpoint.object.Application" name="Vishal Demo Application"/>
      </ApplicationRef>
    </ManagedResource>
  </ManagedResources>
</IntegrationConfig>


Sample Integration Rule
 
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule language="beanshell" name="VIS ServiceNow Service Integration Rule" type="Integration">
  <Description>
 The rule is used to compute the requested for, opened by and assignmnent queue that needs to be 
 set on the ticket created by IIQ for delimited apps.The provisioning plan and its integrationData 
 object are used by Velocity to populate values in the XML templates for the web service calls.
  </Description>
  <Signature returnType="Object">
    <Inputs>
      <Argument name="identity">
        <Description>
    A sailpoint.object.Identity representing the person being remediated.
  </Description>
      </Argument>
      <Argument name="plan">
        <Description>
    A sailpoint.object.ProvisioningPlan representing remediation.
  </Description>
      </Argument>
      <Argument name="integration">
        <Description>
    A sailpoint.object.IntegrationConfig representing configuration object.
  </Description>
      </Argument>
      <Argument name="context">
        <Description>
    A sailpoint.api.SailPointContext object that can be used to query the database to find the Rule.
  </Description>
      </Argument>
      <Argument name="log">
        <Description>
    A Log object to help report and/or debug the Rule.
  </Description>
      </Argument>
    </Inputs>
  </Signature>
  <Source>
   <![CDATA[ 
   
	 import sailpoint.object.Identity;
	 import sailpoint.object.Filter;
	 import sailpoint.object.Configuration;
	 
	 String applicationName;
	 String openedBy;
	 String requestedFor;
	 String defaultUserOpenedBy = (String) integration.getAttribute("visServiceAcount");
	 List accountRequests = plan.getAccountRequests();
	 Map argument = (Map)plan.getArguments();
	 String planSource = plan.getSource();
	 
		requestedFor = identity.getName();
		openedBy = defaultUserOpenedBy;                         
		argument.put("requestedFor", requestedFor);      
		argument.put("openedBy", openedBy);
		argument.put("lanID", identity.getName());
		argument.put("email", identity.getEmail());
		argument.put("empNumber", identity.getAttribute("employeeNumber"));

	 if(accountRequests != null && accountRequests.size() > 0) {
		AccountRequest accountRequest = accountRequests.get(0);
		applicationName = accountRequest.getApplicationName();  
	 }
		   
	  String visDefaultAssignmentGroup = (String)integration.getAttribute("visDefaultAssignmentQueue");  
	  argument.put("assignmentGroup", visDefaultAssignmentGroup);
  ]]>  
  </Source>
</Rule>

Thursday, March 18, 2021

Sailpoint IIQ Azure Active Directory Connector Pre-check

Sailpoint IIQ Azure Active Directory Connector

Recently I was trying to integrate Azure AD with IIQ and found that 7.3P3 doesn't support proxy rather it's supported from patch4 version , so if you are in patch 3 make sure that the server FW is open and should be able to access the below URL which are internally used by Sailpoint Azure Connector.

https://login.windows.net

https://graph.windows.net

https://graph.microsoft.com

https://login.microsoftonline.com

Also make sure that you are able to retrieve data from Azure using below CURL

To generate access token :

curl --location --request POST 'https://login.microsoftonline.com/sailpointdev.onmicrosoft.com/oauth2/v2.0/token' \

--header 'Cookie: fpc=AnqEXhzvx4NKjU5r-HxvS9WRa5E6AQAAALRd4tcOAAAA; stsservicecookie=ests; x-ms-gateway-slice=prod' \

--form 'grant_type="client_credentials"' \

--form 'client_id="<Client_ID>"' \

--form 'client_secret="<Secret>"' \

--form 'scope="https://graph.microsoft.com/.default"'


Use above generated access token and fire below curl :

curl --location --request GET 'https://graph.microsoft.com/v1.0/groups?$top=500&$filter=securityEnabled+eq+true' \

--header 'Authorization: Bearer XXXXXXeyJ0eXAiOiJKV1QiLCJub25jZSIXXXXX' \

--header 'Content-Type: Application/json;odata=minimalmetadata'


Happy Learning !!

Sunday, March 7, 2021

Sailpoint IdentityIQ Creating Business Role Using API

 Creating Business Role Using API

public static void buildRole(HashMap lineHash) {
		 
		String roleName = lineHash.get("RoleName").toString();
		String roleType = lineHash.get("RoleType").toString();
		String locCity = lineHash.get("locCity").toString();
		String coNumber = lineHash.get("coNumber").toString();
		
		System.out.println("locCity " + locCity );
		System.out.println("coNumber " + coNumber );
		
		//Added
		String displayName = lineHash.get("displayName").toString();
		String subRoleName = lineHash.get("requiredRole").toString();

		
		String roleOwner = "";
		String roleExists = "yes";
		Bundle role;
		System.out.println("Data " + lineHash );
		try {
		    role = context.getObject(Bundle.class, roleName);
			if (null == role) {
				role = new sailpoint.object.Bundle();
				roleExists = "no";
			}
			if (roleType.equalsIgnoreCase("business") &amp;&amp; roleExists.equalsIgnoreCase("no")) {
				System.out.println("Creating Role :" + roleName );
				AccountSelectorRules rules = new AccountSelectorRules();
				role.setAccountSelectorRules(rules);
				
				HashMap mapDesc = new HashMap();
				mapDesc.put("en_US", "This is the BR " + roleName);
				
				Identity ownerId = context.getObject(Identity.class, roleOwner);
				if (null == ownerId) {
					ownerId = context.getObject(Identity.class, "spadmin");
				}
				role.setName(roleName);
				role.setDescriptions(mapDesc);
				role.setType("business");
				role.setAllowDuplicateAccounts(false);
				role.setAllowMultipleAssignments(false);
				role.setMergeTemplates(false);
				role.setOwner(ownerId);
				
				//Added
				role.setDisplayName(displayName);
				Bundle requiredRole = context.getObjectByName(Bundle.class, subRoleName);
				role.addRequirement(requiredRole);			

				IdentitySelector is = new IdentitySelector();
				MatchExpression me = new MatchExpression();
				me.setAnd(false);

				MatchTerm term = new MatchTerm();
				MatchTerm term1 = new MatchTerm();
				MatchTerm term2 = new MatchTerm();
				
				if((!(locCity.equalsIgnoreCase(""))) &amp;&amp; (!(coNumber.equalsIgnoreCase("")))){
				
				term1.setName("locCity");
				term1.setValue(locCity);
				term.addChild(term1);
				
				term2.setName("coNumber");
				term2.setValue(coNumber);
				term.addChild(term2);
				
				term.setAnd(true);
				term.setContainer(true);
				me.addTerm(term);
				
				}else if(locCity.equalsIgnoreCase("")){
				
				term2.setName("coNumber");
				term2.setValue(coNumber);
				me.addTerm(term2);
				}else if(coNumber.equalsIgnoreCase("")){
				term2.setName("locCity");
				term2.setValue(locCity);
				me.addTerm(term2);
				}
				
				is.setMatchExpression(me);
				role.setSelector(is);
				context.saveObject(role);
				context.commitTransaction();
				context.decache();
			
			}else if(roleType.equalsIgnoreCase("business") &amp;&amp; roleExists.equalsIgnoreCase("yes")){			
			    
				IdentitySelector is = role.getSelector();
				MatchExpression me = is.getMatchExpression();
				MatchTerm term = new MatchTerm();
				MatchTerm term1 = new MatchTerm();
				MatchTerm term2 = new MatchTerm();

				if((!(locCity.equalsIgnoreCase(""))) &amp;&amp; (!(coNumber.equalsIgnoreCase("")))){
				
				term1.setName("locCity");
				term1.setValue(locCity);
				term.addChild(term1);
				
				term2.setName("coNumber");
				term2.setValue(coNumber);
				term.addChild(term2);
				
				term.setAnd(true);
				term.setContainer(true);
				me.addTerm(term);
				}else if(locCity.equalsIgnoreCase("")){		
				term2.setName("coNumber");
				term2.setValue(coNumber);
				me.addTerm(term2);
				}else if(coNumber.equalsIgnoreCase("")){
				term2.setName("locCity");
				term2.setValue(locCity);
				me.addTerm(term2);
				}
				
				is.setMatchExpression(me);
				role.setSelector(is);
				
				context.saveObject(role);
				context.commitTransaction();
				context.decache();
			} else{
			
			System.out.println("Doing Nothing !" );
			
			}
		} catch (GeneralException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		}

Friday, March 5, 2021

Reading CSV File Data

Sample data

RoleName,displayName,RoleType,locCity,coNumber
AAA1,AAA1 DN,business,AA,1
AAA1,AAA1 DN,business,BB,2
AAA1,AAA1 DN,business,CC,3
AAA2,AAA2 DN,business,AA,1
AAA2,AAA2 DN,business,BB,2
AAA2,AAA2 DN,business,CC,3
AAA2,AAA2 DN,business,DD,4
AAA2,AAA2 DN,business,EE,5
AAA2,AAA2 DN,,business,FF,6
AAA2,AAA2 DN,business,GG,7
AAA2,AAA2 DN,business,HH,8
AAA2,AAA2 DN,business,II,9
AAA2,AAA2 DN,business,JJ,10
AAA2,AAA2 DN,business,KK,11
AAA2,AAA2 DN,business,LL,12
AAA2,AAA2 DN,business,MM,13
AAA1,AAA1 DN,business,DD,4
AAA1,AAA1 DN,business,EE,5
AAA2,AAA2 DN,business,NN,14

Sample Code
 
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule  language="beanshell"  name="VIS CreateBundle">
  <Source>
  <![CDATA[
import java.io.File;
import java.util.List;
import java.util.HashMap;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import sailpoint.api.SailPointContext;
import sailpoint.api.SailPointFactory;
import sailpoint.object.Bundle;
import sailpoint.object.Identity;
import sailpoint.object.IdentitySelector;
import sailpoint.spring.SpringStarter;
import sailpoint.tools.GeneralException;
import sailpoint.tools.RFC4180LineParser;
import sailpoint.object.AccountSelectorRules;
import sailpoint.object.IdentitySelector.MatchTerm;
import sailpoint.object.IdentitySelector.MatchExpression;

		
		String dlm = ",";
		String csvFileName = "/tmp/bCopy/SampleBundle.csv";

		System.out.println("Role Creation Started ...");

		File bundleFile = new File(csvFileName);
		if ((!bundleFile.exists()) || (bundleFile.isDirectory())) {
			System.out.println("Unable to find the bundle csv file: " + csvFileName);
			return;
		}

		System.out.println("Reading Bundle Data from: '" + csvFileName);

		
		try {
			int lineCounter = 0;
			ArrayList headerStrings = new ArrayList();
			String thisLine = "";
		
			BufferedReader fileIn = new BufferedReader(new FileReader(csvFileName));

			RFC4180LineParser parser = new RFC4180LineParser(dlm);

			while (null != (thisLine = fileIn.readLine())) {
				ArrayList tokens = parser.parseLine(thisLine);
				if (lineCounter == 0) {
					for (int i = 0; i<tokens.size(); i++) {
						headerStrings.add((String) (tokens.get(i)));
					}
				}else {
					HashMap lineHash = new HashMap();
					for (int i = 0; i<tokens.size(); i++) {
						String headerString = headerStrings.get(i);
						String valueString = (String) tokens.get(i);
						if (null != valueString) {
							valueString = valueString.trim();
						}else{
							//Added Else Condition
							valueString = "";
						}
						lineHash.put(headerString, valueString);
					}
					try{ 
						//buildRole(lineHash);
					}catch (Exception e){
						System.out.println("Error While Creating Bundle " + lineHash);				
					}	
				}
				lineCounter++;				
			}
			fileIn.close();
			System.out.println("Role Creation Completed ...");					
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (GeneralException e) {
			e.printStackTrace();
		}
]]>
  </Source>
</Rule>