# Update Function Permissions

Salesforce permission sets are used to control which org data Salesforce Functions can access. When you deploy your function, make sure your function is associated with a permission set that has the correct data permissions.

All deployed functions invoked by an org use the Functions permission set in that org when making data requests. This permission set is automatically created when you connect your Dev Hub org with Salesforce Functions. Update the permission set to set data permissions specific to your function.

:::note
Instead of assigning all permissions for all Salesforce Functions to the Functions permission set, you can [use session-based permission sets](/docs/platform/functions/guide/permissions.md#use-session-based-permission-sets) to scope permissions to a specific function.
:::

To modify the data permissions for your function, update the Functions permission set in the org that invokes the function. You can update the permission set using the Salesforce UI, or add a `Functions.permissionset-meta.xml` to your DX project’s `force-app/main/default/permissionsets` folder, with the desired data permissions. For example, give your function read and create access to the Account object in your org with the following `Functions.permissionset-meta.xml`:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<PermissionSet xmlns="http://soap.sforce.com/2006/04/metadata">
    <hasActivationRequired>true</hasActivationRequired>
    <label>Functions</label>
    <objectPermissions>
        <allowCreate>true</allowCreate>
        <allowDelete>false</allowDelete>
        <allowEdit>true</allowEdit>
        <allowRead>true</allowRead>
        <modifyAllRecords>false</modifyAllRecords>
        <object>Account</object>
        <viewAllRecords>false</viewAllRecords>
    </objectPermissions>
</PermissionSet>
```

After you update the Functions permission set in your DX project, deploy your changes to your org. When using scratch orgs, use the `sf project deploy start` command to deploy permission set changes (and other project-side changes) to your org. If you're collaborating with other developers and sharing an org and compute environment, make sure to merge your permissions changes with any changes your team has already pushed to the shared org. Retrieve changes to your project with `sf project retrieve start` first if necessary. For details on syncing project and scratch org changes, see [Retrieve Source from the Scratch Org to Your Project](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_pull_md_from_scratch_org.htm).

:::note
If you're deploying your functions to a sandbox org, merge and push your permission changes using the same `sf` commands: `sf project deploy start` and `sf project retrieve start`. For more information on syncing project and sandbox org changes, see [Develop Functions Using Sandbox Orgs](/docs/platform/functions/guide/sandbox-orgs.md).
:::

For more information on Salesforce permission sets, see [Permission Sets Overview](https://help.salesforce.com/articleView?id=perm_sets_overview.htm\&type=5).

## Use Permission Sets and Local Invocation

When invoking a locally running function against a scratch org using `sf run function start`, the function uses the permissions of the current user, rather than the Functions permission set. The default scratch org user has System Admin privileges, but functions are called by the `Cloud Integration User` profile. Run your functions locally with a dedicated scratch org user with the minimum access to better test for real-world conditions.

Create a scratch org user using the `Minimum Access - Salesforce` profile. Create a permission set that provides the same object access permissions that your Functions permission set has, and apply this permission set to the new user. For more information on creating scratch org users, see [Scratch Org Users](https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_scratch_orgs_users.htm).

## Use Apex Invocation and Permissions

From a permissions perspective, Apex code that invokes Salesforce Functions is no different than other Apex code. The code runs in system mode, as described in [Understanding Describe Information Permissions](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_dynamic_permissions.htm). Functions invoked asynchronously with Apex work similarly. After the function executes, the Apex callback runs in system mode as the user that invoked the function.

Apex invocation can only invoke functions that have been deployed to a compute environment that is associated with the org the Apex code is running in. Apex code that invokes functions, like any Apex code, is considered "trusted code" in a given org, deployed to that org by someone that understands the security and permission requirements of that org.

Salesforce provides additional layers of permissions and security around who can execute Apex code, such as Author Apex permissions and Apex class security. Apply class security to your Apex code that invokes Salesforce Functions so only certain users can execute those classes. See [Salesforce Help: How Does Apex Class Security Work?](https://help.salesforce.com/s/articleView?id=platform.code_package_security.htm\&type=5) for more information on Apex class security.

## Set Deployed Function Permissions

When a deployed function is invoked, the function runs as the `Cloud Integration User` profile when making requests back to the org, not as the org user that invoked the function. Any data access permissions on the invoking user must work for the Cloud Integration User as well.

Data requests are checked against the Functions permission set. Set the object permissions in the Functions permission set to match the object permissions for the users that invoke the function. For example, add **ViewAllRecords** and **ModifyAllRecords** permissions if the function must view or modify objects.

:::note
If you encounter an error trying to access certain objects, check the license for the `Cloud Integration User` profile to verify that the user who runs functions can access the objects needed. Even when you define the access in the Functions permissions metadata file (Functions.permissionset-meta.xml), the license must also allow access to the objects.

To insert, update, or delete records of an object from a function, return record data in the function response and perform the DML operation in the function's Apex callback. This approach runs Apex as the invoking user in system mode.
:::

See [Give Users Access to Data](https://developer.salesforce.com/docs/atlas.en-us.securityImplGuide.meta/securityImplGuide/security_feature_setup.htm) for more details on setting data access permissions in your org.

## Use Session-Based Permission Sets

Users with the Functions permission set assigned can run any function in their org. You can also create individual permission sets for specific functions. These permission sets can be activated directly from the function code while the function executes with a [session-based permission set](https://help.salesforce.com/s/articleView?id=platform.perm_sets_session_use.htm\&type=5).

### Update Permissions for Functions Permission Set

To create and use a session-based permission set, first update the existing Functions permission set so it can manage the individual function permission sets.

1. From Setup, in the Quick Find box, enter `Permission Sets`, and then select **Permission Sets**.
2. Click the Functions permission set and go to **System Permissions**.
3. Click **Edit** and select **Manage Session Permission Set Activations**.
4. Click **Save**.

### Create a New Session-Based Permission Set

Create a permission set to provide permissions when needed, like during invocation.

1. From Setup, in the Quick Find box, enter `Permission Sets`, and then select **Permission Sets**.
2. Click **New** and give the permission set a name specific to its associated function.
3. Select **Session Activation Required**.
4. Add any permissions to this permission set your function needs for execution, like **Accounts** in **Object Settings** if your function needs access to Account objects.
5. Click **Save**.

### Assign a Session-Based Permission Set to the Cloud Integration User

The `Cloud Integration User` profile is what your Salesforce Functions use to make API calls on behalf of the function. You can't assign the Cloud Integration User to a permission set in the Salesforce UI, so this step must be done on the command line.

Make sure you're logged in to your org via the Salesforce CLI and use SOQL to query the User object to find the username for the Cloud Integration User profile.

```sh
sf data query --query "SELECT Id, UserName from User WHERE username like 'cloud@00d%' LIMIT 10"
ID   USERNAME
---------------------
005RO000000HiuWYAS cloud@00dro0000008boa2ay
Total number of records retrieved: 1.
Querying Data... done
```

Copy the username from the output (the one with `cloud@` at the beginning) and use it to assign the session-based permission set to the Cloud Integration User.

```sh
sf org assign permset --name SessionPermFunction --on-behalf-of cloud@00dro0000008boa2ay
=== Permsets Assigned

 Username                 Permission Set Assignment
 ──────────────────────── ─────────────────────────
cloud@00dro0000008boa2ay  SessionPermFunctions
```

### Activate and Deactivate a Session-Based Permission Set in Function Code

To assign a session-based permission set to the `Cloud Integration User` profile, you have to use the Salesforce REST API to make a request to activate it. Add this request in your function code.

#### Activate Session-Based Permission Set

In Node.js with JavaScript:

```js
const { accessToken, baseUrl, apiVersion } = context.org.dataApi;
const authHeaders = {
Authorization: Bearer ${accessToken}
};
const apiUrl = ${baseUrl}/services/data/v${apiVersion};
const { statusCode: statusCodeJob, body: bodyJob } = await request(
${apiUrl}/actions/standard/activateSessionPermSet,
{
    method: "POST",
    headers: {
    "Content-Type": "application/json",
    ...authHeaders
    },
    body: JSON.stringify({
    inputs: [
        {
        PermSetName: "SessionPermFunctions"
        }
    ]
    })
}
);
const createJobResponse = await bodyJob.json();
if (statusCodeJob !== 200) {
logger.error(JSON.stringify(createJobResponse));
throw new Error(Session Permset Activation failed);
}
logger.info(
* Permset Activation *** :  + JSON.stringify(createJobResponse)
);
```

#### Deactivate Session-Based Permission Set

Add the deactivation code to your function to make sure the session-based permission set isn't active for the next invocation.

In Node.js with JavaScript:

```js
const { statusCode: statusCodeJob2, body: bodyJob2 } = await request(
  `${apiUrl}/actions/standard/deactivateSessionPermSet`,
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      ...authHeaders,
    },
    body: JSON.stringify({
      inputs: [
        {
          PermSetName: "SessionPermFunctions",
        },
      ],
    }),
  },
);
const createJobResponse2 = await bodyJob2.json();
if (statusCodeJob2 !== 200) {
  logger.error(JSON.stringify(createJobResponse2));
  throw new Error(`Session Permset DeActivation failed`);
}
logger.info(`******* Permset Deactivation ******** : ` + JSON.stringify(createJobResponse2));
```

In Java:

```java
public class MyFunctonResponseHandler implements functions.FunctionCallback {
    public void handleResult(FunctionInvocationStatus status) {
        // Check status.getStatus() for error handling
        // ...
        // Activate the Function Permission Set
        HttpRequest req = new HttpRequest();
        req.setEndpoint(req.setEndpoint(Url.getOrgDomainUrl().toExternalForm()+
            '/services/data/v49.0/actions/standard/activateSessionPermSet');
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/json');
        req.setBody('{ "inputs" : [ { "PermSetName" : "HelloWorldPermissions"} ] }');
        HTTPResponse res = new Http().send(req);
        // All good store the response (client in this case using CDC to monitor)
        insert new Widget__c(
            SomeCalculatedInfo__c = status.getResponse(),
            CalcContext__c = status.getInnvocationId()));
    }
}
```
