Tuesday, November 27, 2012

Force.com object and record level security

Lately I've been involved in several discussions around how the Force.com platform handles object and record level security. I'm surprised that there's still a lot of confusion around this topic despite all of documentation available out there so I'll try to explain this topic in more detail. It usually helps to start the conversation referring to Jason Ouellete’s “Development with the Force.com Platform” book where he illustrates these layers of security as a funnel.




Each request has to go through several layers starting with CRUD and FLS checks and then moving to verifying org-wide default sharing model and any exceptions to org-wide sharing model if applicable. If the request “survives” through all of these checks then access is granted.

This funnel provides a great way to illustrate how data security works at a high level but it still doesn't explain all the nuts and bolts involved with sharing rules and how to enforce these levels of security when using Apex.

We have different layers when dealing with data security in Force.com. Every time there’s a request, access is determined by analyzing two levels of security: object-level and record-level. 

Object-level security


Object-level security is defined by permissions in the profile or permission set(s) and includes the following:

- CRUD

+ Create, Read, Update, Delete permissions on the object

- Field Level Security (FLS) 


            + Read and Edit permissions for specific fields on the object

* If “View All”  is selected, user can view all records associated with this object, regardless of record-level security (see next section).

* If “Modify All” is selected, users can read, edit, delete, transfer, and approve all records associated with this object, regardless of record-level security (see next section).


Record-level security 


Once we get “clearance” from the object-level security layer, the next step is to check for record-level access. Record-level access is determined by:

- Full ownership


If the user that submitted the request is the owner of the record then he/she has full ownership and therefore it’s possible to perform read and write operations on the record. If the user doesn’t have full ownership then the next step is to look at the org-wide default sharing model.

- Org-wide sharing default (base sharing model for the object)


            + Public Read/Write

If the sharing model is set to “Public Read/Write” then any user will have access to do Read/Write operations on any record (assuming CRUD/FLS permissions provide the right level of access)

            + Public Read
           
If the sharing model is “Public read” then only read operations can be done on all records unless additional “sharing rules” (exceptions to the sharing model)  are configured (see sharing rules section).

            + Private

If the sharing mode is set to “Private” then a user can only do read/write operations on records where he/she is the owner unless additional “sharing rules” (exceptions to the sharing model) are configured (see sharing rules section).

            + Controlled by Parent

If a record is the detail of a master-detail relationship then the access-level to this record is determined by the parent. The platform provides an additional setting while defining a master-detail relationship where we can specify the minimum level of visibility on the parent record in order to be able to edit detail records. This setting is very important as the default value is “Read/Write” so if you’re expecting other users will normally get “Read-Only” on the parent object and you need to give access to detail records, then you should also set the sharing setting in the master-detail field to “Read Only” to make this consistent.

If the object is a “junction” object, meaning it has two master detail relationships, then we must validate the sharing setting on both relationships.

- Inherited access


If “Grant Access Using Hierarchies” is selected while defining the org-wide sharing model  then users higher in the hierarchy will have the selected base level of access to the data owned by or shared with their subordinates in the hierarchy.  "Grant Access Using Hierarchies" is always selected on standard objects (e.g. Account, Opportunity)

- Sharing rules (exceptions to org-wide sharing model)


Sharing rules provide exceptions to the org-wide sharing model and they can never be more restrictive than the level of access defined by the org-wide sharing model.

Objects with an org-wide sharing model different from “Public Read/Write” have a separate object where sharing is maintained. For custom objects, the name of this sharing object is followed by the suffix “__Share” (e.g. CustomObject__Share) and for standard objects it’s just followed by “Share” (e.g. ContactShare). This object stores records that represent visibility and level of access to specific users or group of users.

Each record in the share object stores the target record Id (ParentRecordId), the User or Group Id (UserOrGroupId) that has access to the target record, the level of access to the record (AccessLevel) and a sharing reason (RowCause).

To learn more about how access is granted to records and all the tables involved with access grant you can read the following document:


There are 3 types methods for creating sharing rules and each provides its own reasons (row cause) :

- Force.com managed Sharing

Reasons / Row Causes:
    + Owner
User is the owner of the record or is in a UserRole above the record owner in the role hierarchy.
    + ImplicitChild (Account Sharing)
Indicates that the record has been granted access because the Account related to the record has been shared to the specified User or Group Id. Since the parent was shared, access to child records has been granted with an “ImplicitChild” reason 
    + ImplicitParent (Associated record owner or sharing)
Indicates that the User or Group specified in the sharing record has separate access to an Opportunity or Case associated with this Account, and so they are automatically given access to the Account.

    + Team (Opportunity Sales Team, Account Sales Team)
Indicates that the user has access to the record because he/she is part of the sales team for this record.

    + TerritoryRule (Territory Assignment Rule)

Indicates that a territory assignment rule definition granted access for this record to the specified Group.

For example: An account assignment rule that assigns accounts to territories based on account fields. Only available if territory management has been enabled for your organization. 
    + Rule (Owner or Criteria based sharing Rule) 
Indicates that the sharing was created as part of an owner or criteria based sharing rule definition.

Owner based  sharing rules are used when we have a well defined user or group of users that should always share records with another user or groups of user.

Criteria-based sharing rules are used when we have more dynamic sharing requirements, for example based on certain field values instead of record ownership.


- User-managed Sharing


Reasons / Row Causes: 
    + Manual (Manual Sharing) 
Manual sharing also provides a very dynamic way of sharing but it involves user interaction. With manual sharing, the record owner (or a user with “Modify All” permissions) can share the record with a specific user, public group, or role.

* It’s important to know that manual sharing is removed when the record owner changes or when the access granted in the sharing does not grant additional access beyond the object's base organization-wide sharing model. If you need to maintain the existing sharing then you should look at the Apex Managed sharing. 
    + TerritoryManual (Territory Manual Sharing)
Indicates that the  record was manually assigned to a Territory. 

- Apex-managed Sharing

Apex-managed sharing is a type of "Programmatic Sharing" which allows you to define sharing records in the previously mentioned sharing objects. It also allows us to create a custom sharing reason (defined in the object’s detail page) to associate with your programmatic share.

Apex-managed sharing can be used for both standard and custom objects; however, custom sharing reasons can only be defined for shares written to custom objects. One additional access level developers can define with Apex-managed sharing is “Full Access”, which gives view, edit, transfer, share and delete permissions.

Here’s a summary of access levels that can be defined through Force.com, User and Apex-managed sharing:


Access Level
API Name
Description
Private
None
Only the record owner and users above the record owner in the role hierarchy can view and edit the record. This access level only applies to the AccountShare object.
Read Only
Read
The specified user or group can view the record only.
Read/Write
Edit
The specified user or group can view and edit the record.
Full Access
All
The specified user or group can view, edit, transfer, share, and delete the record.

(This access level can only be granted with Force.com managed sharing.)

 

Additional Considerations


High Volume Portal Users


High Volume Portal users (HVPU) have special considerations when it comes down to the sharing model.
These type of users don’t have roles and they can’t be included in any of the following:

      Personal groups or public groups.
      Sharing rules.
      Account teams, opportunity teams, or case teams.

HVPU can only access records if any of the following criteria is met:

      They have “Update” access on the Account that they belong to.
      They own the record.
      They can access a record's parent, and the organization-wide sharing setting for that record is Controlled by Parent.
      The organization-wide sharing setting for the object is Public Read Only or Public Read/Write.
      The record is the account or contact under which they are enabled.

Additionally, access can be granted to objects through configuration of a sharing sets under “Customer Portal” settings as  long as:

      Objects has an organization-wide sharing setting different from Public Read/Write.
      Objects is available for Customer Portal.
      Custom object has a lookup field to account or contact.

Apex


How these layers of data security are handled by Apex is another big source of confusion. We first need to understand two concepts here : “User Context” and “Security Context”.

User Context

User Context refers to the user that invoked the transaction and Apex is always aware of this user. For example, if User A inserted a record and this fired the execution of some Apex code in a trigger or class then the user context for this event will be User A and that won’t change during the whole transaction.

Security Context

Security Context defines the level of permission when executing Apex Code. There are two types of security contexts: “with sharing” or “without sharing”.  It’s important to know that regardless of the context, CRUD and FLS are not enforced automatically so we’ll need to add checks in the code to validate this.

- “Without Sharing” context (system context)

Without sharing context a.k.a. “system” context is set when “without sharing” keywords are used in the class definition or when no keywords are specified at all. If this security context is used, then record access for the invoking user context is not taken into account. In other words, Apex can do whatever it wants.

- “With Sharing” context (running user context)

If we use “with sharing” a.k.a. “running user” context then the code will enforce all sharing rules for the invoking user. For developers that think that Apex runs in some sort of “GOD Mode” this could cause unexpected behavior such as:

      SOQL/SOSL Queries returning a limited set of records
      Code trying to insert/update/delete a record throws “Insufficient Privileges“ exception.
      Code trying to set the value of a reference field (lookup or master-detail) throws an “Insufficient Privileges“ exception.

Going back to the funnel diagram, interactions with the Salesforce UI or standard Web Service API calls will automatically enforce the first layer of security (object-level). However, if you build a custom apex controller/extension or a custom web service then you need to manually enforce CRUD/FLS permissions and record access validations, otherwise any user with access to this class can perform CRUD operations on records that their profile normally wouldn’t allow. The following points describe how we can enforce CRUD, FLS and Record access.

- CRUD and FLS enforcement

To enforce CRUD and FLS in apex, the platform provides functionality to “describe” the object metadata.

For example:

To verify if the user has access to update the “Name” field in contact we can use:

Schema.sObjectType.Contact.fields.Name.isUpdateable()

To validate the user has access to delete records for the Lead object we could use:

Lead.sObjectType.getDescribe().isDeletable()

This can even be validated in Visualforce by using:

<apex:commandButton action="{!CustomDelete}" rendered="{!$ObjectType.Contact.Deletable}" />

You can learn more about describe calls here:


It’s also recommend adding the “Force.com ESAPI” library to your codebase. It has a lot of handy methods to validate CRUD and FLS.


- Record-level security enforcement



What about record-level security in Apex? Enforcing this layer of security in Apex is easy, you just need to use the “with sharing” keywords in your class. By doing this, Apex will enforce all the rules related to record-level security mentioned previously.

Here are some examples of how enforcing sharing rules could impact your Apex classes:

      SOQL/SOSL will only return records the user is allowed to see , so queries may return less records.
      DML operations may fail since write operations on existing records will be validated to see if the current user has “Write” access according to the record-level sharing rules. This could happen if the user tries to set an Id in a lookup or master-detail field to which he/she has no access or if the access level on the master record is not aligned with the sharing settings on the master-detail field definition.
      If a record already has an existing value for a lookup field and it references an object that the user doesn’t have access to, then any DML operation will execute fine as long as the value for this lookup field is not being changed.


For more information on “with sharing” and “without sharing” keywords please read the following article:


When to bypass CRUD/FLS enforcement


      Interacting with objects that are completely hidden the user
      An example of this is a custom object that simulates a “custom setting” and you don’t want to expose this object anywhere in the Salesforce UI (Tabs, Reports, Detail view, etc...)

      Updating fields that are visible to users of Profile B when User from Profile A perform an action
      Normally this should be done through triggers as triggers should be able to execute operations that the user is normally not allowed to do. However, if this action can’t be done through a trigger then you should validate some context of the user and decide if you should bypass FLS for the current user.

When to use “without sharing”


We should always try to enforce sharing in our Apex classes, however there may be some situations where “without sharing” is needed. We need to carefully analyze these situations and make sure we understand all the risks involved when operating “without sharing”. Also, most of the class logic should run using “with sharing” and use some secure logic to decide if we should execute specific logic that runs in “without sharing” context based on the user context. ESAPI library provides an easy way to set the sharing enforcement context which is very helpful for these situations.

Here are some examples where using “without sharing” makes sense:

      Sharing model limitations

      If you’re using High Volume Portal users and trying to operate on a custom object that has a sharing model different from “Public Read/Write” and the object has no lookup fields to account or contact then you may have to use “without sharing”. A way of mitigating this is by adding the appropriate lookup fields to create the right sharing set configuration however sometimes this means a major change in the data model and things could get even more complicated if you’re trying to add this logic for objects that are part of managed package.

      Users needs to execute an action to operate on all object records

      This scenario is normally a cause of debate since operations on all records should ideally run in batch contexts and this could be scheduled or manually executed by a system administrator which normally has “view all and modify all” permissions. If this really can’t be part of your process, then you should consider creating sharing rules to solve the visibility problem. If none of these options solves your problem then you may have a valid case to run “without sharing”.

      Interacting with a custom object that simulates a “custom setting”

      There may be some cases where you have a custom object used to store custom settings. In this scenario, always consider what type of data is stored in this object. If the data contains secrets or “trusted” configuration data then you should be very careful and consider saving these values inside a protected custom setting in a managed package.
      For more information on storing secrets in custom settings read the following article : http://wiki.developerforce.com/page/Secure_Coding_Storing_Secrets#Apex_and_Visualforce_Applications

      If the data is not sensitive  you can explore the option of moving this to a protected custom setting.

      If the custom setting option is not possible and it’s OK to expose this data to everyone then you can try setting the sharing model to  “Public Read Only”.

      If “Public Read Only” is not acceptable then you should set the sharing model for this object to “Private” and remove all CRUD access for your users.  You can still explore using “sharing rules” to enforce sharing and just bypass CRUD/FLS validations in the controller but if this is not possible then you’ll need to use “without sharing” to make sure the controller has access to all the records.

How to identify potential object or record level security issues


      A good way to verify if record-level access is being enforced is to login as one of your end users to workbench (https://workbench.developerforce.com/login.php) and see what objects this user has access to (if your users don’t have "API Enabled" you'll need to temporarily enable this permission). Once you're logged into workbench take a look at all the objects exposed to this user and then run some SOQL queries on the objects just to get the count of records. Doing these steps can give you clues to determine if you have any data security issues.

      Search across your project for "without sharing" keywords. You can use the Force.com IDE to import your metadata to eclipse and then search across the project for any classes that contain "without sharing". If you get any results, verify if it's really necessary use "without sharing" in any of the classes/triggers that matched your search.

      Search across your project for SOQL, SOSL and DML clues and verify if CRUD and FLS is enforced. For example, searching for the following keywords will give you an idea on CRUD/FLS checks:

      "SaveResult"
      "Database.insert", "Database.update", "Database.delete"
      "insert " , "update " , "delete "
      "Database.query", “search.query”
      "[select ", “[FIND “

      Check your org-wide default sharing model and make sure that objects using "Public Read/Write" or "Public Read Only" are OK with this base model. If not, consider moving to "Private" and create the necessary sharing rules to give the right level of visibility. If you want to search for this in the metadata use the following keywords to search for sharingModel in object metadata.

      “<sharingModel>ReadWrite</sharingModel>”
      “<sharingModel>Read</sharingModel>”

      Check your user profiles and look for "View All" , "Modify All" permissions. Remember that having these permissions will bypass record-level security checks and cause unexpected behavior. To find these permissions in the metadata files, you can search for:

      “<modifyAllRecords>true</modifyAllRecords>”
      “<viewAllRecords>true</viewAllRecords>”

References


Ouellete, Jason. Development with the Force.com Platform : Building Business Applications in the Cloud. Addison Wesley, 2011. Print.

Salesforce.com. “Sharing Rules Overview” Salesforce Online Help. Nov 2012.

Salesforce.com. “Granting High-Volume Portal Users (Service Cloud Portal Users) Access to Records Salesforce Online Help. Nov 2012.


Thursday, October 6, 2011

Force.com commandments - Part II

Wow, every day we learn something new so It seems like I'll never run out of material for my list of commandments!

Here's the continuation of my previous list, it's important to point out that some of these might not be valid for future releases but people should be careful for now when dealing with some of the current limits. So here we go...

Thou shall not:

  • Assume that standard objects in destination orgs don't have any validation rules/trigger logic that will cause your package's test units to fail, place your catch blocks and take actions accordingly

  • Assume your custom settings will be initialized when running test units in a destination org , make sure you validate null values or set the values for custom settings

  • Give end-users credentials to test your app and go on vacation leaving the user wondering what the heck is a verification code. Make sure you change the f$!@#%^ email address!

  • Leave out apex:pageMessages component from your VF page to display any errors (unless your page is used for other purposes)

  • Forget to add a status indicator in your VF pages when doing async stuff

  • Forget to test and probably fix the behavior of the "enter" key in custom VF pages

  • Expect cascading delete operations to execute trigger evaluations on those records that did not initiated the delete operation

  • Forget to use Test.startTest() and Test.stopTest() methods inside test units

  • Assume that you can run a report on humongous amount of data, make sure you plan your report filters and create external ids (indexes) accordingly

  • Assume you can use a Long Text area field in a formula field or SOQL where clause

  • Assume that Force.com can handle all type of attachments , there's a limit of 5 MB!

  • Leave the endpoint for a WSDL to Apex generated class (or any endpoint value) as a hardcoded value, make sure it can be changed through custom settings

  • Create a VF page just to show an image, use IMAGE in formula fields!

  • Ignore revoked tokens scenarios when using OAUTH

  • Use Contacts without Accounts, these are always private regardless of your organization's sharing model

  • Forget that text names for custom objects have a max length of 80 characters

  • Forget there's a data loader built into salesforce

  • Ignore the use LogLevel for debug statements

  • Create tabs for objects that don't really need a tab

  • Assume auto-numbers will always be sequential, numbers will keep increasing but data creation in test units will break the sequence

  • Forget that once a master relationship has been established it CANNOT be changed

  • Create public utility classes for test data creation without the @isTest annotation (Winter 12)

  • Pretend that no one is using the production org while you're doing your deployments

  • Forget that a profile is tied to a type of license

  • Forget you can actually know the total number of records for specific objects just by going to Setup>Data Management>Storage Usage

  • Create a ton of single static resource for your VF pages, use a damn zip file

  • Assume SF to SF will copy all of your lookup relationships

  • Miss new platform releases webinars or not visit http://blog.sforce.com

  • Fall for jeff douglas blog phantom results in Google... but you'll probably will anyway...


Amen.

Sunday, October 2, 2011

RailsForce - Sample App Template

I've been working a lot with rails and force.com during the last months, rails is awesome however getting all of the plumbing to work is a real pain. I'm working on a template app that might be helpful for anyone that's trying to build a rails app that interacts with force.com.

This app template uses:

Databasedotcom gem
Bootstrap framework
Application template
Basic chatter components (more info below)

I decided to use cells for creating reusable components and it's looking very promising. I created a few chatter-related components and got it to a point were the following code:



Renders the following page:




I'm no expert in ruby or rails so I hope that with the help of other developers we can improve the template and expand the components library.

Also, i'm planning to provide some libraries to access the metadata api from ruby but this is still a work in progress.

You can checkout the github project here.

Wednesday, May 11, 2011

Thoughts on auto-number names

Good stuff is coming for the Summer '11 release of the Force.com platform! My personal favorites: dynamic components and javascript remoting, these 2 features will save a lot of headaches when working with Visualforce and I'm already thinking of several pages that I could optimize by using them.

Anyway, every new release comes with very interesting features but there's still a feature that I'd wish Salesforce could improve and it has to do with auto-number names for records. Why? Well, because these records are hard to find. There are cases where you definitely don't need to setup a special name for records and any identifier will work but I've come across several cases in which users need to select a record that has an auto-number name through a lookup dialog and always end up with complaints about the search functionality for these records. This could be solved by telling users to use a naming convention for the record and avoid auto-number names but it's hard to assure naming conventions will be followed and since the name field is a required field you cannot set it through a trigger or workflow unless you make some workarounds with Visualforce.

These situations usually happen when we have an object in which the record name is hard to define since it may describe several things  and these  usually end up being records of junction objects. For example, let's look at the following situation with the well-know Recruiting app.





We have a junction object called Job Application and we name it JA-{00000}, everything works fine at first but as our application continues to grow we might end up creating an object such as "Interview" that we must link to a "Job Application". If you wanted to create an Interview record directly from the "Interview" tab and link it to a Job App through the lookup dialog, well... you'll need to know the auto-number name for the Job Application or hope it comes up in the recently viewed records, although Salesforce gives us the option to set the columns for the lookup dialogs there's no easy way to search within the list for this type of records.

I think any of the following options could help a lot in this situation:

1.- Search by other fields within lookup dialogs, not just name fields
The good:

Minimum impact and it would involve a small amount of configuration tasks since we would probably just need to set some of the fields as external ids to create indexes so that we can search on these.

The bad:

There might be some cases where we might need to create workflow rules for concatenation purposes.

We won't be able to display more info of the record in the auto-number name.

2.- Give the option for removing the required option from the name field
The good:

Gives us the flexibility to set the name through workflows or apex triggers (for more complex situations).

This could be useful for objects in which you might want to create a name depending on the record type, the name field could be hidden from a specific layout and then filled in the background through workflows or triggers.

The bad:

If no name is defined, we might end up with records having the Id of the record as a name and that is a bad thing.

3.- Define an auto-number as a formula field
The good:

Gives us a lot of flexibility for setting up the record name without using any workflow or apex trigger.

The bad:

Probably hard for the platform since it would need to construct an index based on a formula field and I guess that if this is possible it could hurt in performance.

In my opinion, option 1 is probably the best since it seems like the one that has less impact but I wish the platform could offer the flexibility of option 2. I'd  appreciate any feedback on this from everyone out there, there are already  some ideas related to this subject so please vote for them so that we can see this solved in a future release.

Here's a list of some of the ideas related to auto-number fields:

Saludos!

Wednesday, March 23, 2011

Force.com commandments

I spent a few minutes today thinking about my experiences developing on the Force.com platform. Overall it has been great, nevertheless, there have been some bumps in the road (and there will be more to come).

Here's a list of some commandments that I've come to think of as imperatives for development on Force.com.

NOTE: This is just a list with no particular order of some of what I consider are the most important items.

Thou shall not:

  • Say a class or trigger is ready without having any test units

  • Place a SOQL query inside a for loop

  • Place a DML operation (for a single record) inside a for loop

  • Hardcode an id, object prefix or service instance names... anywhere

  • Create a full sandbox without being damn sure you're ready to create it

  • Say a field is hidden by just removing it from a page layout

  • Assume your test units will magically find data in a new org

  • Assume a 75% coverage is good

  • Ignore asserts in your test units

  • Rely on good percentage coverage of other classes to upload your "I must get this to production right now" code

  • Prefer a trigger over a validation rule (if it can be done through a validation rule)

  • Create a new trigger for an object each time you want to add something new unless you're pretty sure of what you're doing

  • Ignore the undelete event in a trigger

  • Assume a trigger will process a single record

  • Create all of your classes with "without sharing" keyword unless you know what you are doing

  • Leave open queries (without a where clause or limit) unless you know very well your data

  • Ignore the escapeSingleQuoutes function to "clean" user-supplied input in dynamic SOQL

  • Keep list of objects as part of your viewstate ... again ... unless you know what your are doing

  • Create a custom object for saving custom settings

  • Look for a RecordType based on the Name instead of the DeveloperName

  • Ignore the certification maintenance emails

  • Reset a security token without taking in account existing integrations or other people that may be using the token

  • Have a mac and not use SoqlXplorer

  • Use chrome and not use the "Force.com utility belt" and "Force.com Logins" extensions

  • Ask questions like crazy unless you already searched in the discussions boards and found no answer

  • Use accents in profiles or page layouts names (for those that use Spanish)

  • Change the type of a lookup relationship without taking in account any impact on the standard report types and reports that are currently using those report types

  • Suspend a user without thinking about re-assigning ownership of his/her records

  • Ignore declarative changes in production, always keep track of these changes

  • Avoid the use of Security Code Scanner to resolve thy issues -- Cory Cowgill


Amen.

Saturday, January 22, 2011

Best Use Cases for Force.com


These are some important considerations for those who plan to build apps for the Force.com platform, these considerations are organized by Data, Logic and User layers.

In conclusion, the ideal Force.com profile is one that works with:


  • Structured data, search, reports

  • User-driven business processes

  • Employee,customer or partners users

  • Real-time information, mobile or collaboration needs


Sunday, August 1, 2010