Showing posts with label Sharing. Show all posts
Showing posts with label Sharing. Show all posts

Sunday, December 27, 2020

Access Grant, Share and Maintenance Tables

Record Access Calculation

As there are many options for managing record level access, and as some of these options are affected by organizational dependencies, determining which records users can access can quickly become complicated. Rather than checking record access in real time, Salesforce calculates record access data only when configuration changes occur. The calculated results persist in a way that facilitates rapid scanning and minimizes the number of database table joins necessary to determine record access at runtime.

Access Grants

When an object has its OWD set to Private or Public Read Only, which is not the least restrictive option, Salesforce uses access grants to define how much access a user or group has to that object’s records. Each access grant gives a specific user or group access to a specific record, It also records the type of sharing tool: sharing rule, team, etc.

Salesforce uses 4 types of access grants:

Explicit grants:

Grants that occur when records are shared directly to users or groups. Examples:

  • A user or a queue becomes the owner of a record, as in Salesforce each record should have an owner, and whenever a record is owned by a user or a queue, an explicit share is used to grant access to the record for the owner or queue.
  • A sharing rule shares the record to a personal or public group, a queue, or a role.
  • An assignment rule shares the record to a user or a queue.
  • A territory assignment rule shares the record to a territory.
  • A user manually shares the record to a user, a personal or public group, a queue, or a role.
  • A user becomes a part of a team for an account, opportunity, or case.
  • A programmatic customization shares the record to a user, a group, or queue, or a role.
Group membership grants:

Grants that occur when a user, group, queue, role, or territory is a member of a group that has explicit access to the record. So as you know by now, an explicit share is used to grant access to group via sharing rules, manual sharing or Apex sharing, because of which all members of a group are granted access using the group membership access grant. For example, if a sharing rule grants the “Sales group” access to the Acme account record, and Bob is a member of the “Sales group”, Bob’s membership in the “Sales group” grants him access to the Acme account record.

Inherited grants:

Grants that occur when a user, group, queue, role, or territory inherits access through a role or territory hierarchy or is a member of a group that inherits access through a group hierarchy. For example, if a user Alice has a role that is higher in the role hierarchy than the role of another user Bob, then Alice can access the same records that Bob can access.

Implicit grants:

Grants that occur when non-configurable record-sharing behaviours built into Salesforce Sales, Service and Portal applications grant access to certain parent and child records. Examples:

  • Read-only access to the parent account for a user with access to a child record.
  • Access to child records for the owner of the parent account record.
  • Access to a community account and all associated contacts for all community users under than account
  • Access to data owned by community users associated with a sharing set for users member of the sharing set’s access group.

Object Share tables store the data that supports explicit and implicit grants.

SELECT Id, ParentId, UserOrGroup.Name, AccessLevel, RowCause FROM AccountShare

Group maintenance tables store the data that supports group membership and inherited access grants.

SELECT Id, Name, DeveloperName, Type, OwnerId FROM Group
Share This:    Facebook Twitter

Monday, December 7, 2020

Secure Salesforce Apex Programming

How permissions and sharing work on Salesforce

The Salesforce platform has two main ways of controlling access to records—permissions and sharing. Permissions in Salesforce focus on what you can do with a particular object in general. Sharing focuses on what records you can see for that object based on their ownership.

With Salesforce's permissions and sharing tools, you can build up a very granular set of visibility permissions that control exactly who can access the different records within your org. When you attempt to retrieve a record in Salesforce, these permissions are checked before any further processing occurs. Following this, sharing calculations are then run to verify whether you have access to the record or records that you are retrieving.

Sharing and performance

As an application grows on the platform, the volume of data stored on it will also inevitably grow. The obvious side effect of this is that querying for records and loading lists of data can become much slower. To help keep your application performant, you should reduce the amount of data every user can see to that which is strictly necessary. This will ensure that queries, list views, reports, and all manner of functionalities continue to run in an optimal way and don't slow down the user experience.

Enforcing sharing

By default, all Apex operations (and Process Builder and certain Flows) run in System Mode; that is, they execute as a generic system user that has access to all metadata and data within the org. Within our Apex configuration, sharing is enforced through the use of the with sharing keywords in our class definition. Declaring either with sharing or without sharing explicitly is a deliberate action for us to verify that we either do or do not want the sharing rules for the current user to be enforced. If we do not define a class as with sharing or without sharing explicitly (ClassC), then the current sharing rules remain in force.

public with sharing class ClassA {
	public static List<Account> getAccounts() {
		return ClassC.getAccounts();
	}
}

public without sharing class ClassB {
	public static List<Account> getAccounts() {
		return ClassC.getAccounts();
	}
}

public class ClassC {
	public static List<Account> getAccounts() {
		return [SELECT Name from Account];
	}
}

If ClassC was the entry point to our transaction, it would operate in without sharing mode by default.

In situations where we want to default to a with sharing context, but enable the code to run in a without sharing context when called from a class defined as without sharing, we can utilize the inherited sharing option as our default. Apex without a sharing declaration is insecure by default. An explicit inherited sharing declaration makes the intent clear, avoiding ambiguity arising from an omitted declaration or false positives from security analysis tooling.

So whenever we are defining our Apex classes, we should apply the following rules to ensure our sharing is actually enforced as we anticipate it to be:

  • Use with sharing when we know we want the sharing model to be enforced.
  • Use without sharing when we know we want the sharing model to be ignored.
  • Otherwise, use inherited sharing as a default.

Sharing records using Apex

Salesforce has several ways of sharing records with users and groups of users such as managed sharing, user-managed (or manual) sharing, and Apex managed sharing:

  • Managed sharing is the point-and-click sharing that most Salesforce developers and administrators are familiar with, and relies upon record ownership, the role hierarchy in the org, and any sharing rules.
  • User-managed sharing or manual sharing is when a user chooses to share a record with a user or group of users using the Share button.
  • Apex managed sharing is the sharing of records with a user or group of users through the use of Apex code.

All three of the methods described store records in the share object associated with the record within the Salesforce database. For every object, there is a corresponding share object. For standard objects, it is the object API name plus share, so AccountShare, ContactShare, and so on. For custom objects, __c in the object API name is replaced by __Share. Sharing via org-wide defaults, the role hierarchy, and permissions such as View All are not stored in these objects.

So as an example, to create an AccountShare record, we require to set the following information:

  • The ID of the record to be shared with the ParentId field.
  • The user of group to be shared within the UserOrGroupId field.
  • An access level, either Edit or Read, in the AccessLevel field.
  • A reason for sharing in the RowCause field. The default value is Manual, as we have set here, however custom reasons can be set by adding them through the setup menu.
AccountShare newShare = new AccountShare();
newShare.ParentId = accId;
newShare.UserOrGroupId = userId;
newShare.AccessLevel = 'Read';
newShare.RowCause = Schema.AccountShare.RowCause.Manual;
accShares.add(newShare);

Enforcing object and field permissions

We have two ways of enforcing our object-level and field-level permissions in Apex code, the first of which is to utilize the describe methods within Apex to verify that the user had the correct permissions. The methods to verify the permissions for an sObject are as follows and are utilized on the Schema.DescribeSObjectResult instance for the given sObject:

  • isAccessible
  • isCreateable
  • isUpdateable
  • isDeletable

For example, we can verify permissions on a Contact object as follows:

if(Schema.sObjectType.Contact.isAccessible()) {
   // Read Contact records
}

if (Schema.sObjectType.Contact.isCreateable()) {
   // Create Contact records
}

if (Schema.sObjectType.Contact.isUpdateable()) {
   // Create Contact records
}

if (Schema.sObjectType.Contact.isDeletable()) {
   // Create Contact records
}

Similarly, at the field level, on the Schema.DescribeFieldResult instance for a field, we have the following methods:

  • isAccessible
  • isCreateable
  • isUpdateable

All of these are available to us to verify that we have the correct permissions to manipulate a field:

if(Schema.sObjectType.Contact.fields.Email.isAccessible()) {
   // Read Contact record Email
}

if (Schema.sObjectType.Contact.fields.Email.isCreateable()) {
   // Populate Contact record Email
}

if (Schema.sObjectType.Contact.fields.Email.isUpdateable()) {
   // Edit Contact record Email
}

We can also enforce field and object permissions using the Security.stripInaccessible method, which takes two parameters. The first is an access level that we wish to verify against, and the second is a List<sObject>. The method then removes any fields that the user does not have the stated access level for. It is also particularly useful for sanitizing records as a whole, such as when we are providing an API and receiving sObject data from external users. Read more about the stripInaccessible method here.

In the following code, we are using the stripInaccessible method to remove any fields that the user does not have update permissions on:

String jsonBody = '[{"FirstName":"Alice", "LastName":"Jones", "Email": "ajones@test.com"}]';

List<Contact> contacts = (List<Contact>)JSON.deserializeStrict(jsonBody, List<Contact>.class);

SObjectAccessDecision accessDecision = Security.stripInaccessible(AccessType.UPDATABLE, contacts);
update accessDecision.getRecords();

If you still want to throw an exception if the user has no access to any one of the fields using <code>stripInaccessible</code> method, then you can use the below Utils method:

public static List<SObject> checkFieldLevelSecurity(List<SObject> sobjects, AccessType accessCheckType) {
    SObjectAccessDecision decision = Security.stripInaccessible(accessCheckType, sobjects);
    System.debug(LoggingLevel.INFO, decision);
    if (decision.getRemovedFields().size() > 0) {
        throw new QueryException('User cannot read these fields: ' + decision.getRemovedFields());
    }
    return decision.getRecords();
}

And to use checkFieldLevelSecurity method, pass the list of sobjects and the AccessType.

(List<Task>) Utils.checkFieldLevelSecurity(sobjects, AccessType.READABLE);

Enforcing permissions and security within SOQL

Salesforce added the WITH SECURITY_ENFORCED clause to the SOQL language. Unlike the stripInaccessible method, if the user is lacking permissions for a field, an exception is thrown rather than the field simply being removed.

To apply this clause, we simply include WITH SECURITY_ENFORCED after any WHERE clause and before any ORDER BY, LIMIT, OFFSET, or aggregate function clauses. For example, consider the following:

List<Contact> contacts = [SELECT FirstName, LastName, Secret_Field__c FROM Contact WITH SECURITY_ENFORCED];

In the preceding query, if the user has no access to Secret_Field__c, then an exception will be thrown, indicating insufficient permissions.

Avoiding SOQL injection vulnerabilities

The first and most simple is to ensure we are using Apex binding variables and static queries. By default, Apex binding variables are automatically escaped and so will ensure that the query would run as expected.

public String searchName {get; set;}

public PageReference search() {
   return [SELECT Id, LastName, Email FROM Contact WHERE LastName Like :searchName];
}

There are instances where we must use a dynamic query. In these instances, we should ensure that we escape any input from the end user using the escapeSingleQuotes method:

public String searchName {get; set;}

public PageReference search() {
   return Database.query('SELECT Id, LastName, Email FROM Contact WHERE LastName Like \'%' + String.escapeSingleQuotes(searchName) + '%\'');
}
Share This:    Facebook Twitter

Monday, August 14, 2017

Salesforce Security: Concepts

Salesforce Security Layers

  1. Organization: Login IP ranges, Login hours; when where how can a user login to the org
  2. Object: Object-level security; what action can a user have on a specific object
  3. Field: Field-level security; what field can a user read/edit
  4. Record: Sharing; what records can a user access

From an architect’s perspective, data access in Salesforce falls into 2 main categories:

  • Object-level access, which includes field-level access
  • Record-level access

Profiles and Permission Sets determine the object level security access to each object in Salesforce.

Read object-level security doesn't give Read access to all account records, it only gives the ability to read account records.

Profiles

A profile contains the baseline permissions of a user, and each user is assigned only 1 profile. It defines a user’s ability to peform different functions, access records, and the way the records are displayed. Profiles are tied to a user license.

Enhanced profile user interface lets you search for any setting and it can take you to this setting page. This can be set up in Setup —> User Management Settings.

Permission Set

A permission set is a collection of permissions that can be assigned to a user extending users’ functional access without changing their profiles. Users can have only one profile but they can have multiple permission sets.

Permission Set Group (PSG): It is a collection of permission sets. Users with a PSG receive the combined permissions of all the permission sets in the group. You can also remove individual permissions from a group with the muting feature, to further customize the group. Please note that a PSG can only contain 1 muting permission set that is created within the group.

A profile doesn’t provide the appropriate mechanism for scalable permission assignment. Permission sets and PSG are used to avoid the creation of many different profiles that are only slightly different.

List of permissions that are missing in a permission set

  • Page layout assignment
  • Login hours and Login IP ranges
  • Desktop client access
  • Session settings
  • Password policies
  • Delegated Authentication
  • 2-factor authentication with SSO
  • Organization-wide email addresses are assignable by profiles only
  • Default record type per object
  • Profile specific search layouts

Record Ownership

Every record must be owned by a single user or queue. The owner has access to the record, based on the object settings of the owner’s profile. Queue members and users higher in a role hierarchy can access queues from list views and take ownership of records in a queue.

Record ownership and full access are synonymous and interchangeable and provide the user with the highest level of access to a record.

Sharing (record-level access)

Record-level access determines which records a user can see for a specific object. Object-level security should allow to access this record.

  • Organization-Wide Defaults
  • Role Hierarchy
  • Sharing Rules
  • Manual Sharing
  • Team Sharing
  • Territory Hierarchy Access

For each object, the View All and Modify All permissions ignore sharing rules and settings.

View All Data and Modify All Data administrative permissions apply to all objects’ records.

Organization-wide Defaults (OWD)

OWD sharing settings specify the default level of access users have to each others’ records. OWD are the only way to restrict record access. You use OWD settings to lock down your data to the most restrictive level, and then use other sharing tools to selectively give access to other users. For each object, the OWD can be:

  • Private
  • Public read only
  • Public read/write
  • Public read/write/transfer: available for cases and leads
  • Public full access: available for campaigns
  • Controlled by parent

Role Hierarchy

Users can access the data of all the users directly below them in the hierarchy (subordinates) using the Grant Access Using Hierachies option. Managers gain as much access as their subordinates. This access applies to records owned by users, as well as records shared with them.

Public Group

A public group is a collection of individual users, roles, territories, and so on, that all have a function in common.

Groups can be nested (group A nested into group B), but keep in mind that nesting has an impact on group maintenance and performance due to group membership calculation. Don’t nest more than 5 levels. As a best practice, keep the total number of public groups for an organization to 100,000.

Sharing Rules

They allow for exceptions to OWD settings and the role hierarchy that give additional users access to records they don’t own. There are 2 types of sharing rules:

  1. Ownership-based: records are shared following criteria based on their owner. For example, share all opportunities owned by user A with Group B. As a best practice, keep the number of ownership-based rules per object to 1000.
  2. Criteria-based: they provide access to records based on the record’s field values.

Manual Sharing

Gives user the flexibility to share specific records with users that need to see them. To grant access to a record, you must be one of the following users:

  • The record owner
  • A user in a role above in the hierarchy
  • Any user granted Full Access to the record
  • An administrator

Team Sharing

For accounts, opportunities and cases, record owners can use teams to allow other users access to their records. The record owner adds team members and specifies the level of access each team member has to the record, so that some team members can have read-only access and others can have read-write access. The record owner can also specify a role for each team member.

Share This:    Facebook Twitter

Total Pageviews

My Social Profiles

View Sonal's profile on LinkedIn

Tags

__proto__ $Browser Access Grants Accessor properties Admin Ajax AllowsCallouts Apex Apex Map Apex Sharing AssignmentRuleHeader AsyncApexJob Asynchronous Auth Provider AWS Callbacks Connected app constructor Cookie CPU Time CSP Trusted Sites CSS Custom settings CustomLabels Data properties Database.Batchable Database.BatchableContext Database.query Describe Result Destructuring Dynamic Apex Dynamic SOQL Einstein Analytics enqueueJob Enterprise Territory Management Enumeration escapeSingleQuotes featured Flows geolocation getGlobalDescribe getOrgDefaults() getPicklistValues getRecordTypeId() getRecordTypeInfosByName() getURLParameters Google Maps Governor Limits hasOwnProperty() Heap Heap Size IIFE Immediately Invoked Function Expression Interview questions isCustom() Javascript Javascript Array jsForce Lightning Lightning Components Lightning Events lightning-record-edit-form lightning:combobox lightning:icon lightning:input lightning:select LockerService Lookup LWC Manual Sharing Map Modal Module Pattern Named Credentials NodeJS OAuth Object.freeze() Object.keys() Object.preventExtensions() Object.seal() Organization Wide Defaults Override PDF Reader Performance performance.now() Permission Sets Picklist Platform events Popup Postman Primitive Types Profiles Promise propertyIsEnumerable() prototype Query Selectivity Queueable Record types Reference Types Regex Regular Expressions Relationships Rest API Rest Operator Revealing Module Pattern Role Hierarchy Salesforce Salesforce Security Schema.DescribeFieldResult Schema.DescribeSObjectResult Schema.PicklistEntry Schema.SObjectField Schema.SObjectType Security Service Components Shadow DOM Sharing Sharing Rules Singleton Slots SOAP API SOAP Web Services SOQL SOQL injection Spread Operator Star Rating stripInaccessible svg svgIcon Synchronous this Token Triggers uiObjectInfoApi Upload Files VSCode Web Services XHR
Scroll To Top