Office 365 Account correlation rule

Which IIQ version are you inquiring about?

Version 8.3

Share all details related to your problem, including any error messages you may have received.

We have tried to build Office365 account correlation rule in sailpoint iiq.
Code and account aggregation executing successfully.
But still we are able to see there are multiple accounts are correlated with Office365 Account Application.

Do you want to share the correlation rule and also identity xml from debug page.

1 Like

@yogesh_thok
Does your correlation rule making sure unique identity is being returned, can you please share.

Yes

<?xml version='1.0' encoding='UTF-8'?> A generic account correlation rule to be used for all applications. It uses the application's correlation config to determine which attribute contains the logon id. A first attempt will be made using the primaryLanID Identity Attribute, then if that fails it will look for a match amongst any authoratative links associated with an identity using the ATTR_NativeID Link Attribute. Existing links to uncorrelated identitities will be re-evaluated and re-linked if a better match is found Matches to multiple identities will be ignored import java.util.LinkedHashSet; import java.util.HashSet; import java.util.regex.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import sailpoint.object.*; import sailpoint.tools.Util;
private Identity getUniqueIdentityFromLinks(List links)
{
	HashSet identities = new HashSet();
	Identity identity = null;
	
	for (Link link : links)
	{
		pLog.debug("Matched Link: " + link.getNativeIdentity() + " from: " + link.getApplicationName());
		identities.add(link.getIdentity());
	}
	pLog.debug("Found: " + identities.size() + " from matching links");
	if (identities.size() == 1)
	{
		identity = identities.iterator().next();
	}
	return(identity);
}

public QueryOptions setIgnoreWorkgroupIndexes (QueryOptions qo) {  
   ArrayList trueAndFalse = new ArrayList();  
   trueAndFalse.add(new java.lang.Boolean(false));  
   qo.addFilter(Filter.in("workgroup", trueAndFalse));  
   qo.setCloneResults(true);
   return qo;  
}  

Log pLog = LogFactory.getLog("rule.Office365_Account_Correlation1");

String appName = application.getName();

pLog.debug("Starting Office365 Account Correlation Rule for application: " + appName + ", account:" + account.getIdentity());

CorrelationConfig correlationConfig = application.getAccountCorrelationConfig();
HashMap result = new HashMap();
Identity identity = null;
String identityName = null;

//Service Account Check
pLog.debug(appName+":Checking for Service Account");
boolean isServiceAccount = false;
if (link == null) {
	Rule checkServiceAccount = context.getObjectByName(Rule.class, "Is Service Account");
	Map map = new HashMap();
	map.put("attributes", account.getAttributes());
	map.put("application", application);
	isServiceAccount = context.runRule(checkServiceAccount, map);
} else {
	isServiceAccount = link.getAttribute("service") != null ? link.getAttribute("service") : false;
}
pLog.debug(appName+":Service Account Status is " + isServiceAccount);

//Generic Account Check
pLog.debug(appName+":Checking for Generic Account");
boolean isGenericAccount = false;
if (link == null) {
	Rule checkGenericAccount = context.getObjectByName(Rule.class, "Is Generic Account");
	Map map = new HashMap();
	map.put("attributes", account.getAttributes());
	map.put("application", application);
	isGenericAccount = context.runRule(checkGenericAccount, map);
} else {
	isGenericAccount = link.getAttribute("generic") != null ? link.getAttribute("generic") : false;
}
pLog.debug(appName+":Generic Account Status is " + isGenericAccount);

if ((isServiceAccount || isGenericAccount) &amp;&amp; !(isServiceAccount &amp;&amp; isGenericAccount)) {
	String accountType = isServiceAccount ? "Service" : "Generic"; 
	Identity managerIdentity = null;
	
	pLog.debug(appName+":Processing " + account.getDisplayName() + " as " + accountType);

	String manager = account.getAttribute("manager");	
	
	if(manager != null){
		QueryOptions qo = new QueryOptions();
		qo.addFilter(Filter.eq("distinguishedName", manager));
		List managerLink = context.getObjects(Link.class, qo);
		
		if(!managerLink.isEmpty()){
			managerIdentity = managerLink.get(0).getIdentity();

			if (null != managerIdentity) {
				pLog.debug(appName+":Generic-account--Processing managerStatus-" +managerIdentity.getManagerStatus());
				
				List activeReporties = context.getObjects(Identity.class, new QueryOptions().addFilter(Filter.and(Filter.eq("manager.name", managerIdentity.getName()),Filter.eq("inactive", false), Filter.or(Filter.eq("links.application.name", "HR"),Filter.eq("links.application.name", "TLC HCM"),Filter.eq("links.application.name", "Oracle HCM")))));
				pLog.debug(appName+":Generic-account--Processing activeReporties-" +activeReporties.size());
				while(activeReporties.isEmpty()){
					pLog.debug(appName+":Generic-account--Processing managerIdentity-1-" +managerIdentity);
					managerIdentity = managerIdentity.getManager();
					pLog.debug(appName+":Generic-account--Processing managerIdentity-2-" +managerIdentity);
					activeReporties = context.getObjects(Identity.class, new QueryOptions().addFilter(Filter.and(Filter.eq("manager.name", managerIdentity.getName()),Filter.eq("inactive", false), Filter.or(Filter.eq("links.application.name", "HR"),Filter.eq("links.application.name", "TLC HCM"),Filter.eq("links.application.name", "Oracle HCM")))));
				}
				identityName = accountType+"-Identity-"+managerIdentity.getName();
				
					pLog.debug(appName+":Generic-account--Processing identity name:" +identityName);
			}
		}
	}
	
	if(link != null &amp;&amp; link.getIdentity() != null &amp;&amp; link.getIdentity().isCorrelated()){
		  
		String linkManager = link.getAttribute("manager");
		pLog.debug(appName+":Generic-account--Processing linkManager-" +linkManager);
		//if(linkManager != null &amp;&amp; manager.equalsIgnoreCase(linkManager)){
			
			identity = link.getIdentity();
			pLog.debug(appName+":Generic-account--Processing link identity -"+identity);
			if(identityName == null &amp;&amp; !appName.equalsIgnoreCase("CORPAD") &amp;&amp; !appName.equalsIgnoreCase("tattsgroup.com")){
				if (identity != null) {
					identityName =identity.getName();
				}					
			}
			else if(identity != null &amp;&amp; !identity.getName().equalsIgnoreCase(identityName)){
				identity = context.getObjectByName(Identity.class, identityName);
				pLog.debug(appName+":Generic-account--Processing identity updated based on IdentityName -"+identity);
			}
			
			pLog.debug(appName+":Generic-account--Processing link identity-" +identity);
		//}
	}
	if(identity == null){
		Rule processServiceGenericAccount = context.getObjectByName(Rule.class, "Process Service or Generic Account");
		
		Map map = new HashMap();
		map.put("account", account);
		map.put("type", accountType);
		map.put("application", application);
		map.put("identityName", identityName);
		map.put("owner", managerIdentity);
		pLog.debug(appName+":Generic-account--creating identity Map-" +map);
		identity = context.runRule(processServiceGenericAccount, map);
		pLog.debug(appName+":Generic-account--Processing identity -" +identity);
	}
	pLog.debug("Processed " + accountType + "account and correlation identity is " + identity);

}
 else {

	// Look for an existing link.  If it is to a correlated identity, then keep it
	if (link != null &amp;&amp; link.getIdentity() != null &amp;&amp; link.getIdentity().isCorrelated()) {
		pLog.debug("Link already correlated to authoratative identity");
		identity = link.getIdentity();
		// Unless we're forcing re-correlation...
		if (application.getBooleanAttributeValue("forceRecorrelation")) {
			boolean recorrelate = true;
			/*
			 * Forced re-correlation.  If we're doing this for an authoratative application
			 * then it only makes sense if we're not already correlated elsewhere
			 */
			if (application.isAuthoritative())
			{
				List links = link.getIdentity().getLinks();
				for (Link l : links)
				{
					Application app = l.getApplication();
					if (!application.equals(app) &amp;&amp; app.isAuthoritative())
					{
						recorrelate = false;
					}
				}
			}
			if (recorrelate)
			{
				pLog.warn("Forcing recorrelation of: " + link.getNativeIdentity());
				identity = null;
			}
		}
	} else {
		
		// See if there is already an uncorrelated Identity for this application ProfileClass
		if (identity == null) {
			String correlateTo = application.getProfileClass();
			pLog.debug("Checking existing links for ProfileClass :" + correlateTo);
			if (!Util.isNullOrEmpty(correlateTo))
			{
				String nativeIdentity = account.getIdentity();
				pLog.debug("Searching Identities in: " + correlateTo + " using: " + nativeIdentity);
				QueryOptions qo = new QueryOptions();
				qo.addFilter(Filter.and(
					Filter.eq("application.profileClass", correlateTo),
					Filter.ignoreCase(Filter.eq("nativeIdentity",nativeIdentity))));
				qo.setCloneResults(true);
				List links = context.getObjects(Link.class, qo);
				if (links.size() == 1)
				{
					identity = links.get(0).getIdentity();
					pLog.debug("id: " + identity.getId());
				} else if (links.size() > 1) {
					identity = getUniqueIdentityFromLinks(links);
					if (identity != null) {
						pLog.debug("id: " + identity.getId());
					}
				}
			}
		}
		
		// Since a Rule fires before a config, run the config first and if we don't have a match then perform special handling
		// We use the application's correlationConfig to tell us what to match against
		if (identity == null || !identity.isCorrelated()) {
			if (correlationConfig != null) {
				List filters = correlationConfig.getAttributeAssignments();
				if (filters != null ) {
					for (Filter filter : filters)
					{
						String identityAttributeName = filter.getProperty();
						String linkAttributeName = filter.getValue();
						if (!Util.isNullOrEmpty(identityAttributeName) &amp;&amp; !Util.isNullOrEmpty(linkAttributeName)) {
							String linkAttributeValue = account.getStringAttribute(linkAttributeName);
							if (!Util.isNullOrEmpty(linkAttributeValue)) {
								pLog.debug("Running correlationConfig: " + identityAttributeName + " -> " + linkAttributeName);
								pLog.debug("Looking for " + identityAttributeName + "=" + linkAttributeValue);

								QueryOptions qo = new QueryOptions();
								setIgnoreWorkgroupIndexes(qo);
								qo.addFilter(Filter.ignoreCase(Filter.eq(identityAttributeName, linkAttributeValue)));
								List identities = context.getObjects(Identity.class, qo);
								pLog.debug("Found: " + identities.size() + " identities");
								if (identities.size() == 1)	{
									identity = identities.get(0);
									pLog.debug("id: " + identity.getId());
									break;
								} else if (identities.size() > 1) {
									pLog.debug(">1 Identities found, not correlating");
								}
							} else {
								// Don't really expect the correlation attribute to be null/empty
								pLog.debug("Correlation Attribute: " + linkAttributeName + " has value: " + linkAttributeValue + " - can't correlate");
							}
						}
					}
				} else {
					pLog.warn("CorrelationConfig is empty" );
				}
			} else {
				pLog.warn("Missing CorrelationConfig, defaulting to FirstName/LastName" );
			}
		}

		// Try and locate using the FullName but split up into firstname/lastname pair.
		if (identity == null || !identity.isCorrelated()) {
			String accountUserName = "";
			if (appName.equals("WagerAD")) {
				String wagerFName = account.getAttribute("givenName") != null ? account.getAttribute("givenName"):"";
				String wagerLName = account.getAttribute("sn") != null ?  account.getAttribute("sn"):"";
				accountUserName = accountUserName + wagerFName + " "+ wagerLName;
			} else if (appName.startsWith("KENO-APPLICATION-QLD") || appName.startsWith("NEO-APPLICATION")) {
					// FullName is Last, First
					accountUserName = account.getAttribute("FullName");
					if (accountUserName != null) {
						accountUserName = accountUserName.replaceFirst("(.*), (.*)", "$2 $1");
					}
				} else {
					accountUserName = account.getAttribute("FullName");
					if (accountUserName == null) {
						accountUserName = account.getAttribute("FULLNAME");
					}
				}
			if (accountUserName != null) {
				String [] nameparts = accountUserName.split(" ");

				String first = "";
				String last = "";
				for (int i=0; i &lt; nameparts.length ; i++){
					if (i==0) {
						first = first + nameparts[i];
					} else {
						last = last + nameparts[i] + " ";
					}
				}
				last = last.trim();
				if (!Util.isNullOrEmpty(first) &amp;&amp; !Util.isNullOrEmpty(last)) {
					pLog.debug("Looking for firstname=" + first + (last != null ? " and lastname=" + last : ""));
					QueryOptions qo = new QueryOptions();
					setIgnoreWorkgroupIndexes(qo);
					qo.addFilter(Filter.ignoreCase(Filter.eq("firstname", first)));
					if (!Util.isNullOrEmpty(last)) {
						qo.addFilter(Filter.ignoreCase(Filter.eq("lastname", last)));
					}
					List identities = context.getObjects(Identity.class, qo);
					pLog.debug("Found: " + identities.size() + " identities");
					if (identities.size() == 1) {
						identity = identities.get(0);
					}
				}
			}
		}
	}
}

// Did we find anything?
if (identity != null) {
	if (link != null) {
		if (identity.equals(link.getIdentity())) {
			pLog.debug("Link correlation unchanged");
		} else {
			if (account !=null &amp;&amp; link.getIdentity() != null &amp;&amp; identity != null) {
				pLog.info("Moved: " + account.getIdentity() + " from: " + link.getIdentity().getName() + " to: " + identity.getName());
			}				
		}
	} else {
		if (account != null &amp;&amp; identity != null) {
			pLog.info("Correlated: " + account.getIdentity() + " to: " + identity.getName());
		}			
	}
}

// If we had an existing link, and the correlator didn't find a better match, then keep it
if (identity == null &amp;&amp; link != null) {
	if(link.getApplication().getType().equals("Active Directory - Direct")){
	  String acctName = link.getAttribute("sAMAccountName");
	  if(acctName != null &amp;&amp; acctName.startsWith("pa_")){
		acctName = acctName.substring(3,acctName.length());
	  }else if(acctName != null &amp;&amp; acctName.endsWith("_admin")){
		acctName = acctName.substring(0,acctName.length()-6);
	  }
	   QueryOptions qo = new QueryOptions();
	   setIgnoreWorkgroupIndexes(qo);
	   qo.addFilter(Filter.ignoreCase(Filter.eq("tc_username", acctName)));
	   List identities = context.getObjects(Identity.class, qo);
	   if (identities.size() == 1) {
		 identity = identities.get(0);
	   }else{
		 identity = link.getIdentity();
	   }
	}else{
	  identity = link.getIdentity();
	}
	pLog.info("Retaining existing link: " + link.getNativeIdentity());
}


//Defaulting to existing uncorrelated identity if found

appName = application.getProfileClass();
if (Util.isNullOrEmpty(appName)) {
appName = application.getName();
}
appName = appName.replace(" ", “”);

String acctName;
if (!Util.isNullOrEmpty(appName) && appName.equals(“Office365”)) {
acctName = account.getAttribute(“userPrincipalName”);
}else{
acctName = account.getIdentity();
}

String delim = “_”;

String identityStr = “u” + delim + appName + delim + acctName;

identity=context.getObjectByName(Identity.class,identityStr);

if (identity != null &amp;&amp; !identity.isWorkgroup()) {
	result.put("identity",identity); 
}
return(result);</Source>

Please help me with correct Office365 Account Correlation rule

This is correlation rule :thinking:. How many account you have and how much time it take aggregation to complete ?

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.