16. Command Execution Rules
16.1. Rule Structure
Rules help Gort to determine who is able to perform what task. Gort rules follow a specific format. The rule structure describes what command is executed and what permission is needed in order to execute the command. If a user does not have the specified permission, the user is not able to execute the command.
The general form of a command is:
COMMAND [when CONDITIONS] [allow|must have PERMISSION]
Command: The command indicates the command that’s affected by the rule. Commands are referred to as
bundle_name:command_name
. For example, thesplitecho
command in theecho
bundle would be referenced asecho:splitecho
.Conditions: The (optional) conditions clause indicates when the rule should be is applied. It starts with the keyword
when
, and consists of one or more logical statements. See below for more detail. If a rule contains no conditions, it always applies when the command is used.Permissions: The permissions clause indicates the permissions that a user must have to execute the command when the conditions are met. It begins with the phrase
must have
. Like commands, permissions are namespaced:bundle_name:permission_name
.Allow: The standard permissions clause may be replaced with the
allow
keyword, which can be used to allow a command meeting the rule conditions to be executed by any Gort user.allow
is used in lieu of a permissions clause, and may not be accompanied with any other keyword or phrase.
A basic example of a rule is:
foo:bar with option[delete] == true must have foo:destroy
This rule states that a user attempting to use the bar
command from
the foo
bundle, with the delete
flag set, must have the
foo:destroy
permission.
Rules can also be used to grant broad permissions by using the allow
keyword:
foo:biz allow
This is the simplest possible rule, which allows any user to use the
foo:biz
command under all conditions.
16.2. The Conditions Clause
The conditions rule clause begins with the keyword with
.
The conditions
clause can match specific command parameter, allowing
you to create rules that apply under very specific invocations of a
command.
16.2.1. Options and Arguments
Any command can have two kinds of command parameters: options, are a general term for command flags and switches, and arguments, which are the main inputs into the command.
For example, given the following command:
curl -I --capath /home http://example.com
The options are -I
and --capath /home
, and the parameter is
http://example.com
16.2.2. Testing Options and Arguments
Each rule can reference two pre-defined two data structures: option
and arg
.
option
: A map or dict of the commands options. The value of specific options can be accessed using standard map notation.arg
: A (zero-indexed) list of the command arguments. Specific arguments can be accessed using standard map notation.
16.2.3. Logical Operators
Individual (non-collection) values can logically evaluated using the
<
, >
, ==
and !=
operators:
with option["dry-run"] == true
Regular expressions may also be used.
with option["set"] == /.*/
Not only can specific arg
positions be referenced by index, the
entire parameter list can also be evaluated as a string by omitting the
index. For example, given the following command:
echo foo bar
The following statements are equivalent:
foo:bar with arg[0] == 'foo' and arg[1] == 'bar' allow
foo:bar with arg == 'foo bar' allow
16.2.4. Sets
Options and arguments can be tested against sets of conditions by using one of the following keywords:
in
– Applied to a non-collection value, resolves to true if and only if the value matches a value in the set.any
,in
– Applied to a collection value, resolves to true if and only if any value in the collection matches a value in the set.all
,in
– Applied to a collection value, resolves to true if and only if all value in the collection match a value in the set.
Conditional sets can include zero or more values between square brackets. Regular expressions are also legal members and will be evaluated accordingly. Some examples are:
foo:bar with arg[0] in ['baz', false, 100] must have foo:read
foo:bar with option["foo"] in ["foo", "bar"] allow
foo:bar with any option == /^prod.*/ must have foo:read
foo:bar with any arg in ['wubba'] must have foo:read
foo:bar with any arg in ['wubba', /^f.*/, 10] must have foo:read
foo:bar with all arg in [10, 'baz', 'wubba'] must have foo:read
foo:bar with all option < 10 must have foo:read
foo:bar with all option in ['staging', 'list'] must have foo:read
16.2.5. Combining Qualifiers
Arbitrarily long compound qualifiers can be constructed using the
and
and/or or
keywords, so your rules can be as simple or as
complicated as you need them to be. For example, the following rule is
legal:
foo:bar with arg=="prod" and option["delete"] == true or option["set"] == /.*/ must have foo:destroy
16.3. Permissions
The permissions clause is where you state any permissions that are
required to execute the command. The beginning of the permissions clause
is indicated by the phrase must have
.
Like the conditions clause, it can be arbitrarily complex, and can a single permission, a specific combination of permissions combination, or a list of permissions. It supports the same operations as well:
or
and
any in
all in
allow
For example, the following are rule examples with valid permission settings:
foo:baz with option[delete] == true must have foo:write and site:admin
foo:export must have all in [foo:write, site:ops] or any in [site:admin, site:management]
foo:bar must have any in [foo:read, foo:write]
foo:qux must have all in [foo:write, site:ops] and any in [site:admin, site:management]
foo:biz allow
Note the special allow
keyword, which can be used in lieu of a
permissions clause to allow a command to be executed by any registered
user in Gort.
16.4. Formal Definition
Gort’s command execution rule syntax may seem quite English-like, but it’s actually a well-structured syntax describable as a formal context-free grammar.
For your reference, we have included the notation for Gort’s command execution using Backus–Naur form, a metasyntax notation for context-free grammars that’s often used to describe the syntax of computing languages used in computing.
<rule> ::= <arguments> " " <permissions> | <permissions> ;
<arguments> ::= "with " <argument> ;
<argument> ::= <argument_part> | <argument_part> " " <conditional> " " <argument> ;
<argument_part> ::= <argument_single> | <argument_plural> ;
<argument_single> ::= <variable_single> " " <operator> " " <variable_single> ;
<argument_plural> ::= "all " <defined_set> " " <operator_set> " " <variable_set> | "any " <defined_set> " " <operator_set> " " <variable_set> ;
<defined_set> ::= "arg" | "option" ;
<operator_set> ::= "in" | <operator> ;
<operator> ::= "==" | "!=" | "<" | "<=" | ">" | ">=" ;
<variable_single> ::= "arg[" <literal_integer> "]" | "option[" <literal_string> "]" | <literal> ;
<variable_set> ::= "[" <variable_list> "]" ;
<variable_list> ::= <variable_single> | <variable_single> "," <variable_set> ;
<conditional> ::= "and" | "or" ;
<permissions> ::= "allow" | "must have " <permission> ;
<permission> ::= <permission_part> | <permission_part> " " <conditional> " " <permission> ;
<permission_part> ::= <permission_single> | <permission_plural> ;
<permission_single> ::= <name> ":" <name> ;
<permission_plural> ::= "all in " <permission_set> | "any in " <permission_set> ;
<permission_list> ::= <permission_single> | <permission_single> ", " <permission_list> ;
<permission_set> ::= "[" <permission_list> "]" ;
<literal> ::= <literal_bool> | <literal_string> | <literal_number> | <literal_regex> ;
<literal_bool> ::= "true" | "false" ;
<literal_string> ::= '"' <string> '"' | "'" <string> "'" ;
<literal_number> ::= <literal_integer> | <literal_float> ;
<literal_regex> ::= "/" <regex> "/" ;
<literal_integer> ::= <digit>+ ;
<literal_float> ::= <digit>+ "." <digit>+ ;
<digit> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
<letter> ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" ;
<symbol> ::= "|" | " " | "!" | "#" | "$" | "%" | "&" | "(" | ")" | "*" | "+" | "," | "-" | "." | "/" | ":" | ";" | ">" | "=" | "<" | "?" | "@" | "[" | "\" | "]" | "^" | "_" | "`" | "{" | "}" | "~"
<rune> ::= <letter> | <digit> | <symbol> ;
<string> ::= <rune>+
<character> ::= <letter> | <digit> ;
<name> ::= <character>+ ;
16.5. Todo
The following list includes some features that are considering adding to the command execution rules language:
Built-in/standard permissions (especially for Gort administration actions)
Syntax to access user/group/adapter attributes in rule conditions
Built-in support functions in conditions?
If any of these is particularly important to you, or if you have an idea for a feature not listed here, please feel free to create an issue.