summaryrefslogtreecommitdiff
path: root/notes/rules-nitty-gritty
blob: fe9b09f094d6697c8155ebbb1c1067b909dbbd90 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
Nitty-gritty of rules
---------------------

Ultimately, all rules boil down to either a grant or deny operation, or else
some manipulation of the current tagset associated with the request.

A request starts with a set of immutable data such as the user making the
request and the repository being operated upon.  At certain times, other
immutable data will be provided in the form of things like the paths being
modified and the refs being updated.

In addition to the immutable data, rules can create, update and delete other
tags associated with the request as the rules processing is underway.  This
allows for potentially complex rulesets.

The moment the 'gitano/action' tag is created, that action is taken.  Thus a
Grant operation actually ends up setting the 'gitano/action' tag to 'allow'.

The values the gitano/action tag is allowed to take are:

    allow -- Allow the requested operation to take place.
    deny  -- Prevent the requested operation from happening.

Any value other than 'allow' will be treated as 'deny'.  In addition, if the
action is to deny the operation then if gitano/reason is set, it will be
provided to the user.

If the gitano/callout tag is created then the current rule processing is
suspended and the referenced ruleset is called out to.  This allows more
complex "subroutines" to be created by admins without having unpleasant huge
rulesets.  If a callout ends up calling out to a ruleset already in the call
stack then rules processing immediately stops with a 'deny' whose reason is
"Loop detected in rules processing" regardless of whether or not a loop would
actually result from the callout.

Each rule consists of a number of match operations and a number of tag
manipulation operations.  For example a rule might match gitano/operation
against "write", gitano/repository against "^myproject$" (note the .git is
stripped during rule evaluation) and gitano/groups against
"/myproject-members/" and would then set gitano/action to allow.

Such a rule might be expressed as:

GrantWrite(Group "myproject-members")

and be present in the myproject gitano/admin branch rules file.

Rules extracted from the admin branch of a project automatically have a match
against the project added to them.  (and since the rules will only be loaded if
that project is referenced, it all makes sense)

Tags in gitano/ are all immutable except for the action and reason tags and the
callout tag.

Tags in $projectname/ are mutable to rules which match the projectname exactly.
As such, project rules files are permitted to mutate those tags at will.

When setting tag values, the string assigned to them will be set directly.
When matching against tags, the tag's value is first expanded according to the
following syntax:

  $. => . (where . is anything except an open brace)
  
  ${tagname}  =>  expanded to the expanded value of the tag in question.

If during string expansion a loop is detected, then immediately the ruleset
action will be set to deny with the reason "Loop detected in tag expansion".
Note that when matching is done, the pattern used to match is also expanded
according to the expansion rules, so you can use tag values during matches.

You can (optionally) force a value to be expanded before assignment by using
the function called Expand.

So at its core, you add rules in the following way:

Rule({ "match", "gitano/groups", "/${gitano/project}-members/" },
     { "match", "gitano/operation", "write" },
     { "set", "gitano/action", "allow" })

The 'Rule' statement here, in a repository's administration branch, will
implicitly add {"match", "gitano/repository", "^thisrepositoryname$"} narrowing
the ruleset so that no mistakes can be made.