04 March 2013

Maker Checker (Segregation of Duties)

Every now and then, I get asked about segregation of duties - in particular how to separate out the ability to create users versus the ability to activate them. Affectionately called maker-checker, this ability prevents a user with the Manage Users permission from escalating their own rights by creating a user that has more access than what they already have.

If you're interested, check out my previous blog posting for more detail on what Manage Users permission controls - it's a lot of stuff. As a result, I often recommend that an organization not have more than one or two users with this permission. However, you do need at least one user to have Manage Users permission which will give them the ability to not only create and edit users, but also to activate them and modify either their profiles or sharing settings. In other words, the keys to the kingdom.

This creates a scenario where a single individual within an organization can become, or enable others to become, the most powerful users in the org.  There are many issues that arise from providing this level of power including the ability to export any/all data or delete any/all data from an org without any appropriate organizational checks in place to prevent this type of behavior.

Below is a workaround designed to prevent any one person from having the ability to both create a user and make them active.  The theory here is that if a ‘Maker’ Administrator may not activate a user, they can't complete the act of creating a user without a second ‘Checker’ Administrator providing activation status. 

The solution below is based on a prototype that will meet most of these requirements.  The sample code should be used as an example of what is possible and not added to an org without fully understanding and testing the consequences that come with someone like me, a non-programmer, creating code.

To try this out, I suggest first creating a trial org or trying this in your sandbox; otherwise, you run the risk of locking yourself out of the user provisioning process by accident.

  1. As the Uber Admin (original System Administrator) create a new Profile: Maker Administrator.
    1. Enable Manage Users – to create users and assign profiles
    2. Enable API Enabled – to mass create and edit user records
    3. Enable Modify All Data – to change user profiles if the original profile already has Modify All Data (system requirement)
  2. As the Uber Admin (original System Administrator) create a new Profile: Checker Administrator.
    1. Enable Manage Users – to create users and assign profiles
    2. Enable API Enabled – to mass create and edit user records
    3. Enable View Setup and Configuration – for navigating to the users screen
  3. As the Uber Admin (original System Administrator) create a new Profile: Builder Administrator.
    1. Enable Customize Application – to create and edit validation rules
    2. Enable Author Apex – to create and edit Apex Triggers
    3. Enable View Setup and Configuration – for navigating the setup tree
  4. As the Uber Admin (original System Administrator) create a new User Validation Rule to prevent the Maker Administrator from changing the User Active Status
    1. Name: isActive
    2. Error Condition: AND( $Profile.Name = "Maker Administrator" , ISCHANGED( IsActive ))
    3. Error Message: You are not authorized to change the user activation status.
  5. As the Uber Admin (original System Administrator) create a new User Validation Rule to prevent the Checker Administrator from creating new user records
    1. Name: isNew
    2. Error Condition: IF( $Profile.Name = "Checker Administrator", ISNEW(),null)
    3. Error Message: You are not authorized to create new user records.
  6. As the Uber Admin (original System Administrator) create a new User Apex Trigger to change the isActive Boolean to false upon creation (overriding current behavior)

    trigger deactivateUserOnCreate on User (before insert) {
    for (User usr : Trigger.new){
    usr.isActive = false;

  7. As the Uber Admin (original System Administrator) create a new User Apex Trigger to prevent the Checker Administrator from changing any field EXCEPT the Active field.  Important Notes:
    1. Change the Profile ID to whatever the current org ID is for the newly created Checker Profile
    2. This won’t have an impact on User Related Lists
    3. Custom Fields may be restricted to read only through FLS rather than hard coding into the Trigger Below
    4. Additional Standard Fields created with each major release would have to be added into the code below
    5. Code may be found below:

    trigger preventCheckerUpdate on User (before update) {

    //User oldUsr : Trigger.old;

    for(User newUsr : Trigger.new){

    for (User oldUsr : Trigger.old){


    // Substitute the Checker Administrator ID below:
    (UserInfo.getProfileId() == '00e30000000vfwXAAQ') &&
    (oldUsr.Alias != newUsr.Alias) ||
    (oldUsr.CallCenterId != newUsr.CallCenterId) ||
    (oldUsr.City != newUsr.City) ||
    (oldUsr.CompanyName != newUsr.CompanyName) ||
    (oldUsr.ContactId != newUsr.ContactId) ||
    (oldUsr.Country != newUsr.Country) ||
    // (oldUsr.CurrencyIsoCode != newUsr.CurrencyIsoCode) ||
    // (oldUsr.DefaultCurrencyIsoCode != newUsr.DefaultCurrencyIsoCode) ||
    // (oldUsr.DefaultDivision != newUsr.DefaultDivision) ||
    (oldUsr.DelegatedApproverId != newUsr.DelegatedApproverId) ||
    (oldUsr.Department != newUsr.Department) ||
    (oldUsr.Division != newUsr.Division) ||
    (oldUsr.Email != newUsr.Email) ||
    (oldUsr.EmailEncodingKey != newUsr.EmailEncodingKey) ||
    (oldUsr.EmployeeNumber != newUsr.EmployeeNumber) ||
    (oldUsr.Extension != newUsr.Extension) ||
    (oldUsr.Fax != newUsr.Fax) ||
    (oldUsr.FirstName != newUsr.FirstName) ||
    (oldUsr.ForecastEnabled != newUsr.ForecastEnabled) ||
    (oldUsr.IsPortalSelfRegistered != newUsr.IsPortalSelfRegistered) ||
    (oldUsr.LanguageLocaleKey != newUsr.LanguageLocaleKey) ||
    (oldUsr.LocaleSidKey != newUsr.LocaleSidKey) ||
    (oldUsr.LastName != newUsr.LastName) ||
    (oldUsr.ManagerId != newUsr.ManagerId) ||
    (oldUsr.MobilePhone != newUsr.MobilePhone) ||
    (oldUsr.Name != newUsr.Name) ||
    (oldUsr.OfflinePdaTrialExpirationDate != newUsr.OfflinePdaTrialExpirationDate) ||
    (oldUsr.OfflineTrialExpirationDate != newUsr.OfflineTrialExpirationDate) ||
    (oldUsr.Phone != newUsr.Phone) ||
    (oldUsr.PortalRole != newUsr.PortalRole) ||
    (oldUsr.PostalCode != newUsr.PostalCode) ||
    (oldUsr.ProfileId != newUsr.ProfileId) ||
    (oldUsr.ReceivesAdminInfoEmails != newUsr.ReceivesAdminInfoEmails) ||
    (oldUsr.ReceivesInfoEmails != newUsr.ReceivesInfoEmails) ||
    (oldUsr.State != newUsr.State) ||
    (oldUsr.Street != newUsr.Street) ||
    (oldUsr.TimeZoneSidKey != newUsr.TimeZoneSidKey) ||
    (oldUsr.Title != newUsr.Title) ||
    (oldUsr.UserPermissionsAvantgoUser != newUsr.UserPermissionsAvantgoUser) ||
    (oldUsr.UserPermissionsCallCenterAutoLogin != newUsr.UserPermissionsCallCenterAutoLogin) ||
    (oldUsr.UserPermissionsMarketingUser != newUsr.UserPermissionsMarketingUser) ||
    (oldUsr.UserPermissionsMobileUser != newUsr.UserPermissionsMobileUser) ||
    (oldUsr.UserPermissionsOfflineUser != newUsr.UserPermissionsOfflineUser) ||
    (oldUsr.UserPreferencesActivityRemindersPopup != newUsr.UserPreferencesActivityRemindersPopup) ||
    (oldUsr.UserPreferencesApexPagesDeveloperMode != newUsr.UserPreferencesApexPagesDeveloperMode) ||
    (oldUsr.UserPreferencesEventRemindersCheckboxDefault != newUsr.UserPreferencesEventRemindersCheckboxDefault) ||
    (oldUsr.UserPreferencesReminderSoundOff != newUsr.UserPreferencesReminderSoundOff) ||
    (oldUsr.UserPreferencesTaskRemindersCheckboxDefault != newUsr.UserPreferencesTaskRemindersCheckboxDefault) ||
    (oldUsr.UserRoleId != newUsr.UserRoleId) ||
    (oldUsr.UserType != newUsr.UserType) ||
    (oldUsr.Username != newUsr.Username)))

    newUsr.addError('Trigger Err 1001 - You are not authorized to change anything except for the active state');
    // newUsr.addError('\nYour Profile: ' + UserInfo.getProfileId() + ' and Active is: ' + newUsr.isActive);

  8. As the Uber Admin (original System Administrator) create three new users and assign to the three new profiles: Maker, Checker, and Builder Administrators
  9. Modify any references to the Uber Admin in the org 
    1. Default Workflow User
    2. Case Assignment Rule Entries
    3. Case Escalation Rule Entries 
    4. Lead Assignment Rule Entries
    5. Default Lead Creator
    6. Google Apps Admin
    7. Portal Admins for customer and partner portals
    8. Workflow Tasks
    9. Workflow Alerts
  10. Have the Maker Administrator login and change the profile of the Uber Admin to Standard User
  11. Have the Checker Administrator login and deactivate the Uber Admin
  12. If the Uber Admin needs to be resurrected,  have the Maker Administrator change the profile of the Uber Admin back to System Administrator and have the Checker Administrator reactivate the Uber Admin User record.

Keep in mind:
1. Because both the Maker Administrator has both Manage Users and Modify All Data, they may use the Grant Login Access functionality to login as the Builder Administrator and deactivate or modify both the Validation rules and User Apex Triggers.  This risk may be mitigated by explicitly telling the Builder Administrator (or anyone with Customize Application permissions) not to grant login access.
2. The Builder Administrator in this scenario may be the only person to configure delegated Administration since Customize Application is required. However, anyone with Delegated Administrator access may potentially change user access, profiles, or login as an administrator. This risk may be mitigated by explicitly defining separate roles for the Administrators at the top of the hierarchy or defining no roles for the Administrators in a public share model.

One final note on this topic, any time we discuss segregation of duties and escalation of privileges, we get into a fundamental conundrum - if you need more than one person to perform some sensitive task productivity may ultimately be sacrificed to the audit gods. Creating users can be a very regular occurrence, but ensuring that you have maker-checker segregation of duties ensures that if one person is out of the commission, you have stopped what may be a critical day-to-day process. It's a slippery, though sometimes necessary, slope on the road to preventing escalation of privileges. 


  1. This is very interesting! We've been struggling with a different separation of duties - trying to segment out security admin tasks (users, profiles, etc) vs. the rest of system administration (enabling the business). Our regulations require these duties to be separate in production. Any resources that help to draw that line that you can point me to? Or can that be the subject of a post?


    1. Hi James, can you tell me some more about what kinds of separation you need for admins or users? The example above goes into a very specific use case of how to prevent admins from both creating *and* activating users. What kinds of use cases do you have, whether they be about separating duties around data, user, customization, or any other kind of org management? I'm always looking for a good blog post ideas. Thanks!

    2. The standard "System Administrator" profile - we'd like to break into two custom profiles. This would create separation of duties to comply with our policies.

      Here's my functional breakdown:
      Security Administrator - Responsible for safeguarding the system and the data within it. Makes SFDC setup comply with information security policy and user access consistent with their business need. Sets up new users, terminates users as needed, manages profiles and role hierarchy - including assignment of page layouts and field level security. Reviews logs to conduct periodic audit of user access.

      Business system administrator - responsible for aligning SFDC functionality with business needs. This includes fields, objects, page layouts, reports, and managing queues.

      Obviously, these two roles need to coordinate significantly to be successful. The larger pain point is to create a good Business System Administrator profile. Can't seem to accomplish this without adding back in "Manage Users" (for queues) or "View All".


Note: Only a member of this blog may post a comment.