You are here:
Protection and Privacy Options for Custom Metadata Types
Manage the visibility and permissions of custom metadata types.
Required Editions
| Available in: Salesforce Classic and Lightning Experience |
Available in: Enterprise, Performance, Unlimited, and Developer Editions You can create, edit, and delete custom metadata type records from installed packages in: Group and Professional Editions |
Packages
For sensitive data, like application secrets, it’s important that custom metadata types are included in a managed package. When contained in a managed package and set to protected or package protected, they’re not visible to subscribing orgs, making it a good place to store certain kinds of secrets.
Protected custom metadata types in an unmanaged package behave like public custom metadata types. Make sure that secrets, personally identifying information, or any private data is stored in protected custom metadata types that are installed as part of a managed package.
Visibility
You can create protected custom metadata types in developer and scratch orgs. These options for custom metadata types are available.
- PackageProtected—When in a second-generation managed package, only Apex code in the same managed package can see the type. The name of the type and the record are visible if they’re referenced in a formula.
- Protected—When in a managed package, only Apex code in the same namespace can see the type. The name of the type and the record are visible if they’re referenced in a formula.
- Public—Regardless of the type of package (managed or unmanaged), these
have access.
- Apex
- Formulas
- Flows
- API for users with Customize Application permission or permissions granted through profiles or permission sets. The custom metadata type, fields, and unprotected records are visible in Setup.
Schema Settings
The Schema Settings option, Restrict access to custom metadata types, is an org-wide preference that limits access to custom metadata types. This preference is enabled by default, and API Read access to custom metadata types must be explicitly granted. Admins with Customize Application permission can grant access to users through profiles and permission sets.
Behavior of Apex, Visualforce, and Aura
There are different execution code modes within Salesforce that affect the accessibility of custom metadata types.
Apex code that runs in system mode ignores user permissions, and your Apex code has access to all objects and fields. Object permissions, field-level security, and sharing rules aren't applied for the current user. Running in system mode ensures that the code doesn’t fail because of hidden fields or objects for a user.
In user mode, functionality such as Visualforce Components, Visualforce Email templates, and Aura, runs with respect to the user's permissions and sharing of records.
with sharing modifier in the Apex class, doesn’t
affect query behavior such as, isAccessible() and isCreatable(). If a field value is retrieved in Apex and assigned to a
non-sObject variable, the behavior is the same whether or not the preference is
enabled.When functionality is run in user mode, such as Visualforce Components, Visualforce
Email templates, and Aura, you must have permission to access the custom metadata
types. For example, without permission, the fields on Visualforce pages that you
don't have access to aren’t displayed. The $Setup global variable (available in Visualforce and formulas)
continues to load values by direct reference (meaning, data that’s assigned to an
sObject type) regardless of the running user.
Consider this scenario:
- (1) Apex loads a record that’s a row included in a variable such as
MyCMT__c. - (2) What Visualforce displays is
MyCMT__c.MyPath__c. - (3) Access checks run when the page is loaded.
- (4) But the checks aren’t run in system mode, which is the standard Visualforce behavior. Users without permission to the custom metadata type can’t display the Visualforce page because Visualforce reinitiates the access check.
In this scenario, if a user isn’t allowed permission to the custom metadata type,
there are two workarounds. You can create a string for each object, which can be
passed through, or create a wrapper class. Use these options instead of assigning a
variable such as MyCMT__c, then rendering
myCMT.Path__c myCMT.Name. For example,
class DataHolder{
public string path {get;set;}
public boolean active {get;set;}
}When you load the rows into a collection, the Visualforce checks are bypassed because the type is a data type instead of an sObject.
Here’s an example that includes the @AuraEnabled annotation for an Aura or Lightning component
controller.
class with sharing MyController {
@AuraEnabled
public static List<My__mdt> thisWillNotWork() {
return [select developername from my__mdt];
}
@AuraEnabled
public static List<String> thisWill() {
List<String> retVal = new List<String>();
for(My__mdt config: [select developername from my__mdt]) {
retVal.add(config.DeveloperName);
}
return retVal;
}
}Retrieving Custom Metadata Type Information
When you retrieve custom metadata type records or a count of custom metadata types, the number returned by SOQL, Apex, formulas, flows, or APIs can differ from the number in the user interface. This difference is because of the custom metadata type visibility settings and the access that a user has to the type based on profiles and permissions.

