OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package coordinator | 5 package coordinator |
6 | 6 |
7 import ( | 7 import ( |
8 "fmt" | 8 "fmt" |
9 | 9 |
10 "github.com/luci/gae/service/info" | 10 "github.com/luci/gae/service/info" |
11 luciConfig "github.com/luci/luci-go/common/config" | 11 luciConfig "github.com/luci/luci-go/common/config" |
12 log "github.com/luci/luci-go/common/logging" | 12 log "github.com/luci/luci-go/common/logging" |
13 "golang.org/x/net/context" | 13 "golang.org/x/net/context" |
14 ) | 14 ) |
15 | 15 |
| 16 // NamespaceAccessType specifies the type of namespace access that is being |
| 17 // requested for WithProjectNamespace. |
| 18 type NamespaceAccessType int |
| 19 |
| 20 const ( |
| 21 // NamespaceAccessNoAuth grants unconditional access to a project's name
space. |
| 22 // This bypasses all ACL checks, and must only be used by service endpoi
nts |
| 23 // that explicitly apply ACLs elsewhere. |
| 24 NamespaceAccessNoAuth NamespaceAccessType = iota |
| 25 |
| 26 // NamespaceAccessREAD enforces READ permission access to a project's |
| 27 // namespace. |
| 28 NamespaceAccessREAD |
| 29 |
| 30 // NamespaceAccessWRITE enforces WRITE permission access to a project's |
| 31 // namespace. |
| 32 NamespaceAccessWRITE |
| 33 ) |
| 34 |
16 type servicesKeyType int | 35 type servicesKeyType int |
17 | 36 |
18 // WithServices installs the supplied Services instance into a Context. | 37 // WithServices installs the supplied Services instance into a Context. |
19 func WithServices(c context.Context, s Services) context.Context { | 38 func WithServices(c context.Context, s Services) context.Context { |
20 return context.WithValue(c, servicesKeyType(0), s) | 39 return context.WithValue(c, servicesKeyType(0), s) |
21 } | 40 } |
22 | 41 |
23 // GetServices gets the Services instance installed in the supplied Context. | 42 // GetServices gets the Services instance installed in the supplied Context. |
24 // | 43 // |
25 // If no Services has been installed, it will panic. | 44 // If no Services has been installed, it will panic. |
26 func GetServices(c context.Context) Services { | 45 func GetServices(c context.Context) Services { |
27 s, ok := c.Value(servicesKeyType(0)).(Services) | 46 s, ok := c.Value(servicesKeyType(0)).(Services) |
28 if !ok { | 47 if !ok { |
29 panic("no Services instance is installed") | 48 panic("no Services instance is installed") |
30 } | 49 } |
31 return s | 50 return s |
32 } | 51 } |
33 | 52 |
34 // WithProjectNamespace sets the current namespace to the project name. | 53 // WithProjectNamespace sets the current namespace to the project name. |
35 // | 54 // |
36 // It will return an error if the project name or the project's namespace is | 55 // It will return an error if the project name or the project's namespace is |
37 // invalid. | 56 // invalid. |
38 // | 57 // |
39 // If the current user does not have READ permission for the project, a | 58 // If the current user does not have the requested permission for the project, a |
40 // MembershipError will be returned. | 59 // MembershipError will be returned. |
41 func WithProjectNamespace(c *context.Context, project luciConfig.ProjectName) er
ror { | 60 func WithProjectNamespace(c *context.Context, project luciConfig.ProjectName, at
NamespaceAccessType) error { |
42 » return withProjectNamespaceImpl(c, project, true) | |
43 } | |
44 | |
45 // WithProjectNamespaceNoAuth sets the current namespace to the project name. It | |
46 // does NOT assert that the current user has project access. This should only be | |
47 // used for service functions that are not acting on behalf of a user. | |
48 // | |
49 // It will fail if the project name is invalid. | |
50 func WithProjectNamespaceNoAuth(c *context.Context, project luciConfig.ProjectNa
me) error { | |
51 » return withProjectNamespaceImpl(c, project, false) | |
52 } | |
53 | |
54 func withProjectNamespaceImpl(c *context.Context, project luciConfig.ProjectName
, auth bool) error { | |
55 ctx := *c | 61 ctx := *c |
56 | 62 |
57 // TODO(dnj): REQUIRE this to be non-empty once namespacing is mandatory
. | 63 // TODO(dnj): REQUIRE this to be non-empty once namespacing is mandatory
. |
58 if project == "" { | 64 if project == "" { |
59 return nil | 65 return nil |
60 } | 66 } |
61 | 67 |
62 if err := project.Validate(); err != nil { | 68 if err := project.Validate(); err != nil { |
63 log.WithError(err).Errorf(ctx, "Project name is invalid.") | 69 log.WithError(err).Errorf(ctx, "Project name is invalid.") |
64 return err | 70 return err |
65 } | 71 } |
66 | 72 |
67 » // Validate the user's READ access to the named project, if authenticati
ng. | 73 » // Validate the current user has the requested access. |
68 » if auth { | 74 » switch at { |
| 75 » case NamespaceAccessNoAuth: |
| 76 » » break |
| 77 |
| 78 » case NamespaceAccessREAD: |
69 pcfg, err := GetServices(ctx).ProjectConfig(ctx, project) | 79 pcfg, err := GetServices(ctx).ProjectConfig(ctx, project) |
70 if err != nil { | 80 if err != nil { |
71 log.WithError(err).Errorf(ctx, "Failed to load project c
onfig.") | 81 log.WithError(err).Errorf(ctx, "Failed to load project c
onfig.") |
72 return err | 82 return err |
73 } | 83 } |
74 | 84 |
75 » » if err := IsProjectReader(ctx, pcfg); err != nil { | 85 » » if err := IsProjectReader(*c, pcfg); err != nil { |
76 » » » log.WithError(err).Errorf(ctx, "User cannot access reque
sted project.") | 86 » » » log.WithError(err).Errorf(*c, "User denied READ access t
o requested project.") |
77 return err | 87 return err |
78 } | 88 } |
| 89 |
| 90 case NamespaceAccessWRITE: |
| 91 pcfg, err := GetServices(ctx).ProjectConfig(ctx, project) |
| 92 if err != nil { |
| 93 log.WithError(err).Errorf(ctx, "Failed to load project c
onfig.") |
| 94 return err |
| 95 } |
| 96 |
| 97 if err := IsProjectWriter(*c, pcfg); err != nil { |
| 98 log.WithError(err).Errorf(*c, "User denied WRITE access
to requested project.") |
| 99 return err |
| 100 } |
| 101 |
| 102 default: |
| 103 return fmt.Errorf("unknown access type: %v", at) |
79 } | 104 } |
80 | 105 |
81 pns := ProjectNamespace(project) | 106 pns := ProjectNamespace(project) |
82 nc, err := info.Get(ctx).Namespace(pns) | 107 nc, err := info.Get(ctx).Namespace(pns) |
83 if err != nil { | 108 if err != nil { |
84 log.Fields{ | 109 log.Fields{ |
85 log.ErrorKey: err, | 110 log.ErrorKey: err, |
86 "project": project, | 111 "project": project, |
87 "namespace": pns, | 112 "namespace": pns, |
88 }.Errorf(ctx, "Failed to set namespace.") | 113 }.Errorf(ctx, "Failed to set namespace.") |
(...skipping 18 matching lines...) Expand all Loading... |
107 if ns == "" { | 132 if ns == "" { |
108 return "" | 133 return "" |
109 } | 134 } |
110 | 135 |
111 project := ProjectFromNamespace(ns) | 136 project := ProjectFromNamespace(ns) |
112 if project != "" { | 137 if project != "" { |
113 return project | 138 return project |
114 } | 139 } |
115 panic(fmt.Errorf("current namespace %q does not begin with project names
pace prefix (%q)", ns, projectNamespacePrefix)) | 140 panic(fmt.Errorf("current namespace %q does not begin with project names
pace prefix (%q)", ns, projectNamespacePrefix)) |
116 } | 141 } |
OLD | NEW |