Chromium Code Reviews| Index: server/config/access/access.go |
| diff --git a/server/config/access/access.go b/server/config/access/access.go |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..dcf740a8962bd6e9e5705fdacf8235b138ab5fc3 |
| --- /dev/null |
| +++ b/server/config/access/access.go |
| @@ -0,0 +1,99 @@ |
| +// Copyright 2016 The LUCI Authors. All rights reserved. |
| +// Use of this source code is governed under the Apache License, Version 2.0 |
| +// that can be found in the LICENSE file. |
| + |
| +// Package access implements a software config.Authority access check against a |
|
iannucci
2017/01/07 20:29:23
not sure `software` is the right adjective. maybe
dnj
2017/01/10 03:27:13
Done.
|
| +// project config. |
| +// |
| +// Note that this is a soft check, as the true access authority is the config |
| +// service, and this check is not hitting that service. |
| +// |
| +// If access is granted, this function will return nil. If access is explicitly |
| +// denied, this will return ErrNoAccess. |
| +// |
| +// This is a port of the ACL implementation from the config service: |
| +// https://chromium.googlesource.com/external/github.com/luci/luci-py/+/e3fbb1f5dafa59a2c57cf3a9fe3708f4309ab653/appengine/components/components/config/api.py |
| +package access |
| + |
| +import ( |
| + "strings" |
| + |
| + "github.com/luci/luci-go/common/errors" |
| + configPB "github.com/luci/luci-go/common/proto/config" |
| + "github.com/luci/luci-go/server/auth" |
| + "github.com/luci/luci-go/server/auth/identity" |
| + "github.com/luci/luci-go/server/config" |
| + "github.com/luci/luci-go/server/config/textproto" |
| + |
| + "golang.org/x/net/context" |
| +) |
| + |
| +// ErrNoAccess is an error returned by CheckAccess if the supplied Authority |
| +// does not have access to the supplied config set. |
| +var ErrNoAccess = errors.New("no access") |
| + |
| +// Check tests if a given Authority can access the named config set. |
| +func Check(c context.Context, a config.Authority, configSet string) error { |
| + if a == config.AsService { |
| + return nil |
| + } |
| + |
| + _, projectConfigSet, _ := config.ParseProjectConfigSet(configSet) |
| + if projectConfigSet == "" { |
| + // Not a project config set, so neither remaining Authority can access. |
| + return ErrNoAccess |
| + } |
| + |
| + // Load the project config. We execute this RPC as the service, not the user, |
| + // so while this will recurse (and hopefully take advantage of the cache), it |
| + // will not trigger an infinite access check loop. |
| + var pcfg configPB.ProjectCfg |
| + if err := config.Get(c, config.AsService, projectConfigSet, config.ProjectConfigPath, |
| + textproto.Message(&pcfg), nil); err != nil { |
| + return errors.Annotate(err).Reason("failed to load %(path)q in %(configSet)q"). |
| + D("path", config.ProjectConfigPath).D("configSet", projectConfigSet).Err() |
| + } |
| + |
| + id := identity.AnonymousIdentity |
| + if a == config.AsUser { |
| + id = auth.CurrentIdentity(c) |
| + } |
| + checkGroups := make([]string, 0, len(pcfg.Access)) |
| + for _, access := range pcfg.Access { |
| + if group, ok := trimPrefix(access, "group:"); ok { |
| + // If "group" is "all", then short-circuit to permitted. |
|
iannucci
2017/01/07 20:29:23
!!! but `all` is a valid group name!
can't we mak
Vadim Sh.
2017/01/07 21:04:23
We should just remove this short-circuit (if luci-
dnj
2017/01/10 03:27:13
Done.
|
| + if group == "all" { |
| + return nil |
| + } |
| + |
| + // Check group membership. |
| + checkGroups = append(checkGroups, group) |
| + } else { |
| + // If there is no ":" in the access string, this is a user ACL. |
|
iannucci
2017/01/07 20:29:23
bummer that we couldn't have made the access strin
Vadim Sh.
2017/01/07 21:04:23
It is the same. "user:" is just default prefix.
S
|
| + if strings.IndexRune(access, ':') < 0 { |
| + access = "user:" + access |
| + } |
| + if identity.Identity(access) == id { |
| + return nil |
| + } |
| + } |
| + } |
| + |
| + // No individual accesses, check groups if we're not anonymous. |
| + if a != config.AsAnonymous && len(checkGroups) > 0 { |
| + switch canAccess, err := auth.IsMember(c, checkGroups...); { |
| + case err != nil: |
| + return errors.Annotate(err).Reason("failed to check group membership").Err() |
| + case canAccess: |
| + return nil |
| + } |
| + } |
| + return ErrNoAccess |
| +} |
| + |
| +func trimPrefix(v, pfx string) (string, bool) { |
| + if strings.HasPrefix(v, pfx) { |
| + return v[len(pfx):], true |
| + } |
| + return v, false |
| +} |