JDBC Provisioning Rule showing NULL values for Roles/Entitlements

Ok, I need some help with this one…. I’m writing a JDBC Provisioning Rule and I’m running into a problem that has been giving me a headache for weeks now…

Here’s the simplified version of my code:

	import //(lots of imports. I won't list them all. They are the class imports needed from java.sql to sailpoint.object, blah blah blah)
	
	public String getAttributeRequestValue(AccountRequest acctReq, String attribute) {
	      if ( acctReq != null ) {
	        AttributeRequest attrReq = acctReq.getAttributeRequest(attribute);
	        if ( attrReq != null ) {
	          return attrReq.getValue();
	        }
	      }
	      return null;
	    }
	     // Initialize the result
	    
	     PreparedStatement statement = null;
	     CallableStatement cstmt = null;
	     ResultSet resultSet = null;
	
	 if (plan != null) {
	      List accounts = plan.getAccountRequests();
	      
	     
	      
	      if ( ( accounts != null ) && ( accounts.size() > 0 ) )  {
	       
	        for (AccountRequest account : accounts) {
	          try {
	            if (AccountRequest.Operation.Create.equals(account.getOperation())) {
	              // Get values from the provisioning plan
	              
	              String nativeIdentity = account.getNativeIdentity(); //1
	              
	              String lastName = getAttributeRequestValue(account, "LastName"); //2
	              
	              String firstName = getAttributeRequestValue(account, "FirstName"); //3  
	              
	              String email = getAttributeRequestValue(account, "Email"); //4
	                            
	                         
	 
	             
	              // Check if the user already exists in SQL database
	              statement = connection.prepareStatement("USE sprocDatabase SELECT COUNT(*) FROM userDatabase.dbo.User WHERE FirstName = ? AND LastName = ? AND Email = ?");
	              statement.setString(1, firstName);
	              statement.setString(2, lastName);
	              statement.setString(3, email);
	                    
	              resultSet = statement.executeQuery();
	              resultSet.next();
	              int countBefore = resultSet.getInt(1);
	              if (countBefore > 0) {
	              // User already exists, log the message and continue to adding user to required groups
	                log.error("User already exists within the database.... Continuing to add to requested groups");
	                return null;
	                
	                       
	              } else {
	              // User doesn't exist, create the account in the SQL database before adding to groups
	                
	          
	                String sql = "{call dbo.idmgr_CreateNewUserAccount_SAILPOINT(?, ?, ?, ?, ?, ?)}";
	                
				  cstmt = connection.prepareCall(sql);
	                  
	                // Set input parameters
	                cstmt.setString(1, nativeIdentity);
	                cstmt.setString(2, lastName);
	                cstmt.setString(3, firstName);
	                cstmt.setString(4, email);
	               
				 AttributeRequest officeName = account.getAttributeRequest("OfficeID");
	                  
	                    if (officeName != null && ProvisioningPlan.Operation.Remove.equals(officeName.getOperation())) { 
	                      cstmt.setNull(5, Types.NULL);
	                      
	                    } else if (officeName != null) {
	                      cstmt.setString(5, officeName.getValue());
	                     
	                      } else {
	                        log.error("OFFICENAME FROM ATTRIBUTEREQUEST IS NULLEMPTY");
	                      }
	                                
	              AttributeRequest roleName = account.getAttributeRequest("RoleGroupID");              
	                    if (roleName != null && ProvisioningPlan.Operation.Remove.equals(roleName.getOperation())) { 
	                      cstmt.setNull(6, Types.NULL);
	                     
	                    } else if (roleName != null){
	                      cstmt.setString(6, roleName.getValue());
	                      
	                      } else {
	                        log.error("ROLENAME FROM ATTRIBUTEREQUEST IS NULLEMPTY");
	                      }
	                
	      
	                // Execute stored procedure
	                cstmt.executeUpdate();                   
	                cstmt.close();
	    
	                ProvisioningResult result = new ProvisioningResult();
	                result.setStatus(ProvisioningResult.STATUS_COMMITTED);
	                             
	                      
	                      
	                      
	                  
	              
	                
	
	  
	              
	            
	          } else if (AccountRequest.Operation.Modify.equals(account.getOperation())) {
			/* Modify account request -- change role*/         
	        } else if ( AccountRequest.Operation.Delete.equals( account.getOperation() ) ) {
	             //disable
	        } else if ( AccountRequest.Operation.Disable.equals( account.getOperation() ) ) {
	          // Disable, not supported.
	        } else if ( AccountRequest.Operation.Enable.equals( account.getOperation() ) ) {
	          // Enable, not supported
	        } else if ( AccountRequest.Operation.Lock.equals( account.getOperation() ) ) {
	          // Lock, not supported.
	        } else if ( AccountRequest.Operation.Unlock.equals( account.getOperation() ) ) {
	          // Unlock, not supported.
	        } else {
	          // Unknown operation!
	        }
	
	      
	    
	      }catch (SQLException e) {
	            ProvisioningResult result = new ProvisioningResult();
	            result.setStatus(ProvisioningResult.STATUS_FAILED);
	            
	          } finally {
	            if (statement != null) {
	              statement.close();
	            }
	            if (resultSet != null) {
	              resultSet.close();
	            }
	            if (cstmt != null){
	              cstmt.close();
	            }
	            
	      }
	    }
	  } else {
	    result.addError("ACCOUNT IS NULL!!!");
	  }
	}
	    
	        
	      
	    
	    
	    return result;

Here’s the problem I’m running into:

I do not receive any errors from the Sailpoint IDN/ISC side. As far as ISC can tell, the user was setup successfully in the database and the user was assigned their respective entitlements:

However… the user is never set up in the SQL database because the SPROC won’t trigger if the “role” fields are NULL…. Which led me to my CCG log files where I found this…

"message":"OFFICENAME FROM ATTRIBUTEREQUEST IS NULLEMPTY","pipeline":"12…….
"message":"ROLENAME FROM ATTRIBUTEREQUEST IS NULLEMPTY","pipeline":"12….

This is because of these two lines in my code:

Which tells me that:

AttributeRequest officeName = account.getAttributeRequest("OfficeID");

AND

AttributeRequest roleName = account.getAttributeRequest("RoleGroupID");'

are NULL.

But that doesn’t make sense…

If account.getAttributeRequest("OfficeID") and account.getAttributeRequest("RoleGroupID") are NULL, why are they assigned on the Identity Cube in Sailpoint?

Shouldn’t those be NULL too then?

Also, why are account.getAttributeRequest("OfficeID") and account.getAttributeRequest("RoleGroupID") showing as NULL, when their respective Entitlements show that they have values?

!

What am I missing fam? Am I not using account.getAttributeRequest() correctly? I wish I could see the ProvisioningPlan so I could see what these values look like, but plan.toXml doesn’t work in Sailpoint IDN/ISC….

A Haiku:

I need your help, please. 
I've lost my mind with this one. 
All help is welcomed. 

Thank you! <3

@bburrell I saw that you put a logic to validate if the account already exist in the DB, but in that case wouldn’t be a modify action trigged by IDN?
I’m not so sure how is the provisioning plan in that case when you send multiples objects.

What you’re saying makes sense. That could be a possibility. To test, I removed ALL code and only left this line of code in to see what the Provisioning Plan was building from the account.

if (plan != null) {
      List accounts = plan.getAccountRequests();

      if ( ( accounts != null ) && ( accounts.size() > 0 ) )  {
       

        for (AccountRequest account : accounts) {
          List attrReq2 = account.getAttributeRequests();

          if (attrReq2 != null) {
            for (AttributeRequest attrReq3 : attrReq2){
              log.error("LOOK HERE FIRST!!!" + attrReq3.getName() + "::" + attrReq3.getValue());
            }
          }
        }
      }
 } else {
    result.addError("ACCOUNT IS NULL!!!");
  }

    return result;

The results from that are confusing…

Here are the results of the supposed ProvisioningPlan…

Column 1 Column 2
AttributeName Value
TeamID (Entitlement) 156156
FirstName Javeria
LastName Lithikar
Email [email protected]
Position Analyst

I can tell you right off the bat that a LOT of things are missing here.

First, the request contained 3 Entitlements as seen here:

And before I made any changes, those Entitlements WERE assigned to the user:

But for some reason, only the Teams Type is showing up in the Provisioning Plan, as seen in the logs:

Furthermore, the results from the ProvisioningPlan are missing attributes from the Source.
If I understand this correctly, the Provisioning Plan should build it’s map based on the attributes defined in the CreateAccount Provisioning Policy on the Source.

These are the attributes we have defined:

  1. FirstName
  2. LastName
  3. Email
  4. UserName
  5. Initials
  6. Position
  7. Phone

If you scroll up and review the results of the ProvisioningPlan, we are mising the following:
4. UserName
5. Initials
7. Phone

So at this point, I am confused as to how the ProvisionigPlan is being generated. Any advice is appreciated.
Thanks!

I have a rule for JDBC provisioning that’s way more simple but i think that can help you!

Provisioning Policy


    "name": "Account",
    "description": null,
    "usageType": "CREATE",
    "fields": [
        {
            "name": "networkLogin",
            "transform": {
                "type": "identityAttribute",
                "attributes": {
                    "name": "networkLogin"
                }
            },
            "attributes": {
                "cloudRequired": "true"
            },
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "displayName",
            "transform": {
                "type": "identityAttribute",
                "attributes": {
                    "name": "displayName"
                }
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "email",
            "transform": {
                "type": "identityAttribute",
                "attributes": {
                    "name": "email"
                }
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "cpf",
            "transform": {
                "type": "identityAttribute",
                "attributes": {
                    "name": "cpf"
                }
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        },
        {
            "name": "personNumber",
            "transform": {
                "type": "identityAttribute",
                "attributes": {
                    "name": "personNumberSap"
                }
            },
            "attributes": {},
            "isRequired": false,
            "type": "string",
            "isMultiValued": false
        }
    ]
}```

Provisioning Rule:
imports*****
  public String getAttributeRequestValue(AccountRequest acctReq, String attribute) {
    log.error("=== ATRIBUTOSSS -- "+ acctReq.toString());
    if ( acctReq != null ) {
      AttributeRequest attrReq=acctReq.getAttributeRequest(attribute);
      if(attrReq!=null){
          return attrReq.getValue();
      }
    }
    
    return null;
  }  
  ProvisioningResult result = new ProvisioningResult();
  PreparedStatement statement;

  if ( plan != null ) {

    List accounts = plan.getAccountRequests();
    if ( ( accounts != null ) && ( accounts.size() > 0 ) ) {
      for ( AccountRequest account : accounts ) {
        try {
          if ( AccountRequest.Operation.Create.equals( account.getOperation() ) ) {

            statement = connection.prepareStatement( "insert into emergency_exceptions_users (conta,nome,email,cpf,personNumber,idRole) values (?,?,?,?,?,?)" );
            statement.setString ( 1, getAttributeRequestValue(account,"networkLogin") );
            statement.setString ( 2, getAttributeRequestValue(account,"displayName") );
            statement.setString ( 3, getAttributeRequestValue(account,"email") );
            statement.setString ( 4, getAttributeRequestValue(account,"cpf"));
            statement.setString ( 5, (String) account.getNativeIdentity() );
            statement.setString ( 6, getAttributeRequestValue(account,"idRole") );
            statement.executeUpdate();
            result.setStatus( ProvisioningResult.STATUS_COMMITTED );        

          } else if ( AccountRequest.Operation.Modify.equals( account.getOperation() ) ) {
            AttributeRequest attrReq = account.getAttributeRequest("idRole");
            if ( attrReq != null && ProvisioningPlan.Operation.Remove.equals(attrReq.getOperation()) ) {
                statement = connection.prepareStatement( "delete from emergency_exceptions_users where personNumber = ?" );
                statement.setString ( 1, (String) account.getNativeIdentity() );
                statement.executeUpdate();
                result.setStatus( ProvisioningResult.STATUS_COMMITTED );
            }

            if ( attrReq != null && ProvisioningPlan.Operation.Add.equals(attrReq.getOperation()) ) {
                log.info("=== EmergencyShutdown-updateUserPermission - Start");
                statement = connection.prepareStatement( "update emergency_exceptions_users set idRole=? where personNumber = ?" );
                statement.setString ( 1, getAttributeRequestValue(account,"idRole") );
                statement.setString ( 2, (String) account.getNativeIdentity() );
                statement.executeUpdate();
                result.setStatus( ProvisioningResult.STATUS_COMMITTED );
            }

            result.setStatus( ProvisioningResult.STATUS_COMMITTED );

        } else if ( AccountRequest.Operation.Disable.equals( account.getOperation() ) ) {
            statement = connection.prepareStatement( "delete from emergency_exceptions_users where personNumber = ?" );
            statement.setString ( 1, (String) account.getNativeIdentity() );
            statement.executeUpdate();
            result.setStatus( ProvisioningResult.STATUS_COMMITTED );

        }        
      }
        catch( SQLException e ) {
          result.setStatus( ProvisioningResult.STATUS_FAILED );
          result.addError( e );
        }
        finally {
          if(statement != null) {
            statement.close();
          }
        }
      }
    }
  }

  return result;
I think the first action will be the creation event where you can get all users attributes and the entitlement Id, you need to point the id name that you have mapped and then a Add event where you will update the user permissions.

obs: the only provisioning policie that i created was for account creation.

Each entitlement requested and approved is being provisioned on its own path, so you are dealing with a create and then two modifies after, so your provisioning rule should technically be firing three times. Your original rule was only looking for everything in the Create operation, so you’ll need to update your logic to also look for the entitlement attribute requests in the Modify operation block.

1 Like

Oh wow! This is some good information from both of you! Thank you! I will test this out and get back to you! :slight_smile:

I’m still curious why all the attributes I created aren’t coming through on the Provisioning Policy though…

This was definitely the solution. I didn’t realize that was how it worked. (Couldn’t find any information on this in Community Portal or standard Sailpoint Documentation.)

So this means if you select 3 entitlements, Sailpoint will pick a random entitlement to include with the “create” operation, and will then pass the remaining entitlements through the “modify” operation.

Sounds messy. But I understand it at least now. :sweat_smile:
Thanks Patrick! <3

2 Likes