lib/azure.bend
near-v-framework@0.2.0.0 · 192 lines · 0 definitions
1import Base
2import near-architecture@0.9.1.0/arc.bend as Arc
3
4# Public metadata and effective ARM permission patterns only. No credential values.
5type Resource is Data:
6 Resource{name: String, kind: String, group: String, location: String}
7
8type DeclaredResource is Data:
9 DeclaredResource{name: String, kind: String, group: String}
10
11type Permission is Data:
12 Permission{scope: String, actions: List<&2, String>, not_actions: List<&2, String>}
13
14type Project is Data:
15 Project{name: String}
16
17type Repository is Data:
18 Repository{name: String}
19
20type Pipeline is Data:
21 Pipeline{name: String, repository: String, yaml: String}
22
23type ServiceConnection is Data:
24 ServiceConnection{name: String, kind: String, scheme: String}
25
26type Identity is Data:
27 Identity{profile: String, name: String}
28
29def same_resource(a: Resource, b: Resource) -> Bool:
30 match a b:
31 case Resource{an, ak, ag, al} Resource{bn, bk, bg, bl}:
32 String.eq(an, bn) && String.eq(ak, bk) && String.eq(ag, bg) && String.eq(al, bl)
33
34def contains_resource(+x: Resource, xs: List<&2, Resource>) -> Bool:
35 match xs:
36 case []:
37 False{}
38 case y <> rest:
39 same_resource(x, y) || contains_resource(x, rest)
40
41def all_resources_in(xs: List<&2, Resource>, +ys: List<&2, Resource>) -> Bool:
42 match xs:
43 case []:
44 True{}
45 case x <> rest:
46 contains_resource(x, ys) && all_resources_in(rest, ys)
47
48def resources_equal(+a: List<&2, Resource>, +b: List<&2, Resource>) -> Bool:
49 all_resources_in(a, b) && all_resources_in(b, a)
50
51def declared_matches(d: DeclaredResource, r: Resource) -> Bool:
52 match d r:
53 case DeclaredResource{dn, dk, dg} Resource{rn, rk, rg, _}:
54 String.eq(dn, rn) && String.eq(dk, rk) && String.eq(dg, rg)
55
56def resource_declared(+r: Resource, ds: List<&2, DeclaredResource>) -> Bool:
57 match ds:
58 case []: False{}
59 case d <> rest: declared_matches(d, r) || resource_declared(r, rest)
60
61def declaration_exists(+d: DeclaredResource, rs: List<&2, Resource>) -> Bool:
62 match rs:
63 case []: False{}
64 case r <> rest: declared_matches(d, r) || declaration_exists(d, rest)
65
66def all_observed_declared(rs: List<&2, Resource>, +ds: List<&2, DeclaredResource>) -> Bool:
67 match rs:
68 case []: True{}
69 case r <> rest: resource_declared(r, ds) && all_observed_declared(rest, ds)
70
71def all_declared_observed(ds: List<&2, DeclaredResource>, +rs: List<&2, Resource>) -> Bool:
72 match ds:
73 case []: True{}
74 case d <> rest: declaration_exists(d, rs) && all_declared_observed(rest, rs)
75
76def inventory_equal(+ds: List<&2, DeclaredResource>, +rs: List<&2, Resource>) -> Bool:
77 all_observed_declared(rs, ds) && all_declared_observed(ds, rs)
78
79def declaration_names(ds: List<&2, DeclaredResource>) -> List<&2, String>:
80 match ds:
81 case []: []
82 case d <> rest:
83 match d:
84 case DeclaredResource{name, _, _}: name <> declaration_names(rest)
85
86def contains_name(+name: String, xs: List<&2, String>) -> Bool:
87 match xs:
88 case []: False{}
89 case x <> rest: String.eq(name, x) || contains_name(name, rest)
90
91def all_names_in(xs: List<&2, String>, +ys: List<&2, String>) -> Bool:
92 match xs:
93 case []: True{}
94 case x <> rest: contains_name(x, ys) && all_names_in(rest, ys)
95
96def scopes_equal(+declared: List<&2, String>, +observed: List<&2, String>) -> Bool:
97 all_names_in(declared, observed) && all_names_in(observed, declared)
98
99# ARM patterns use '*' for zero or more characters; matching is case insensitive.
100def glob_chars(+ps: List<&2, Char>, +vs: List<&2, Char>) -> Bool:
101 match ps:
102 case []:
103 match vs:
104 case []: True{}
105 case _ <> _: False{}
106 case p <> rest:
107 match p:
108 case '*':
109 match vs:
110 case []: glob_chars(rest, [])
111 case v <> tail: glob_chars(rest, v <> tail) || glob_chars('*' <> rest, tail)
112 case _:
113 match vs:
114 case []: False{}
115 case v <> tail: Char.is_eq(p, v) && glob_chars(rest, tail)
116
117def glob(pattern: String, value: String) -> Bool:
118 glob_chars(String.to_list(String.to_lower(pattern)), String.to_list(String.to_lower(value)))
119
120# A wildcard pattern is covered only by the same pattern or '*'. This conservative
121# rule avoids claiming that overlapping wildcards grant exactly equal permissions.
122def covers(+want: String, +actual: String) -> Bool:
123 String.eq(String.to_lower(want), String.to_lower(actual)) ||
124 String.eq(want, "*") ||
125 (Bool.not(String.contains(actual, "*")) && glob(want, actual))
126
127def covered(+action: String, patterns: List<&2, String>) -> Bool:
128 match patterns:
129 case []:
130 False{}
131 case p <> rest:
132 covers(p, action) || covered(action, rest)
133
134def every_covered(xs: List<&2, String>, +by: List<&2, String>) -> Bool:
135 match xs:
136 case []:
137 True{}
138 case x <> rest:
139 covered(x, by) && every_covered(rest, by)
140
141def actions_if(hit: Bool, acts: List<&2, String>, rest: List<&2, String>) -> List<&2, String>:
142 match hit:
143 case True{}: List.append(&2, String, acts, rest)
144 case False{}: rest
145
146def scope_actions(+scope: String, xs: List<&2, Permission>) -> List<&2, String>:
147 match xs:
148 case []:
149 []
150 case p <> rest:
151 match p:
152 case Permission{s, acts, _}:
153 actions_if(String.eq(scope, s), acts, scope_actions(scope, rest))
154
155def no_exclusions(+scope: String, xs: List<&2, Permission>) -> Bool:
156 match xs:
157 case []:
158 True{}
159 case p <> rest:
160 match p:
161 case Permission{s, _, exclusions}:
162 (Bool.not(String.eq(scope, s)) || List.is_empty(&2, String, exclusions)) && no_exclusions(scope, rest)
163
164def permissions_equal(+scope: String, +want: List<&2, String>, +actual: List<&2, Permission>) -> Bool:
165 +got : List<&2, String> = scope_actions(scope, actual)
166 no_exclusions(scope, actual) && every_covered(got, want) && every_covered(want, got)
167
168def in_domain(name: String, domain: String) -> Bool:
169 String.ends_with(String.to_lower(name), "@" ++ String.to_lower(domain))
170
171def identities_in_domain(xs: List<&2, Identity>, +domain: String) -> Bool:
172 match xs:
173 case []:
174 True{}
175 case x <> rest:
176 match x:
177 case Identity{_, name}:
178 in_domain(name, domain) && identities_in_domain(rest, domain)
179
180def identity_names(xs: List<&2, Identity>) -> List<&2, String>:
181 match xs:
182 case []: []
183 case x <> rest:
184 match x:
185 case Identity{_, name}: name <> identity_names(rest)
186
187# Permission minimality: on every resource group, `person` has exactly what the agreement grants
188# there (nothing granted means no access at all).
189def access_minimal(groups: List<&2, String>, +a: Arc.Agreement, +person: String, +actual: List<&2, Permission>) -> Bool:
190 match groups:
191 case []: True{}
192 case +g <> rest: permissions_equal(g, Arc.granted(a, person, g), actual) && access_minimal(rest, a, person, actual)