Skip to main content

Access policy expressions

An access rule is a condition a user must meet before Mattermost lets them join a channel or team, or take an action such as uploading a file. Mattermost stores each rule as an expression in CEL (Common Expression Language), a small language for writing yes-or-no conditions such as user.attributes.Department == "Engineering".

You can write rules in two ways:

  • Simple Mode is a table. Each row picks an attribute, an operator, and one or more values, and Mattermost writes the expression for you. A user must match every row.
  • Advanced Mode is a text editor where you write the expression yourself. You can combine conditions with && (and), || (or), and ! (not), and group them with parentheses.

Both modes are available in the System Console. Channel Settings and Team Settings offer Simple Mode only.

This page describes everything a rule can refer to and every operator Mattermost accepts. To create and assign policies, see System-wide attribute-based access policies, Team membership access policies, and Channel-specific access rules. To define the attributes themselves, see Attribute Management.

Attributes you can use​

A rule can refer to the following values:

In a ruleShown in Simple Mode asWhat it holdsWhere you can use it
user.attributes.<name>The attribute's display nameA user attribute defined in Attribute Management, such as user.attributes.Department.Any rule
user.emailEmailThe user's email address.Any rule
user.verifiedEmail verifiedtrue if the user has verified their email address, otherwise false.Any rule
user.isbotBot accounttrue for bot accounts, otherwise false.Any rule
user.createatAccount createdWhen the account was created. It can only be used with youngerThanDays.Any rule
user.session.<name>The session attribute's display nameA property of the user's current connection, such as their IP address. See Session attributes.Permission rules only
resource.attributes.<name>The channel attribute's display name, under Channel attributes in the value menuAn attribute of the channel being accessed. See Channel attributes.Any rule except team membership rules

Keep the following in mind:

  • In Advanced Mode, use the attribute's unique name, not its display name. Names can contain only letters, numbers, and underscores, and can't start with a number. They're case-sensitive, so Clearance and clearance are different attributes. Older attributes whose names contain spaces can't be used in rules.
  • Attributes that users can edit themselves are excluded. Otherwise, users could give themselves access by changing their own profile. Attributes synced from AD/LDAP or SAML, and attributes users can't edit, are always available. Simple Mode shows user-editable attributes as unavailable, and a rule that uses one can't be saved.
  • true and false aren't quoted for user.verified and user.isbot: write user.verified == true. Session attributes are different; see Session attributes.

Operators by attribute type​

Which operators Simple Mode offers depends on the attribute's type. The table shows the expression each operator produces, so you can write the same condition in Advanced Mode.

Attribute typeSimple Mode operatorExpression it produces
Text, Phone, URL, Selectisuser.attributes.Department == "Engineering"
is notuser.attributes.Department != "Engineering"
inuser.attributes.Department in ["Engineering", "Sales"]
starts withuser.attributes.Location.startsWith("US-")
ends withuser.attributes.Location.endsWith("-HQ")
containsuser.attributes.Location.contains("East")
Multiselecthas any of("North" in user.attributes.Regions || "South" in user.attributes.Regions)
has all of"North" in user.attributes.Regions && "South" in user.attributes.Regions
Rankedis exactlyuser.attributes.Clearance == "Secret"
is notuser.attributes.Clearance != "Secret"
is at leastuser.attributes.Clearance >= "Secret"
is greater thanuser.attributes.Clearance > "Secret"
is at mostuser.attributes.Clearance <= "Secret"
is less thanuser.attributes.Clearance < "Secret"
Hierarchicalcovers all ofuser.attributes.Programs.coversAll(["F-18", "F-35"])
covers any ofuser.attributes.Programs.coversAny(["F-18", "F-35"])
is within all ofuser.attributes.Programs.withinAll(["Air"])
is within any ofuser.attributes.Programs.withinAny(["Air", "Sea"])
has any of("F-18" in user.attributes.Programs || "F-35" in user.attributes.Programs)
has all of"F-18" in user.attributes.Programs && "F-35" in user.attributes.Programs
Emailis, is not, in, contains, starts with, ends withuser.email.endsWith("@example.com")
Email verified, Bot accountis, is notuser.verified == true, user.isbot == false
Account createdyounger than (days)user.createat.youngerThanDays(30)
Session attributesis, is not, in, starts with, ends with, containsuser.session.os_platform == "windows"
Session IP addressAlso in IP rangeuser.session.ip_address.inCIDR("10.0.0.0/8")
Session versionsAlso version is, version is greater than, version is at least, version is less than, version is at mostuser.session.client_version.versionGTE("6.3.0")

Where an operator can compare against a channel attribute, Simple Mode lists the matching channel attributes under Channel attributes in the value menu. See Channel attributes.

Everything Advanced Mode accepts​

Advanced Mode accepts only the following:

  • Comparisons: ==, !=, <, <=, >, >=. Use <, <=, >, and >= with ranked attributes.
  • Logic: && (and), || (or), ! (not), parentheses, and condition ? a : b (if condition is true, use a, otherwise b).
  • Membership: in. Use user.attributes.Department in ["Engineering", "Sales"] to check a single value against a list, and "North" in user.attributes.Regions to check whether a multiselect or hierarchical attribute includes a value.
  • Text functions: startsWith, endsWith, contains.
  • Multiselect functions: hasAnyOf, hasAllOf. Their argument must be a channel attribute. To check fixed values, use in.
  • Hierarchical functions: coversAll, coversAny, withinAll, withinAny. See Hierarchical attributes.
  • Session functions: inCIDR, versionEQ, versionGT, versionGTE, versionLT, versionLTE. See Session attributes.
  • Account age: youngerThanDays, on user.createat only.
  • Values: text in quotes, true and false, whole numbers, and lists of these in square brackets.

Anything else is rejected when you validate or save the rule. This includes other CEL functions, decimal numbers, null, maps such as {"a": 1}, and list macros such as all(), exists(), filter(), and map() (CEL shortcuts that loop over a list).

Ranked attributes​

A ranked attribute's options have a fixed order, from lowest to highest, set in Attribute Management. Comparisons use that order, not alphabetical order. For example, if Clearance runs Public, Confidential, Secret, Top Secret, this rule matches users whose clearance is Secret or Top Secret:

user.attributes.Clearance >= "Secret"

Keep the following in mind:

  • Compare against an option name or a channel attribute. The other side of the comparison must be one of the attribute's option names, or a ranked channel attribute that comes from the same global attribute, such as user.attributes.Clearance >= resource.attributes.Clearance. See Channel attributes.
  • in isn't supported. To match a range, use >= or <=. To match specific options, join == checks with ||.
  • Two user attributes can't be compared with each other. For example, user.attributes.Clearance >= user.attributes.RequiredClearance is rejected.
  • A misspelled option name is rejected when you save.
  • Deleting or renaming an option an existing rule uses breaks the rule. The whole rule then evaluates to false for everyone, even if the comparison is inside !. Update the rule before you change the options.

Hierarchical attributes​

From Mattermost v12.0, an attribute's options can form a hierarchy, where an option can have parent options above it and child options below it. For example, a Programs attribute might have these options:

All Programs
├── Air
│ ├── F-18
│ └── F-35
└── Sea
└── Submarine

A user can hold one or more options. Holding an option counts for everything below it: a user who holds Air also covers F-18 and F-35. Four functions use the hierarchy:

  • coversAll(list) (covers all of): for every option in the list, the user holds that option or one above it.
  • coversAny(list) (covers any of): for at least one option in the list, the user holds that option or one above it.
  • withinAll(list) (is within all of): every option the user holds is in the list or below an option in it.
  • withinAny(list) (is within any of): at least one option the user holds is in the list or below an option in it.

In short, covers checks that the user is at or above the options you name, and within checks that the user is at or below them. Within all is the strict one: it excludes a user who holds anything outside the options you name.

Here's how the rules evaluate for three users. Alex holds Air, Blake holds F-18, and Casey holds F-18 and Submarine.

RuleAlexBlakeCasey
user.attributes.Programs.coversAll(["F-18"])YesYesYes
user.attributes.Programs.coversAll(["F-18", "Submarine"])NoNoYes
user.attributes.Programs.coversAny(["F-35", "Submarine"])YesNoYes
user.attributes.Programs.withinAll(["Air"])YesYesNo
user.attributes.Programs.withinAny(["Air"])YesYesYes
"Air" in user.attributes.ProgramsYesNoNo

Keep the following in mind:

  • in ignores the hierarchy. "Air" in user.attributes.Programs matches only users who hold Air itself. Each in tests one option; to test several, join them with && or ||. The Simple Mode has any of and has all of operators do this for you.
  • Compare against a channel attribute by passing it instead of a list, for example user.attributes.Programs.coversAll(resource.attributes.Programs). Both must come from the same global attribute.
  • No other operators work with hierarchical attributes. ==, !=, <, >, startsWith, and the rest are rejected when you save, because a hierarchy has no single order to compare against.
  • The list can't be empty.
  • Option names aren't checked when you save. If a rule names an option that doesn't exist, because it's misspelled or was later deleted, the whole rule evaluates to false for everyone. Use Test access rule or Simulate access to catch this before it removes members.

Channel attributes​

From Mattermost v12.0, a rule can compare a user's attributes with the attributes of the channel being accessed, written resource.attributes.<name>. This lets one system-wide policy serve many channels, each with its own requirement. For example, assign this policy to every classified channel and set each channel's Clearance attribute:

user.attributes.Clearance >= resource.attributes.Clearance

A user can then join a Secret channel if their clearance is Secret or higher, and a Top Secret channel only if it's Top Secret.

To compare a user attribute with a channel attribute, apply one global attribute to both Users and Channels in Attribute Management. Keep the following in mind:

  • Ranked, multiselect, and hierarchical attributes must come from the same global attribute on both sides, so that the options match. Mattermost rejects the rule otherwise.
  • A multiselect channel attribute can only be used with hasAnyOf or hasAllOf, called on a multiselect user attribute. user.attributes.Regions.hasAnyOf(resource.attributes.Regions) matches users who have at least one of the channel's regions. hasAllOf matches users who have every one of the channel's regions; extra regions are fine.
  • Team membership rules can't use channel attributes, because teams don't have attributes. This includes system-wide policies assigned to a team.
  • Only resource.attributes.<name> is supported. Other forms, such as has(resource.attributes.Clearance), are rejected.

Session attributes​

From Mattermost v12.0, permission rules can check the user's current connection, such as their IP address, their app version, or whether a VPN is active. These values are written user.session.<name>. They can't be used in membership rules, because membership doesn't depend on how someone is connected right now.

Each session attribute must be enabled in System Console > System Attributes > Session Attributes before a rule can use it. A rule that uses an unknown or disabled attribute can't be saved. See Session attributes for the full list and for which clients report each one.

Two kinds of function work only with session attributes:

  • inCIDR checks whether an IP address is inside a range. Ranges are written in CIDR notation, an address followed by the number of fixed leading bits; for example, 10.0.0.0/8 covers every address starting with 10.:

    user.session.ip_address.inCIDR("10.0.0.0/8")
  • versionEQ, versionGT, versionGTE, versionLT, and versionLTE compare version numbers part by part, so 6.10.0 counts as newer than 6.9.0:

    user.session.client_version.versionGTE("6.3.0")

Session values are always text. For yes-or-no attributes, compare against the quoted strings "true" and "false", for example user.session.vpn_active == "true".

How rules combine​

Mattermost combines conditions and rules in the following ways:

  • Rows in Simple Mode are combined with AND: a user must match every row.
  • Channel permission rules for the same role and action are combined with OR: a user must match at least one. For example, a channel could have one rule letting members in Engineering upload files and another letting members on the VPN upload files; a member who matches either can upload. A channel admin with no rule of their own for an action is checked against the channel member rules.
  • A policy and the system-wide policies it inherits are combined with AND: the user must pass all of them. A channel's or team's own rules can narrow a system-wide policy but never widen it.
  • For permission actions, such as uploading or downloading files, the channel's permission rules and the system-wide permission policies must all allow the action.
  • An action with no rules at all isn't restricted.

Membership rules and permission rules are checked at different times. Membership rules decide who can be in a channel or team. They're checked when someone joins or is added, and again on each synchronization. Permission rules decide whether a member can take an action, and they're checked each time the user tries.

When a rule can't be evaluated​

When Mattermost can't decide whether a rule matches, it denies access. This happens in the following cases:

  • The user has no value for an attribute the rule uses. The condition doesn't match, even for is not or inside !. For example, user.attributes.Department != "Sales" doesn't match a user with no department. Another condition joined with || can still match.
  • A session attribute is missing, because the client doesn't report it or the value has expired.
  • The channel has no value for a channel attribute the rule uses. See Channel attributes.
  • A ranked or hierarchical option no longer exists. The whole rule evaluates to false.
  • A value can't be read, such as an IP address or version that isn't valid.

To find these problems before they affect users, select Test access rule in a membership policy to see which users match. In a permission policy, select Simulate rules to check specific users. See Simulate access.