Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(467)

Side by Side Diff: scheduler/appengine/acl/acl.go

Issue 2986033003: [scheduler]: ACLs phase 1 - per Job ACL specification and enforcement. (Closed)
Patch Set: pcg Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2017 The LUCI Authors. 1 // Copyright 2017 The LUCI Authors.
2 // 2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License. 4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at 5 // You may obtain a copy of the License at
6 // 6 //
7 // http://www.apache.org/licenses/LICENSE-2.0 7 // http://www.apache.org/licenses/LICENSE-2.0
8 // 8 //
9 // Unless required by applicable law or agreed to in writing, software 9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, 10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and 12 // See the License for the specific language governing permissions and
13 // limitations under the License. 13 // limitations under the License.
14 14
15 package acl 15 package acl
16 16
17 import ( 17 import (
18 » "golang.org/x/net/context" 18 » "context"
19 » "fmt"
20 » "regexp"
21 » "sort"
22 » "strings"
19 23
24 "github.com/luci/luci-go/scheduler/appengine/messages"
20 "github.com/luci/luci-go/server/auth" 25 "github.com/luci/luci-go/server/auth"
21 ) 26 )
22 27
28 // IsJobOwner is deprecated. UIse GrantsByRole.IsOwner instead.
23 func IsJobOwner(c context.Context, projectID, jobName string) bool { 29 func IsJobOwner(c context.Context, projectID, jobName string) bool {
24 // TODO(vadimsh): Do real ACLs. 30 // TODO(vadimsh): Do real ACLs.
25 ok, err := auth.IsMember(c, "administrators") 31 ok, err := auth.IsMember(c, "administrators")
26 if err != nil { 32 if err != nil {
27 panic(err) 33 panic(err)
28 } 34 }
29 return ok 35 return ok
30 } 36 }
37
38 // GrantsByRole can answer questions who can READ and who OWNS the task.
39 type GrantsByRole struct {
40 Owners []string `gae:",noindex"`
41 Readers []string `gae:",noindex"`
42 }
43
44 func (g *GrantsByRole) IsOwner(c context.Context) (bool, error) {
45 return hasGrant(c, g.Owners, groupsAdministrators)
46 }
47
48 func (g *GrantsByRole) IsReader(c context.Context) (bool, error) {
49 return hasGrant(c, g.Owners, g.Readers, groupsAdministrators)
50 }
51
52 func (g *GrantsByRole) Equal(o *GrantsByRole) bool {
53 eqSlice := func(a, b []string) bool {
54 if len(a) != len(b) {
55 return false
56 }
57 for i := range a {
58 if a[i] != b[i] {
59 return false
60 }
61 }
62 return true
63 }
64 return eqSlice(g.Owners, o.Owners) && eqSlice(g.Readers, o.Readers)
65 }
66
67 // AclSets are parsed and indexed `AclSet` of a project.
68 type AclSets map[string][]*messages.Acl
69
70 // ValidateAclSets validates list of AclSet of a project and returns AclSets.
71 func ValidateAclSets(sets []*messages.AclSet) (AclSets, error) {
Vadim Sh. 2017/08/01 01:56:19 you may or may not be interested in this thing: ht
tandrii(chromium) 2017/08/01 22:50:01 Neat! I'm punting it to another CL to refactor cat
Vadim Sh. 2017/08/01 23:04:09 No.
72 as := make(AclSets, len(sets))
73 for _, s := range sets {
74 if s.Name == "" {
75 return nil, fmt.Errorf("missing 'name' field'")
76 }
77 if !aclSetNameRe.MatchString(s.Name) {
78 return nil, fmt.Errorf("%q is not valid value for 'name' field", s.Name)
79 }
80 if _, isDup := as[s.Name]; isDup {
81 return nil, fmt.Errorf("aclSet name %q is not unique", s .Name)
82 }
83 if len(s.GetAcls()) == 0 {
84 return nil, fmt.Errorf("aclSet %q has no entries", s.Nam e)
85 }
86 as[s.Name] = s.GetAcls()
87 }
88 return as, nil
89 }
90
91 // ValidateTaskAcls validates task's ACLs and returns TaskAcls.
92 func ValidateTaskAcls(pSets AclSets, tSets []string, tAcls []*messages.Acl) (*Gr antsByRole, error) {
93 grantsLists := make([][]*messages.Acl, 0, 1+len(tSets))
94 if err := validateGrants(tAcls); err != nil {
95 return nil, err
96 }
97 grantsLists = append(grantsLists, tAcls)
98 for _, set := range tSets {
99 grantsList, exists := pSets[set]
100 if !exists {
101 return nil, fmt.Errorf("referencing AclSet '%s' which do esn't exist", set)
102 }
103 grantsLists = append(grantsLists, grantsList)
104 }
105 mg := mergeGrants(grantsLists...)
106 if n := len(mg.Owners) + len(mg.Readers); n > maxGrantsPerJob {
107 return nil, fmt.Errorf("Job or Trigger can have at most %d acls, but %d given", maxGrantsPerJob, n)
108 }
109 return mg, nil
110 }
111
112 ////////////////////////////////////////////////////////////////////////////////
113
114 var (
115 // aclSetNameRe is used to validate AclSet Name field.
116 aclSetNameRe = regexp.MustCompile(`^[0-9A-Za-z_\-\.]{1,100}$`)
tandrii(chromium) 2017/08/01 22:50:01 changed here instead.
117 // maxGrantsPerJob is how many different grants are specified for a job.
118 maxGrantsPerJob = 32
119
120 groupsAdministrators = []string{"group:administrators"}
121 )
122
123 func validateGrants(gs []*messages.Acl) error {
124 for _, g := range gs {
125 if g.GetRole() != messages.Acl_OWNER && g.GetRole() != messages. Acl_READER {
126 return fmt.Errorf("invalid role %q", g.GetRole())
127 }
128 if g.GetGrantedTo() == "" {
129 return fmt.Errorf("missing granted_to for role %s", g.Ge tRole())
130 }
Vadim Sh. 2017/08/01 01:56:18 please validate granted_to: if it has prefix "gro
tandrii(chromium) 2017/08/01 22:50:01 Done.
131 }
132 return nil
133 }
134
135 // mergeGrants merges valid grants into GrantsByRole, removing and sorting dupli cates.
136 func mergeGrants(grantsLists ...[]*messages.Acl) *GrantsByRole {
137 all := map[messages.Acl_Role]map[string]bool{
Vadim Sh. 2017/08/01 01:56:19 you may or may not be interested in https://godoc.
tandrii(chromium) 2017/08/01 22:50:01 indeed I am :) Done.
138 messages.Acl_OWNER: {},
139 messages.Acl_READER: {},
140 }
141 for _, grantsList := range grantsLists {
142 for _, g := range grantsList {
143 all[g.GetRole()][g.GetGrantedTo()] = true
144 }
145 }
146
147 sortKeys := func(m map[string]bool) []string {
148 r := make([]string, 0, len(m))
149 for k := range m {
150 r = append(r, k)
151 }
152 sort.Strings(r)
153 return r
154 }
155 return &GrantsByRole{
156 Owners: sortKeys(all[messages.Acl_OWNER]),
157 Readers: sortKeys(all[messages.Acl_READER]),
158 }
159 }
160
161 // hasGrant is current user is covered by any given grants.
162 func hasGrant(c context.Context, grantsList ...[]string) (bool, error) {
163 groups := []string{}
164 for _, grants := range grantsList {
165 for _, grant := range grants {
166 parts := strings.SplitN(grant, ":", 2)
167 if len(parts) < 2 {
168 // grant is just email.
169 if auth.CurrentIdentity(c).Email() == grant {
Vadim Sh. 2017/08/01 01:56:19 this returns "" if CurretnIdentity is not of 'user
tandrii(chromium) 2017/08/01 22:50:01 Done & refactored.
170 return true, nil
171 }
172 continue
173 }
174 kind, value := parts[0], parts[1]
175 // TODO(tandrii): other kinds.
176 switch kind {
177 case "group":
178 groups = append(groups, value)
179 default:
180 panic(fmt.Errorf("granted_to '%s' kind is not su pported", grant))
Vadim Sh. 2017/08/01 01:56:19 just check auth.CurrentIdentity(c) == identity.Ide
tandrii(chromium) 2017/08/01 22:50:01 Done.
181 }
182 }
183 }
184 return auth.IsMember(c, groups...)
185 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698