【フロー】オブジェクト権限を取得する

Salesforceのフローにおいて、オブジェクト権限を取得する仕組みを考えてみます。

## 考える具体例
任意のオブジェクトを識別する入力値によって、実行ユーザが対応するオブジェクトに対する権限を持っているかを判定する自動起動フローを構築します。

:::note info
自動起動フローにしておくことで、外部のフローからいつでも呼び出すことができるようになります。
:::

## ユーザが保有するオブジェクト権限の情報はどのように保存されているか
オブジェクト権限は __ObjectPermissions__ というオブジェクトに保存されています。
このオブジェクトには組織全てのユーザの様々な設定で与えられたオブジェクトに対する権限が保存されているため、 __「どの設定で与えられたか？」__ を把握するためには、

- __PermissionSet__ ： 権限セットの定義情報を保存している

というオブジェクトを介して保存情報にアクセスする必要があります。

:::note info
プロファイルと権限セットグループに関しては、内部的に権限セットが作成されます。
:::

ユーザに対して割り当てられている権限セットを特定するためには、

- __PermissionSetAssignment__ ： 権限セットグループと権限セットのユーザへの割り当てを保存している

というオブジェクトを介して判定を行う必要があります。

## 実装方法
#### フローの種類を選択する
フローの種類は __「自動起動フロー（トリガーなし）」__ を選択します。

![Flow_Get_ObjectPermissionInfo_フロー種別の選択.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/7066a8c2-3be3-23d8-2b61-86882d4f8e7b.png)

#### 入力変数を定義する
入力変数として下記を準備します。

- __SObjectApiName__ ： オブジェクトのAPI参照名を入力するテキスト型の入力で使用可能な変数

![Flow_Get_ObjectPermissionInfo_入力変数_SObjectApiName.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/494d60af-c8d8-da52-a6ec-4a41e845139d.png)

#### 出力変数を定義する
出力変数として下記を準備します。

- __Create__ ： オブジェクトの作成権限を出力するBoolean型の出力で使用可能な変数
- __Read__ ： オブジェクトの参照権限を出力するBoolean型の出力で使用可能な変数
- __Edit__ ： オブジェクトの編集権限を出力するBoolean型の出力で使用可能な変数
- __Delete__ ： オブジェクトの削除権限を出力するBoolean型の出力で使用可能な変数
- __ViewAll__ ： オブジェクトのすべて参照権限を出力するBoolean型の出力で使用可能な変数
- __ModifyAll__ ： オブジェクトのすべて更新権限を出力するBoolean型の出力で使用可能な変数

![Flow_Get_ObjectPermissionInfo_出力変数_Create.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/30bd4a8f-0ebd-4400-9242-f75c036596ec.png)
![Flow_Get_ObjectPermissionInfo_出力変数_Read.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/d62689e8-771c-e188-9c9a-e2eb35cbca63.png)
![Flow_Get_ObjectPermissionInfo_出力変数_Edit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/201d3520-997a-5320-8645-c253daf06831.png)
![Flow_Get_ObjectPermissionInfo_出力変数_Delete.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/db89262d-532e-d05c-0c6c-00c8caa9448b.png)
![Flow_Get_ObjectPermissionInfo_出力変数_ViewAll.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/d91a1a52-8e7b-e9ee-ac2b-6e44fd9edf1f.png)
![Flow_Get_ObjectPermissionInfo_出力変数_ModifyAll.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/2a0c8c47-7238-4065-f1e5-6b77182c172a.png)

#### 内部変数を定義する
内部変数として下記を準備します。

- __AssignedPermissionSetIds__ ： 割り当てられた権限セットのIdを保存するテキスト型のコレクション変数

![Flow_Get_ObjectPermissionInfo_内部変数_AssignedPermissionSetIds.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/28fc771e-6faf-f279-6f00-aab1aeb11331.png)

#### 権限セットの割り当てを取得する
レコードの取得要素を使用して、実行ユーザに対する権限セットの割り当て（PermissionSetAssignment）を取得します。
ここでは下記のような設定とします。

- API参照名 ： Get_PermissionSetAssignment
- オブジェクト ： 権限セットの割り当て
- 絞り込み条件 ： すべての条件に一致（AND）
  - AssigneeId 次の文字列と一致する {!$User.Id}
- 保存するレコード数 ： すべてのレコード

![Flow_Get_ObjectPermissionInfo_レコードの取得_Get_PermissionSetAssignment.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/ca9adce3-55d1-2ca3-f14c-6436b6e325ec.png)

#### 権限セットの割り当てをループする
取得した権限セットの割り当て（PermissionSetAssignment）をループします。
ここでは下記のような設定とします。

- API参照名 ： Loop_PermissionSetAssignment
- コレクション変数 ： {!Get_PermissionSetAssignment}

![Flow_Get_ObjectPermissionInfo_ループ_Loop_PermissionSetAssignment.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/cdfc2e4a-f69d-187e-6a9e-41678411f3f6.png)

#### 権限セットIDを追加する
ループの中の権限セットの割り当て（PermissionSetAssignment）から変数に権限セットIDを追加します。
ここでは下記のような設定とします。

- API参照名 ： Assign_AddPermissionSetId
- 変数値を設定　：
  - {!AssignedPermissionSetIds} 追加 {!Loop_PermissionSetAssignment.PermissionSetId}

![Flow_Get_ObjectPermissionInfo_割り当て_Assign_AddPermissionSetId.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/7b2d2e79-fba1-1257-c28e-0f23384eff19.png)

#### オブジェクト権限を取得する
レコードの取得要素を使用して、実行ユーザに対して割り当てられた権限セットを介してオブジェクト権限（ObjectPermissions）を取得します。
ここでは下記のような設定とします。

- API参照名 ： Get_ObjectPermissions
- オブジェクト ： オブジェクト権限
- 絞り込み条件 ： すべての条件に一致（AND）
  - ParentId 次に含まれる {!AssignedPermissionSetIds}
  - SobjectType 次の文字列と一致する {!SObjectApiName}
- 保存するレコード数 ： すべてのレコード

![Flow_Get_ObjectPermissionInfo_レコードの取得_Get_ObjectPermissions.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/56e686a8-aec8-7d07-09b2-6cd594669f4b.png)

#### オブジェクト権限をループする
取得したオブジェクト権限（ObjectPermissions）をループします。
ここでは下記のような設定とします。

- API参照名 ： Loop_ObjectPermissions
- コレクション変数 ： {!Get_ObjectPermissions}

![Flow_Get_ObjectPermissionInfo_ループ_Loop_ObjectPermissions.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/9bcdc38e-f5d4-8c77-f099-a1a627861966.png)

#### 数式リソースを作成する
フローのアルゴリズムを簡素にするために、下記の数式リソースを作成します。

- __Formula_Create__ ： オブジェクトの作成権限を判定するBoolean型の数式リソース

```
OR( {!Create} , {!Loop_ObjectPermissions.PermissionsCreate} )
```

- __Formula_Read__ ： オブジェクトの参照権限を判定するBoolean型の数式リソース

```
OR( {!Read} , {!Loop_ObjectPermissions.PermissionsRead} )
```

- __Formula_Edit__ ： オブジェクトの編集権限を判定するBoolean型の数式リソース

```
OR( {!Edit} , {!Loop_ObjectPermissions.PermissionsEdit} )
```

- __Formula_Delete__ ： オブジェクトの削除権限を判定するBoolean型の数式リソース

```
OR( {!Delete} , {!Loop_ObjectPermissions.PermissionsDelete} )
```

- __Formula_ViewAll__ ： オブジェクトのすべて参照権限を判定するBoolean型の数式リソース

```
OR( {!ViewAll} , {!Loop_ObjectPermissions.PermissionsViewAllRecords} )
```

- __Formula_ModifyAll__ ： オブジェクトのすべて更新権限を判定するBoolean型の数式リソース

```
OR( {!ModifyAll} , {!Loop_ObjectPermissions.PermissionsModifyAllRecords} )
```

![Flow_Get_ObjectPermissionInfo_数式リソース_Formula_Create.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/aaa21999-bdce-f61d-3098-07fe5a2ad1e9.png)
![Flow_Get_ObjectPermissionInfo_数式リソース_Formula_Read.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/6a60fa82-3e06-bb53-592e-441f96f42bf1.png)
![Flow_Get_ObjectPermissionInfo_数式リソース_Formula_Edit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/2973267e-7cf9-d9da-6c9c-9d6d970d9084.png)
![Flow_Get_ObjectPermissionInfo_数式リソース_Formula_Delete.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/c7b1893c-1996-7a66-d37b-3236ac865548.png)
![Flow_Get_ObjectPermissionInfo_数式リソース_Formula_ViewAll.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/60b25615-5151-10fc-7bb1-77c8aaa60ef6.png)
![Flow_Get_ObjectPermissionInfo_数式リソース_Formula_ModifyAll.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/1b2d3e5a-d8e2-1943-502b-b532540491ed.png)

#### オブジェクト権限を割り当てる
ループの中で前述の数式から出力変数のオブジェクト権限を設定します。
ここでは下記のような設定とします。

- API参照名 ： Assign_ObjectPermissions
- 変数値を設定　：
  - {!Create} 次の文字列と一致する {!Formula_Create}
  - {!Read} 次の文字列と一致する {!Formula_Read}
  - {!Edit} 次の文字列と一致する {!Formula_Edit}
  - {!Delete} 次の文字列と一致する {!Formula_Delete}
  - {!ViewAll} 次の文字列と一致する {!Formula_ViewAll}
  - {!ModifyAll} 次の文字列と一致する {!Formula_ModifyAll}

![Flow_Get_ObjectPermissionInfo_割り当て_Assign_ObjectPermissions.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/0bd58c2c-2538-cd94-51df-6c0c7e71dd2f.png)

#### フローの完成
完成したフローの形は下記のような形になります。

![Flow_Get_ObjectPermissionInfo_フロー全体像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/3b7a3520-d181-4d41-00a0-231ff3d15b4c.png)

#### フローの保存
フローを保存する際には、設定情報にアクセスするためにフローの実行方法を __「システムコンテキスト共有なし ー すべてのデータにアクセス」__ とする必要があります。

![Flow_Get_ObjectPermissionInfo_フロー保存.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3285411/5fcfef5d-44a6-62b2-2c2b-3375c4a12d32.png)

## まとめ
権限関係の動作として、

- プロファイルは内部的に権限セットを作成している
- 権限セットグループは含まれている権限セットおよびミュート権限セットを集約した権限セットを内部的に作成している
- ユーザの権限は割り当てられている権限セット（上記含む）によって決定される

となっている点は、権限周りを深掘りしなければ知らないことかと思います。

フローに関しては基本的にオブジェクト権限は適用されないため、オブジェクト権限のないユーザでもレコードに対する操作を行えます。
この動作が良いこともありますが、セキュリティとしてはリスクとなります。
本記事のフローをオブジェクトに対する操作の前に呼び出すことで、オブジェクト権限を簡単にチェックすることができる点は良いことかと思います。

## おまけ
作成したフローのメタデータです。
```xml
<?xml version="1.0" encoding="UTF-8"?>
<Flow xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>61.0</apiVersion>
    <assignments>
        <name>Assign_AddPermissionSetId</name>
        <label>権限セットIDを追加する</label>
        <locationX>264</locationX>
        <locationY>350</locationY>
        <assignmentItems>
            <assignToReference>AssignedPermissionSetIds</assignToReference>
            <operator>Add</operator>
            <value>
                <elementReference>Loop_PermissionSetAssignment.PermissionSetId</elementReference>
            </value>
        </assignmentItems>
        <connector>
            <targetReference>Loop_PermissionSetAssignment</targetReference>
        </connector>
    </assignments>
    <assignments>
        <name>Assign_ObjectPermissions</name>
        <label>オブジェクト権限を割り当てる</label>
        <locationX>264</locationX>
        <locationY>758</locationY>
        <assignmentItems>
            <assignToReference>Create</assignToReference>
            <operator>Assign</operator>
            <value>
                <elementReference>Formula_Create</elementReference>
            </value>
        </assignmentItems>
        <assignmentItems>
            <assignToReference>Read</assignToReference>
            <operator>Assign</operator>
            <value>
                <elementReference>Formula_Read</elementReference>
            </value>
        </assignmentItems>
        <assignmentItems>
            <assignToReference>Edit</assignToReference>
            <operator>Assign</operator>
            <value>
                <elementReference>Formula_Edit</elementReference>
            </value>
        </assignmentItems>
        <assignmentItems>
            <assignToReference>Delete</assignToReference>
            <operator>Assign</operator>
            <value>
                <elementReference>Formula_Delete</elementReference>
            </value>
        </assignmentItems>
        <assignmentItems>
            <assignToReference>ViewAll</assignToReference>
            <operator>Assign</operator>
            <value>
                <elementReference>Formula_ViewAll</elementReference>
            </value>
        </assignmentItems>
        <assignmentItems>
            <assignToReference>ModifyAll</assignToReference>
            <operator>Assign</operator>
            <value>
                <elementReference>Formula_ModifyAll</elementReference>
            </value>
        </assignmentItems>
        <connector>
            <targetReference>Loop_ObjectPermissions</targetReference>
        </connector>
    </assignments>
    <environments>Default</environments>
    <formulas>
        <name>Formula_Create</name>
        <dataType>Boolean</dataType>
        <expression>OR( {!Create} , {!Loop_ObjectPermissions.PermissionsCreate} )</expression>
    </formulas>
    <formulas>
        <name>Formula_Delete</name>
        <dataType>Boolean</dataType>
        <expression>OR( {!Delete} , {!Loop_ObjectPermissions.PermissionsDelete} )</expression>
    </formulas>
    <formulas>
        <name>Formula_Edit</name>
        <dataType>Boolean</dataType>
        <expression>OR( {!Edit} , {!Loop_ObjectPermissions.PermissionsEdit} )</expression>
    </formulas>
    <formulas>
        <name>Formula_ModifyAll</name>
        <dataType>Boolean</dataType>
        <expression>OR( {!ModifyAll} , {!Loop_ObjectPermissions.PermissionsModifyAllRecords} )</expression>
    </formulas>
    <formulas>
        <name>Formula_Read</name>
        <dataType>Boolean</dataType>
        <expression>OR( {!Read} , {!Loop_ObjectPermissions.PermissionsRead} )</expression>
    </formulas>
    <formulas>
        <name>Formula_ViewAll</name>
        <dataType>Boolean</dataType>
        <expression>OR( {!ViewAll} , {!Loop_ObjectPermissions.PermissionsViewAllRecords} )</expression>
    </formulas>
    <interviewLabel>（自動起動フロー）オブジェクト権限取得 {!$Flow.CurrentDateTime}</interviewLabel>
    <label>（自動起動フロー）オブジェクト権限取得</label>
    <loops>
        <name>Loop_ObjectPermissions</name>
        <label>オブジェクト権限をループする</label>
        <locationX>176</locationX>
        <locationY>650</locationY>
        <collectionReference>Get_ObjectPermissions</collectionReference>
        <iterationOrder>Asc</iterationOrder>
        <nextValueConnector>
            <targetReference>Assign_ObjectPermissions</targetReference>
        </nextValueConnector>
    </loops>
    <loops>
        <name>Loop_PermissionSetAssignment</name>
        <label>権限セットの割り当てをループする</label>
        <locationX>176</locationX>
        <locationY>242</locationY>
        <collectionReference>Get_PermissionSetAssignment</collectionReference>
        <iterationOrder>Asc</iterationOrder>
        <nextValueConnector>
            <targetReference>Assign_AddPermissionSetId</targetReference>
        </nextValueConnector>
        <noMoreValuesConnector>
            <targetReference>Get_ObjectPermissions</targetReference>
        </noMoreValuesConnector>
    </loops>
    <processMetadataValues>
        <name>BuilderType</name>
        <value>
            <stringValue>LightningFlowBuilder</stringValue>
        </value>
    </processMetadataValues>
    <processMetadataValues>
        <name>CanvasMode</name>
        <value>
            <stringValue>AUTO_LAYOUT_CANVAS</stringValue>
        </value>
    </processMetadataValues>
    <processMetadataValues>
        <name>OriginBuilderType</name>
        <value>
            <stringValue>LightningFlowBuilder</stringValue>
        </value>
    </processMetadataValues>
    <processType>AutoLaunchedFlow</processType>
    <recordLookups>
        <name>Get_ObjectPermissions</name>
        <label>オブジェクト権限を取得する</label>
        <locationX>176</locationX>
        <locationY>542</locationY>
        <assignNullValuesIfNoRecordsFound>false</assignNullValuesIfNoRecordsFound>
        <connector>
            <targetReference>Loop_ObjectPermissions</targetReference>
        </connector>
        <filterLogic>and</filterLogic>
        <filters>
            <field>ParentId</field>
            <operator>In</operator>
            <value>
                <elementReference>AssignedPermissionSetIds</elementReference>
            </value>
        </filters>
        <filters>
            <field>SobjectType</field>
            <operator>EqualTo</operator>
            <value>
                <elementReference>SObjectApiName</elementReference>
            </value>
        </filters>
        <getFirstRecordOnly>false</getFirstRecordOnly>
        <object>ObjectPermissions</object>
        <storeOutputAutomatically>true</storeOutputAutomatically>
    </recordLookups>
    <recordLookups>
        <name>Get_PermissionSetAssignment</name>
        <label>権限セットの割り当てを取得する</label>
        <locationX>176</locationX>
        <locationY>134</locationY>
        <assignNullValuesIfNoRecordsFound>false</assignNullValuesIfNoRecordsFound>
        <connector>
            <targetReference>Loop_PermissionSetAssignment</targetReference>
        </connector>
        <filterLogic>and</filterLogic>
        <filters>
            <field>AssigneeId</field>
            <operator>EqualTo</operator>
            <value>
                <elementReference>$User.Id</elementReference>
            </value>
        </filters>
        <getFirstRecordOnly>false</getFirstRecordOnly>
        <object>PermissionSetAssignment</object>
        <storeOutputAutomatically>true</storeOutputAutomatically>
    </recordLookups>
    <runInMode>SystemModeWithoutSharing</runInMode>
    <start>
        <locationX>50</locationX>
        <locationY>0</locationY>
        <connector>
            <targetReference>Get_PermissionSetAssignment</targetReference>
        </connector>
    </start>
    <status>Active</status>
    <variables>
        <name>AssignedPermissionSetIds</name>
        <dataType>String</dataType>
        <isCollection>true</isCollection>
        <isInput>false</isInput>
        <isOutput>false</isOutput>
    </variables>
    <variables>
        <name>Create</name>
        <dataType>Boolean</dataType>
        <isCollection>false</isCollection>
        <isInput>false</isInput>
        <isOutput>true</isOutput>
        <value>
            <booleanValue>false</booleanValue>
        </value>
    </variables>
    <variables>
        <name>Delete</name>
        <dataType>Boolean</dataType>
        <isCollection>false</isCollection>
        <isInput>false</isInput>
        <isOutput>true</isOutput>
        <value>
            <booleanValue>false</booleanValue>
        </value>
    </variables>
    <variables>
        <name>Edit</name>
        <dataType>Boolean</dataType>
        <isCollection>false</isCollection>
        <isInput>false</isInput>
        <isOutput>true</isOutput>
        <value>
            <booleanValue>false</booleanValue>
        </value>
    </variables>
    <variables>
        <name>ModifyAll</name>
        <dataType>Boolean</dataType>
        <isCollection>false</isCollection>
        <isInput>false</isInput>
        <isOutput>true</isOutput>
        <value>
            <booleanValue>false</booleanValue>
        </value>
    </variables>
    <variables>
        <name>Read</name>
        <dataType>Boolean</dataType>
        <isCollection>false</isCollection>
        <isInput>false</isInput>
        <isOutput>true</isOutput>
        <value>
            <booleanValue>false</booleanValue>
        </value>
    </variables>
    <variables>
        <name>SObjectApiName</name>
        <dataType>String</dataType>
        <isCollection>false</isCollection>
        <isInput>true</isInput>
        <isOutput>false</isOutput>
    </variables>
    <variables>
        <name>ViewAll</name>
        <dataType>Boolean</dataType>
        <isCollection>false</isCollection>
        <isInput>false</isInput>
        <isOutput>true</isOutput>
        <value>
            <booleanValue>false</booleanValue>
        </value>
    </variables>
</Flow>
```