Daily Processing For Permission Set Access

I came across a use case earlier this month where a Salesforce business owner needed to be able to role out some functionality in a series of waves. Basically, the business was planning on running a pilot with a subset of users for a period of time and then at some interval they planned on granting access to additional users. The business owners wanted to be able to maintain that access and they wanted to be able to turn it on easily.

The org in which this logic was being utilized was large (approximately 5,000 users) and the users that would be accessing the functionality were grouped into territories but the management of that territory designation was simply a field on the User record not the out-of-the-box territory management provided by Salesforce natively.

Also, the waves were going to span a number of months and access was anticipated to be to a couple hundred users at a time.

You are probably thinking that the natural choice here is to use permission sets to grant application/functionality access. I agree. The issue with permission sets is the maintenance. One-off assignment to a given permission set is fine but granting access to many people requires a little more work and manually could be done with the data loader. But, as I mentioned, the business owners wanted something simple and easy. So no data loader.

The final solution was to create a permission set with the proper configuration and object privileges but then to automate the process of granting access. This was accomplished by creating a table (custom setting) and populating that table with the various territory values that each User might have. Then I created a checkbox (boolean) field for denoting when a specific territory should have access to the permission set. Next, I created some batch apex logic that would do the heavy lifting of granting access to the permission set. Finally, I scheduled that apex logic to run on a daily basis.

By scheduling the batch process to run daily, the business owners could indicates the territories that should and should not have access to the fundamental application logic and know that their User's would have access granted/revoked that same day. This allowed the business owners the flexibility they were seeking without having to ask for development or admin help, which could cause delays to their processes.

Now that you know the background you can make some sense of the apex class below:

/*
	Created by: Greg Hacic
	Last Update: 10 February 2014 by Greg Hacic
	Questions?: greg@interactiveties.com
*/
global class batchPermissionSetAccess implements Database.Batchable<SObject> {

	//the "start" method is called at the beginning of a batch Apex job
	//use this method to collect the records (of objects) to be passed to the "execute" method for processing
	global Database.QueryLocator start(Database.BatchableContext BC) {
		//query for Users with the active territory values
		Set<String> activeTerritories = new Set<String>(); //holds customTerritoryObject__c values
		String soql = 'SELECT Id FROM User WHERE isActive = true AND'; //query string variable
		for (sasiCTARegions__c t : [SELECT territoryName__c FROM customTerritoryObject__c WHERE isActive__c = true]) { //for all active territories
			activeTerritories.add(t.territoryName__c); //add the territory name to our list
		}
		if (activeTerritories.isEmpty()) { //if we did not find any active territories
			soql += ' Territory__c = \'nothingtoquerytoday\''; //use some random filter to prevent logic from actually getting SOQL results
		} else { //otherwise, we found some territories
			soql += ' ('; //open parenthesis for SOQL statement filters
			Integer counter = 0; //create a counter variable
			for (String a : activeTerritories) { //for all active territories
				counter++; //increment the counter
				if (counter != 1) { //if the counter is not equal to 1
					soql += ' OR'; //append the OR filter
				}
				soql += ' Territory__c = \''+a+'\''; //append the spcific territory filter
			}
			soql += ')'; //close the parenthesis
		}
		return Database.getQueryLocator(soql); //run the query
	}

	//the "execute" method is called after the "start" method has been invoked and passed a batch of records
	global void execute(Database.BatchableContext BC, List<SObject> scope) {

		List<PermissionSetAssignment> newPermissionSetAccess = new List<PermissionSetAssignment>(); //list for new permission sets
		Set<String> usersWithAccess = new Set<String>(); //set for users Ids with access to the permission set already
		Id psaId; //Id of the permission set we want Users to be assigned

		//query for the permission set Id
		for (PermissionSet ps : [SELECT Id FROM PermissionSet WHERE Name = 'permissionSetAPIName']) {
			psaId = ps.Id; //assign the premission set Id
			for (PermissionSetAssignment psa : [SELECT AssigneeId FROM PermissionSetAssignment WHERE PermissionSetId = :ps.Id]) { //query for the permission set Users that are already assigned
				usersWithAccess.add(psa.AssigneeId); //add the Id of each assigned User to our set
			}
		}

		//compare the list of all users to the list of already granted users and make another list of those missing access
		//take that list of missing access and grant access
		for (SObject s : scope) { //for all objects from our batch
			User u = (User)s; //grab the individual User record
			if (!usersWithAccess.contains(u.Id)) { //if the User is not in our 'already has access' set
				PermissionSetAssignment newPSA = new PermissionSetAssignment(); //PermissionSetAssignment sobject
				newPSA.PermissionSetId = psaId; //set the permission set Id
				newPSA.AssigneeId = u.Id; //set the User Id
				newPermissionSetAccess.add(newPSA); //add the record to our list
			}
		}
		if (!newPermissionSetAccess.isEmpty()) { //if there are records to insert
			insert newPermissionSetAccess; //insert
		}
	}

	//the "finish" method is called once all the batches are processed
	global void finish(Database.BatchableContext info) {
		//do nothing...
	}
}

Again, this Apex class is the logic that is scheduled to run on a daily basis. You will need the scheduleable class in order to actual create the schedule. Let me know if you need something like that and I can make publicly available on this site...

Automated Exchange Rates in Salesforce.com

Reduce Repetitive Tasks, Eliminate Errors & Free Up Your Administrators.

Birthday Reminders for Salesforce.com

It might lead to a sale. Or it might make you feel good.