Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

Apexで他のユーザの項目レベルセキュリティを見るには

はじめに

お久しぶりです。技術本部 Sansan Engineering Unit Data Hubグループの菊池です。 皆さんSalesforce開発を楽しんでいますか?

私はSalesforce開発に携わり9年経ちますが、まだまだ知らないことが多く日々勉強であると痛感しています。

その中で今回は技術調査で得たナレッジを共有します。

背景

普段Apex開発で項目レベルセキュリティを確認する処理を実装したことがある方は多いと思います。

public void createLeadRecord(String phoneValue, String emailValue) {
    Schema.DescribeSObjectResult leadDescribe = Lead.SObjectType.getDescribe();

    Lead lead = new Lead();
    lead.LastName = 'testLastName';
    lead.Company = 'testCompany';

    Schema.DescribeFieldResult phoneFieldDescribe = leadDescribe.fields.getMap().get('Phone').getDescribe();
    if (phoneFieldDescribe.isAccessible() && phoneFieldDescribe.isCreateable()) {
        lead.Phone = phoneValue;
    }
    Schema.DescribeFieldResult emailFieldDescribe = leadDescribe.fields.getMap().get('Email').getDescribe();
    if (emailFieldDescribe.isAccessible() && emailFieldDescribe.isCreateable()) {
        lead.Email = emailValue;
    }
    insert lead;
}

上記例のように対象のオブジェクトに対し SObjectType.etDescribe() を呼び出して、オブジェクト定義を取得し権限チェックすると思います。

ただ SObjectType.getDescribe() はコード実行者の権限は取れるが、他のユーザの権限を取ることはできないみたいです。

大体の権限チェックはコード実行者の権限チェックで問題ないのですが、他のユーザのチェックをしたいニーズがあった場合どうするか検証しました。

検証内容

①SOQLによる検証

結論

プロファイル、権限セットの情報をSOQLで取得できるか確認しました。 結論ドキュメントや開発者コンソールで確認したところ、各項目の項目レベルセキュリティを取得することはできないみたいです。

ドキュメントリンク

➁APIによる検証

結論

Metadata APIを用いれば他ユーザの権限を取得することができました。 下記検証手順を共有します。

検証内容

① 以下の手順に従い、Metadata APIを呼び出すClientクラスを生成します。

  1. 設定画面のクイック検索に「API」と入力し、「API」をクリック
  2. 「メタデータ WSDL の生成」をクリックし、表示されたxmlファイルをダウンロードする
  3. ダウンロードしたxmlファイルから不要なmetadataを削除する ※そのままだと、ファイルサイズ制限(1MB)を超えるため不要な部分を削除する
  4. 再度設定画面を開き「Apex」と入力し、「Apexクラス」をクリック
  5. 「WSDLからの生成」をクリック
  6. 任意のクラス名を入力し、「Apexコードの生成」をクリック

※今回は①の手順を基に出力されたApexクラスを加工して、以下のApexクラスを用いて検証しました。

SampleMetadataApiClient.cls(クリックすると展開されます)

public with sharing class SampleMetadataApiClient {
    public static String SOAP_M_URI = 'http://soap.sforce.com/2006/04/metadata';

    public interface IReadResult {
        SampleMetadataApiClient.Metadata[] getRecords();
    }

    public interface IReadResponseElement {
        IReadResult getResult();
    }

    public class ReadProfileResult implements IReadResult {
        public SampleMetadataApiClient.Profile[] records;
        public SampleMetadataApiClient.Metadata[] getRecords() { return records; }
        private String[] records_type_info = new String[]{'records',SOAP_M_URI,null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'records'};
    }

    public class readProfileResponse_element implements IReadResponseElement {
        public SampleMetadataApiClient.ReadProfileResult result;
        public IReadResult getResult() { return result; }
        private String[] result_type_info = new String[]{'result',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'result'};
    }

    public class Profile extends Metadata {
        public String type = 'Profile';
        public String fullName;
        private String[] fullName_type_info = new String[]{'fullName',SOAP_M_URI,null,'0','1','false'};
        public SampleMetadataApiClient.ProfileApplicationVisibility[] applicationVisibilities;
        public SampleMetadataApiClient.ProfileCategoryGroupVisibility[] categoryGroupVisibilities;
        public SampleMetadataApiClient.ProfileApexClassAccess[] classAccesses;
        public Boolean custom;
        public SampleMetadataApiClient.ProfileCustomPermissions[] customPermissions;
        public String description;
        public SampleMetadataApiClient.ProfileExternalDataSourceAccess[] externalDataSourceAccesses;
        public SampleMetadataApiClient.ProfileFieldLevelSecurity[] fieldPermissions;
        public SampleMetadataApiClient.ProfileLayoutAssignment[] layoutAssignments;
        public SampleMetadataApiClient.ProfileLoginHours loginHours;
        public SampleMetadataApiClient.ProfileLoginIpRange[] loginIpRanges;
        public SampleMetadataApiClient.ProfileObjectPermissions[] objectPermissions;
        public SampleMetadataApiClient.ProfileApexPageAccess[] pageAccesses;
        public SampleMetadataApiClient.ProfileActionOverride[] profileActionOverrides;
        public SampleMetadataApiClient.ProfileRecordTypeVisibility[] recordTypeVisibilities;
        public SampleMetadataApiClient.ProfileTabVisibility[] tabVisibilities;
        public String userLicense;
        public SampleMetadataApiClient.ProfileUserPermission[] userPermissions;
        private String[] applicationVisibilities_type_info = new String[]{'applicationVisibilities',SOAP_M_URI,null,'0','-1','false'};
        private String[] categoryGroupVisibilities_type_info = new String[]{'categoryGroupVisibilities',SOAP_M_URI,null,'0','-1','false'};
        private String[] classAccesses_type_info = new String[]{'classAccesses',SOAP_M_URI,null,'0','-1','false'};
        private String[] custom_type_info = new String[]{'custom',SOAP_M_URI,null,'0','1','false'};
        private String[] customPermissions_type_info = new String[]{'customPermissions',SOAP_M_URI,null,'0','-1','false'};
        private String[] description_type_info = new String[]{'description',SOAP_M_URI,null,'0','1','false'};
        private String[] externalDataSourceAccesses_type_info = new String[]{'externalDataSourceAccesses',SOAP_M_URI,null,'0','-1','false'};
        private String[] fieldPermissions_type_info = new String[]{'fieldPermissions',SOAP_M_URI,null,'0','-1','false'};
        private String[] layoutAssignments_type_info = new String[]{'layoutAssignments',SOAP_M_URI,null,'0','-1','false'};
        private String[] loginHours_type_info = new String[]{'loginHours',SOAP_M_URI,null,'0','1','false'};
        private String[] loginIpRanges_type_info = new String[]{'loginIpRanges',SOAP_M_URI,null,'0','-1','false'};
        private String[] objectPermissions_type_info = new String[]{'objectPermissions',SOAP_M_URI,null,'0','-1','false'};
        private String[] pageAccesses_type_info = new String[]{'pageAccesses',SOAP_M_URI,null,'0','-1','false'};
        private String[] profileActionOverrides_type_info = new String[]{'profileActionOverrides',SOAP_M_URI,null,'0','-1','false'};
        private String[] recordTypeVisibilities_type_info = new String[]{'recordTypeVisibilities',SOAP_M_URI,null,'0','-1','false'};
        private String[] tabVisibilities_type_info = new String[]{'tabVisibilities',SOAP_M_URI,null,'0','-1','false'};
        private String[] userLicense_type_info = new String[]{'userLicense',SOAP_M_URI,null,'0','1','false'};
        private String[] userPermissions_type_info = new String[]{'userPermissions',SOAP_M_URI,null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] type_att_info = new String[]{'xsi:type'};
        private String[] field_order_type_info = new String[]{'fullName', 'applicationVisibilities','categoryGroupVisibilities','classAccesses','custom','customPermissions','description','externalDataSourceAccesses','fieldPermissions','layoutAssignments','loginHours','loginIpRanges','objectPermissions','pageAccesses','profileActionOverrides','recordTypeVisibilities','tabVisibilities','userLicense','userPermissions'};
    }

    public class ProfileApplicationVisibility {
        public String application;
        public Boolean default_x;
        public Boolean visible;
        private String[] application_type_info = new String[]{'application',SOAP_M_URI,null,'1','1','false'};
        private String[] default_x_type_info = new String[]{'default',SOAP_M_URI,null,'1','1','false'};
        private String[] visible_type_info = new String[]{'visible',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'application','default_x','visible'};
    }

    public class ProfileCategoryGroupVisibility {
        public String[] dataCategories;
        public String dataCategoryGroup;
        public String visibility;
        private String[] dataCategories_type_info = new String[]{'dataCategories',SOAP_M_URI,null,'0','-1','false'};
        private String[] dataCategoryGroup_type_info = new String[]{'dataCategoryGroup',SOAP_M_URI,null,'1','1','false'};
        private String[] visibility_type_info = new String[]{'visibility',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'dataCategories','dataCategoryGroup','visibility'};
    }

    public class ProfileApexClassAccess {
        public String apexClass;
        public Boolean enabled;
        private String[] apexClass_type_info = new String[]{'apexClass',SOAP_M_URI,null,'1','1','false'};
        private String[] enabled_type_info = new String[]{'enabled',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'apexClass','enabled'};
    }

    public class ProfileCustomPermissions {
        public Boolean enabled;
        public String name;
        private String[] enabled_type_info = new String[]{'enabled',SOAP_M_URI,null,'1','1','false'};
        private String[] name_type_info = new String[]{'name',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'enabled','name'};
    }

    public class ProfileExternalDataSourceAccess {
        public Boolean enabled;
        public String externalDataSource;
        private String[] enabled_type_info = new String[]{'enabled',SOAP_M_URI,null,'1','1','false'};
        private String[] externalDataSource_type_info = new String[]{'externalDataSource',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'enabled','externalDataSource'};
    }

    public class ProfileFieldLevelSecurity {
        public Boolean editable;
        public String field;
        public Boolean readable;
        private String[] editable_type_info = new String[]{'editable',SOAP_M_URI,null,'1','1','false'};
        private String[] field_type_info = new String[]{'field',SOAP_M_URI,null,'1','1','false'};
        private String[] readable_type_info = new String[]{'readable',SOAP_M_URI,null,'0','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'editable','field','readable'};
    }

    public class ProfileLayoutAssignment {
        public String layout;
        public String recordType;
        private String[] layout_type_info = new String[]{'layout',SOAP_M_URI,null,'1','1','false'};
        private String[] recordType_type_info = new String[]{'recordType',SOAP_M_URI,null,'0','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'layout','recordType'};
    }

    public class ProfileLoginHours {
        public String fridayEnd;
        public String fridayStart;
        public String mondayEnd;
        public String mondayStart;
        public String saturdayEnd;
        public String saturdayStart;
        public String sundayEnd;
        public String sundayStart;
        public String thursdayEnd;
        public String thursdayStart;
        public String tuesdayEnd;
        public String tuesdayStart;
        public String wednesdayEnd;
        public String wednesdayStart;
        private String[] fridayEnd_type_info = new String[]{'fridayEnd',SOAP_M_URI,null,'0','1','false'};
        private String[] fridayStart_type_info = new String[]{'fridayStart',SOAP_M_URI,null,'0','1','false'};
        private String[] mondayEnd_type_info = new String[]{'mondayEnd',SOAP_M_URI,null,'0','1','false'};
        private String[] mondayStart_type_info = new String[]{'mondayStart',SOAP_M_URI,null,'0','1','false'};
        private String[] saturdayEnd_type_info = new String[]{'saturdayEnd',SOAP_M_URI,null,'0','1','false'};
        private String[] saturdayStart_type_info = new String[]{'saturdayStart',SOAP_M_URI,null,'0','1','false'};
        private String[] sundayEnd_type_info = new String[]{'sundayEnd',SOAP_M_URI,null,'0','1','false'};
        private String[] sundayStart_type_info = new String[]{'sundayStart',SOAP_M_URI,null,'0','1','false'};
        private String[] thursdayEnd_type_info = new String[]{'thursdayEnd',SOAP_M_URI,null,'0','1','false'};
        private String[] thursdayStart_type_info = new String[]{'thursdayStart',SOAP_M_URI,null,'0','1','false'};
        private String[] tuesdayEnd_type_info = new String[]{'tuesdayEnd',SOAP_M_URI,null,'0','1','false'};
        private String[] tuesdayStart_type_info = new String[]{'tuesdayStart',SOAP_M_URI,null,'0','1','false'};
        private String[] wednesdayEnd_type_info = new String[]{'wednesdayEnd',SOAP_M_URI,null,'0','1','false'};
        private String[] wednesdayStart_type_info = new String[]{'wednesdayStart',SOAP_M_URI,null,'0','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'fridayEnd','fridayStart','mondayEnd','mondayStart','saturdayEnd','saturdayStart','sundayEnd','sundayStart','thursdayEnd','thursdayStart','tuesdayEnd','tuesdayStart','wednesdayEnd','wednesdayStart'};
    }

    public class ProfileLoginIpRange {
        public String description;
        public String endAddress;
        public String startAddress;
        private String[] description_type_info = new String[]{'description',SOAP_M_URI,null,'0','1','false'};
        private String[] endAddress_type_info = new String[]{'endAddress',SOAP_M_URI,null,'1','1','false'};
        private String[] startAddress_type_info = new String[]{'startAddress',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'description','endAddress','startAddress'};
    }

    public class ProfileObjectPermissions {
        public Boolean allowCreate;
        public Boolean allowDelete;
        public Boolean allowEdit;
        public Boolean allowRead;
        public Boolean modifyAllRecords;
        public String object_x;
        public Boolean viewAllRecords;
        private String[] allowCreate_type_info = new String[]{'allowCreate',SOAP_M_URI,null,'0','1','false'};
        private String[] allowDelete_type_info = new String[]{'allowDelete',SOAP_M_URI,null,'0','1','false'};
        private String[] allowEdit_type_info = new String[]{'allowEdit',SOAP_M_URI,null,'0','1','false'};
        private String[] allowRead_type_info = new String[]{'allowRead',SOAP_M_URI,null,'0','1','false'};
        private String[] modifyAllRecords_type_info = new String[]{'modifyAllRecords',SOAP_M_URI,null,'0','1','false'};
        private String[] object_x_type_info = new String[]{'object',SOAP_M_URI,null,'1','1','false'};
        private String[] viewAllRecords_type_info = new String[]{'viewAllRecords',SOAP_M_URI,null,'0','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'allowCreate','allowDelete','allowEdit','allowRead','modifyAllRecords','object_x','viewAllRecords'};
    }

    public class ProfileApexPageAccess {
        public String apexPage;
        public Boolean enabled;
        private String[] apexPage_type_info = new String[]{'apexPage',SOAP_M_URI,null,'1','1','false'};
        private String[] enabled_type_info = new String[]{'enabled',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'apexPage','enabled'};
    }

    public class ProfileActionOverride {
        public String actionName;
        public String content;
        public String formFactor;
        public String pageOrSobjectType;
        public String recordType;
        public String type_x;
        private String[] actionName_type_info = new String[]{'actionName',SOAP_M_URI,null,'1','1','false'};
        private String[] content_type_info = new String[]{'content',SOAP_M_URI,null,'0','1','false'};
        private String[] formFactor_type_info = new String[]{'formFactor',SOAP_M_URI,null,'1','1','false'};
        private String[] pageOrSobjectType_type_info = new String[]{'pageOrSobjectType',SOAP_M_URI,null,'1','1','false'};
        private String[] recordType_type_info = new String[]{'recordType',SOAP_M_URI,null,'0','1','false'};
        private String[] type_x_type_info = new String[]{'type',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'actionName','content','formFactor','pageOrSobjectType','recordType','type_x'};
    }

    public class ProfileRecordTypeVisibility {
        public Boolean default_x;
        public Boolean personAccountDefault;
        public String recordType;
        public Boolean visible;
        private String[] default_x_type_info = new String[]{'default',SOAP_M_URI,null,'1','1','false'};
        private String[] personAccountDefault_type_info = new String[]{'personAccountDefault',SOAP_M_URI,null,'0','1','false'};
        private String[] recordType_type_info = new String[]{'recordType',SOAP_M_URI,null,'1','1','false'};
        private String[] visible_type_info = new String[]{'visible',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'default_x','personAccountDefault','recordType','visible'};
    }

    public class ProfileTabVisibility {
        public String tab;
        public String visibility;
        private String[] tab_type_info = new String[]{'tab',SOAP_M_URI,null,'1','1','false'};
        private String[] visibility_type_info = new String[]{'visibility',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'tab','visibility'};
    }

    public class ProfileUserPermission {
        public Boolean enabled;
        public String name;
        private String[] enabled_type_info = new String[]{'enabled',SOAP_M_URI,null,'1','1','false'};
        private String[] name_type_info = new String[]{'name',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'enabled','name'};
    }

    public class ReadPermissionSetResult implements IReadResult {
        public SampleMetadataApiClient.PermissionSet[] records;
        public SampleMetadataApiClient.Metadata[] getRecords() { return records; }
        private String[] records_type_info = new String[]{'records',SOAP_M_URI,null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'records'};
    }

    public class readPermissionSetResponse_element implements IReadResponseElement {
        public SampleMetadataApiClient.ReadPermissionSetResult result;
        public IReadResult getResult() { return result; }
        private String[] result_type_info = new String[]{'result',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'result'};
    }

    public class PermissionSet extends Metadata {
        public String type = 'PermissionSet';
        public String fullName;
        private String[] fullName_type_info = new String[]{'fullName',SOAP_M_URI,null,'0','1','false'};
        public SampleMetadataApiClient.PermissionSetApplicationVisibility[] applicationVisibilities;
        public SampleMetadataApiClient.PermissionSetApexClassAccess[] classAccesses;
        public SampleMetadataApiClient.PermissionSetCustomPermissions[] customPermissions;
        public String description;
        public SampleMetadataApiClient.PermissionSetExternalDataSourceAccess[] externalDataSourceAccesses;
        public SampleMetadataApiClient.PermissionSetFieldPermissions[] fieldPermissions;
        public Boolean hasActivationRequired;
        public String label;
        public String license;
        public SampleMetadataApiClient.PermissionSetObjectPermissions[] objectPermissions;
        public SampleMetadataApiClient.PermissionSetApexPageAccess[] pageAccesses;
        public SampleMetadataApiClient.PermissionSetRecordTypeVisibility[] recordTypeVisibilities;
        public SampleMetadataApiClient.PermissionSetTabSetting[] tabSettings;
        public SampleMetadataApiClient.PermissionSetUserPermission[] userPermissions;
        private String[] applicationVisibilities_type_info = new String[]{'applicationVisibilities',SOAP_M_URI,null,'0','-1','false'};
        private String[] classAccesses_type_info = new String[]{'classAccesses',SOAP_M_URI,null,'0','-1','false'};
        private String[] customPermissions_type_info = new String[]{'customPermissions',SOAP_M_URI,null,'0','-1','false'};
        private String[] description_type_info = new String[]{'description',SOAP_M_URI,null,'0','1','false'};
        private String[] externalDataSourceAccesses_type_info = new String[]{'externalDataSourceAccesses',SOAP_M_URI,null,'0','-1','false'};
        private String[] fieldPermissions_type_info = new String[]{'fieldPermissions',SOAP_M_URI,null,'0','-1','false'};
        private String[] hasActivationRequired_type_info = new String[]{'hasActivationRequired',SOAP_M_URI,null,'0','1','false'};
        private String[] label_type_info = new String[]{'label',SOAP_M_URI,null,'1','1','false'};
        private String[] license_type_info = new String[]{'license',SOAP_M_URI,null,'0','1','false'};
        private String[] objectPermissions_type_info = new String[]{'objectPermissions',SOAP_M_URI,null,'0','-1','false'};
        private String[] pageAccesses_type_info = new String[]{'pageAccesses',SOAP_M_URI,null,'0','-1','false'};
        private String[] recordTypeVisibilities_type_info = new String[]{'recordTypeVisibilities',SOAP_M_URI,null,'0','-1','false'};
        private String[] tabSettings_type_info = new String[]{'tabSettings',SOAP_M_URI,null,'0','-1','false'};
        private String[] userPermissions_type_info = new String[]{'userPermissions',SOAP_M_URI,null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] type_att_info = new String[]{'xsi:type'};
        private String[] field_order_type_info = new String[]{'fullName', 'applicationVisibilities','classAccesses','customPermissions','description','externalDataSourceAccesses','fieldPermissions','hasActivationRequired','label','license','objectPermissions','pageAccesses','recordTypeVisibilities','tabSettings','userPermissions'};
    }

    public class PermissionSetApplicationVisibility {
        public String application;
        public Boolean visible;
        private String[] application_type_info = new String[]{'application',SOAP_M_URI,null,'1','1','false'};
        private String[] visible_type_info = new String[]{'visible',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'application','visible'};
    }

    public class PermissionSetApexClassAccess {
        public String apexClass;
        public Boolean enabled;
        private String[] apexClass_type_info = new String[]{'apexClass',SOAP_M_URI,null,'1','1','false'};
        private String[] enabled_type_info = new String[]{'enabled',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'apexClass','enabled'};
    }

    public class PermissionSetCustomPermissions {
        public Boolean enabled;
        public String name;
        private String[] enabled_type_info = new String[]{'enabled',SOAP_M_URI,null,'1','1','false'};
        private String[] name_type_info = new String[]{'name',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'enabled','name'};
    }

    public class PermissionSetExternalDataSourceAccess {
        public Boolean enabled;
        public String externalDataSource;
        private String[] enabled_type_info = new String[]{'enabled',SOAP_M_URI,null,'1','1','false'};
        private String[] externalDataSource_type_info = new String[]{'externalDataSource',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'enabled','externalDataSource'};
    }

    public class PermissionSetFieldPermissions {
        public Boolean editable;
        public String field;
        public Boolean readable;
        private String[] editable_type_info = new String[]{'editable',SOAP_M_URI,null,'1','1','false'};
        private String[] field_type_info = new String[]{'field',SOAP_M_URI,null,'1','1','false'};
        private String[] readable_type_info = new String[]{'readable',SOAP_M_URI,null,'0','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'editable','field','readable'};
    }

    public class PermissionSetObjectPermissions {
        public Boolean allowCreate;
        public Boolean allowDelete;
        public Boolean allowEdit;
        public Boolean allowRead;
        public Boolean modifyAllRecords;
        public String object_x;
        public Boolean viewAllRecords;
        private String[] allowCreate_type_info = new String[]{'allowCreate',SOAP_M_URI,null,'1','1','false'};
        private String[] allowDelete_type_info = new String[]{'allowDelete',SOAP_M_URI,null,'1','1','false'};
        private String[] allowEdit_type_info = new String[]{'allowEdit',SOAP_M_URI,null,'1','1','false'};
        private String[] allowRead_type_info = new String[]{'allowRead',SOAP_M_URI,null,'1','1','false'};
        private String[] modifyAllRecords_type_info = new String[]{'modifyAllRecords',SOAP_M_URI,null,'1','1','false'};
        private String[] object_x_type_info = new String[]{'object',SOAP_M_URI,null,'1','1','false'};
        private String[] viewAllRecords_type_info = new String[]{'viewAllRecords',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'allowCreate','allowDelete','allowEdit','allowRead','modifyAllRecords','object_x','viewAllRecords'};
    }

    public class PermissionSetApexPageAccess {
        public String apexPage;
        public Boolean enabled;
        private String[] apexPage_type_info = new String[]{'apexPage',SOAP_M_URI,null,'1','1','false'};
        private String[] enabled_type_info = new String[]{'enabled',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'apexPage','enabled'};
    }

    public class PermissionSetRecordTypeVisibility {
        public String recordType;
        public Boolean visible;
        private String[] recordType_type_info = new String[]{'recordType',SOAP_M_URI,null,'1','1','false'};
        private String[] visible_type_info = new String[]{'visible',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'recordType','visible'};
    }

    public class PermissionSetTabSetting {
        public String tab;
        public String visibility;
        private String[] tab_type_info = new String[]{'tab',SOAP_M_URI,null,'1','1','false'};
        private String[] visibility_type_info = new String[]{'visibility',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'tab','visibility'};
    }

    public class PermissionSetUserPermission {
        public Boolean enabled;
        public String name;
        private String[] enabled_type_info = new String[]{'enabled',SOAP_M_URI,null,'1','1','false'};
        private String[] name_type_info = new String[]{'name',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'enabled','name'};
    }

    public virtual class Metadata {
        public String fullName;
    }

    public class MetadataPort {
        public String endpoint_x = URL.getOrgDomainUrl().toExternalForm() + '/services/Soap/m/42.0';
        public Map<String,String> inputHttpHeaders_x;
        public Map<String,String> outputHttpHeaders_x;
        public String clientCertName_x;
        public String clientCert_x;
        public String clientCertPasswd_x;
        public Integer timeout_x;
        public SampleMetadataApiClient.SessionHeader_element SessionHeader;
        public SampleMetadataApiClient.DebuggingInfo_element DebuggingInfo;
        public SampleMetadataApiClient.DebuggingHeader_element DebuggingHeader;
        public SampleMetadataApiClient.CallOptions_element CallOptions;
        public SampleMetadataApiClient.AllOrNoneHeader_element AllOrNoneHeader;
        private String SessionHeader_hns = 'SessionHeader=http://soap.sforce.com/2006/04/metadata';
        private String DebuggingInfo_hns = 'DebuggingInfo=http://soap.sforce.com/2006/04/metadata';
        private String DebuggingHeader_hns = 'DebuggingHeader=http://soap.sforce.com/2006/04/metadata';
        private String CallOptions_hns = 'CallOptions=http://soap.sforce.com/2006/04/metadata';
        private String AllOrNoneHeader_hns = 'AllOrNoneHeader=http://soap.sforce.com/2006/04/metadata';
        private String[] ns_map_type_info = new String[]{SOAP_M_URI, 'SampleMetadataApiClient'};

        public SampleMetadataApiClient.IReadResult readMetadata(String type_x,String[] fullNames) {
            SampleMetadataApiClient.readMetadata_element request_x = new SampleMetadataApiClient.readMetadata_element();
            request_x.type_x = type_x;
            request_x.fullNames = fullNames;
            SampleMetadataApiClient.IReadResponseElement response_x;
            Map<String, SampleMetadataApiClient.IReadResponseElement> response_map_x = new Map<String, SampleMetadataApiClient.IReadResponseElement>();
            response_map_x.put('response_x', response_x);
            WebServiceCallout.invoke(
              this,
              request_x,
              response_map_x,
              new String[]{endpoint_x,
              '',
              SOAP_M_URI,
              'readMetadata',
              SOAP_M_URI,
              'readMetadataResponse',
              'SampleMetadataApiClient.read' + type_x + 'Response_element'}
            );
            response_x = response_map_x.get('response_x');
            return response_x.getResult();
        }
    }

    public class SessionHeader_element {
        public String sessionId;
        private String[] sessionId_type_info = new String[]{'sessionId',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'sessionId'};
    }

    public class DebuggingInfo_element {
        public String debugLog;
        private String[] debugLog_type_info = new String[]{'debugLog',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'debugLog'};
    }

    public class DebuggingHeader_element {
        public SampleMetadataApiClient.LogInfo[] categories;
        public String debugLevel;
        private String[] categories_type_info = new String[]{'categories',SOAP_M_URI,null,'0','-1','false'};
        private String[] debugLevel_type_info = new String[]{'debugLevel',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'categories','debugLevel'};
    }

    public class LogInfo {
        public String category;
        public String level;
        private String[] category_type_info = new String[]{'category',SOAP_M_URI,null,'1','1','false'};
        private String[] level_type_info = new String[]{'level',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'category','level'};
    }
    public class CallOptions_element {
        public String client;
        private String[] client_type_info = new String[]{'client',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'client'};
    }
    public class AllOrNoneHeader_element {
        public Boolean allOrNone;
        private String[] allOrNone_type_info = new String[]{'allOrNone',SOAP_M_URI,null,'1','1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'allOrNone'};
    }

    public class readMetadata_element {
        public String type_x;
        public String[] fullNames;
        private String[] type_x_type_info = new String[]{'type',SOAP_M_URI,null,'1','1','false'};
        private String[] fullNames_type_info = new String[]{'fullNames',SOAP_M_URI,null,'0','-1','false'};
        private String[] apex_schema_type_info = new String[]{SOAP_M_URI,'true','false'};
        private String[] field_order_type_info = new String[]{'type_x','fullNames'};
    }
}

➁ ①で作成したSampleMetadataApiClientを呼び出すクラスを実装する

今回は以下の順番で処理するよう実装しました。

  • プロファイルの場合
    • プロファイル情報を取得
    • プロファイル名を変換※標準プロファイルの場合のみ
    • SampleMetadataApiClientを元にMetadata APIを呼び出す
    • 呼び出し結果を確認
  • 権限セットの場合
    • ユーザに付与している権限セット情報を取得する
    • SampleMetadataApiClientを元にMetadata APIを呼び出す
    • 呼び出し結果を確認

上記処理の流れを実装したクラスが下記リンクのクラスです。

SampleGetPermissionService.cls(クリックすると展開されます)

public with sharing class SampleGetPermissionService {

    private SampleMetadataApiClient.MetadataPort metadataPort;

    // 標準プロファイル名はMetadata APIを呼び出す際でrenameする必要がある
    public final static Map<String, String> renameProfileNameMap = new Map<String, String>{
        'システム管理者' => 'admin'
    };

    public SampleGetPermissionService() {
        metadataPort = createMetadataPort();
    }

    public void getProfilePermission() {
        Profile profile = [SELECT Id, Name FROM Profile WHERE Id =: UserInfo.getProfileId() LIMIT 1];
        String profileName = renameProfileNameMap.containsKey(profile.Name)
            ? renameProfileNameMap.get(profile.Name)
            : profile.Name;
        List<String> fullNameList = new List<String>{profileName};
        SampleMetadataApiClient.ReadProfileResult  response = (SampleMetadataApiClient.ReadProfileResult)metadataPort.readMetadata('Profile', fullNameList);
        for (SampleMetadataApiClient.Profile pro : response.records) {
            System.debug(response.records[0].type);
            System.debug(response.records[0].fullName);
            for (SampleMetadataApiClient.ProfileFieldLevelSecurity s : pro.fieldPermissions) {
                System.debug('■ ■ ■ s.field ■ ■ ■' + s.field);
                System.debug('■ ■ ■ s.readable ■ ■ ■' + s.readable);
                System.debug('■ ■ ■ s.editable ■ ■ ■' + s.editable);
            }
        }
    }

    public void getPermissionSetPermission() {
        List<PermissionSetAssignment> permissionSetList = [Select Id, PermissionSetId, PermissionSet.Name, AssigneeId, Assignee.Name FROM PermissionSetAssignment WHERE AssigneeId =: UserInfo.getUserId()];
        List<String> permissionSetNames = new List<String>();
        for (PermissionSetAssignment p : permissionSetList) {
            permissionSetNames.add(p.PermissionSet.Name);
        }

        SampleMetadataApiClient.ReadPermissionSetResult response = (SampleMetadataApiClient.ReadPermissionSetResult)metadataPort.readMetadata('PermissionSet', permissionSetNames);
        for (SampleMetadataApiClient.PermissionSet permissionSet : response.records) {
            System.debug(permissionSet.type);
            System.debug(permissionSet.fullName);
            System.debug(permissionSet.label);
            if (permissionSet.fieldPermissions != null) {
                for (SampleMetadataApiClient.PermissionSetFieldPermissions s : permissionSet.fieldPermissions) {
                    System.debug('■ ■ ■ s.field ■ ■ ■' + s.field);
                    System.debug('■ ■ ■ s.readable ■ ■ ■' + s.readable);
                    System.debug('■ ■ ■ s.editable ■ ■ ■' + s.editable);
                }
            }
        }
    }

    private static SampleMetadataApiClient.MetadataPort createMetadataPort(){
        SampleMetadataApiClient.MetadataPort metadataPort = new SampleMetadataApiClient.MetadataPort();
        metadataPort.SessionHeader = new SampleMetadataApiClient.SessionHeader_element();
        metadataPort.SessionHeader.sessionId = UserInfo.getSessionId();
        return metadataPort;
    }
}

③ ➁で実装したクラスを開発者コンソールで呼び出す

開発者コンソールにて実行したら、下記キャプチャの通り各項目の権限を取得できました。

  • プロファイル

  • 権限セット

今回は開発者コンソールで実行したユーザの情報を取得しましたが、UserレコードIdがわかれば他の人の情報もとれるので他のユーザの権限をチェックしたい場合には実装すれば解決すると思います。

おわりに

今回ニッチなニーズに対し、SalesforceのAPIを用いて実現できることがわかりまだまだ奥は深いと思いました。 今後も新たにSalesforceに関するマニアックな知識が見つかったら本ブログにて共有します。 最後まで読んでいただきありがとうございました。

© Sansan, Inc.