blob: 2adfa8077e6ea375039f279838b9386bf09fd4ff [file] [log] [blame]
Tilak Sharma3ed30242014-08-11 11:45:55 -07001package security
2
3// This file provides an implementation of security.Authorizer.
4//
5// Definitions
6// * Self-RPC: An RPC request is said to be a "self-RPC" if the identities
7// at the local and remote ends are identical.
8
9import (
10 "errors"
11 "os"
12 "reflect"
13
Jiri Simsa519c5072014-09-17 21:37:57 -070014 "veyron.io/veyron/veyron2/security"
Tilak Sharma3ed30242014-08-11 11:45:55 -070015)
16
17var (
18 errACL = errors.New("no matching ACL entry found")
19 errInvalidLabel = errors.New("label is invalid")
Tilak Sharma3ed30242014-08-11 11:45:55 -070020)
21
22// aclAuthorizer implements Authorizer.
23type aclAuthorizer security.ACL
24
25// Authorize verifies a request iff the identity at the remote end has a name authorized by
26// the aclAuthorizer's ACL for the request's label, or the request corresponds to a self-RPC.
27func (a aclAuthorizer) Authorize(ctx security.Context) error {
28 // Test if the request corresponds to a self-RPC.
Asim Shankarbf6263f2014-10-01 12:32:30 -070029 if ctx.LocalBlessings() != nil && ctx.RemoteBlessings() != nil && reflect.DeepEqual(ctx.LocalBlessings().PublicKey(), ctx.RemoteBlessings().PublicKey()) {
30 return nil
31 }
Asim Shankarbf6263f2014-10-01 12:32:30 -070032 var blessings []string
33 if ctx.RemoteBlessings() != nil {
34 blessings = ctx.RemoteBlessings().ForContext(ctx)
Asim Shankarbf6263f2014-10-01 12:32:30 -070035 }
Asim Shankarbf6263f2014-10-01 12:32:30 -070036 return matchesACL(blessings, ctx.Label(), security.ACL(a))
Tilak Sharma3ed30242014-08-11 11:45:55 -070037}
38
39// NewACLAuthorizer creates an authorizer from the provided ACL. The
40// authorizer authorizes a request iff the identity at the remote end has a name
41// authorized by the provided ACL for the request's label, or the request
42// corresponds to a self-RPC.
43func NewACLAuthorizer(acl security.ACL) security.Authorizer { return aclAuthorizer(acl) }
44
45// fileACLAuthorizer implements Authorizer.
46type fileACLAuthorizer string
47
48// Authorize reads and decodes the fileACLAuthorizer's ACL file into a ACL and
49// then verifies the request according to an aclAuthorizer based on the ACL. If
50// reading or decoding the file fails then no requests are authorized.
51func (a fileACLAuthorizer) Authorize(ctx security.Context) error {
52 acl, err := loadACLFromFile(string(a))
53 if err != nil {
54 return err
55 }
56 return aclAuthorizer(acl).Authorize(ctx)
57}
58
59// NewFileACLAuthorizer creates an authorizer from the provided path to a file
60// containing a JSON-encoded ACL. Each call to "Authorize" involves reading and
61// decoding a ACL from the file and then authorizing the request according to the
62// ACL. The authorizer monitors the file so out of band changes to the contents of
63// the file are reflected in the ACL. If reading or decoding the file fails then
64// no requests are authorized.
65//
66// The JSON-encoding of a ACL is essentially a JSON object describing a map from
Asim Shankar6bc64582014-08-27 12:51:42 -070067// BlessingPatterns to encoded LabelSets (see LabelSet.MarshalJSON).
Tilak Sharma3ed30242014-08-11 11:45:55 -070068// Examples:
Asim Shankar4cf06752014-09-29 16:52:23 -070069// * `{"In": {"..." : "RW"}}` encodes an ACL that allows all principals to access all methods with
Tilak Sharma3ed30242014-08-11 11:45:55 -070070// ReadLabel or WriteLabel.
Asim Shankar4cf06752014-09-29 16:52:23 -070071// * `{"In":{"veyron/alice": "RW", "veyron/bob/...": "R"}}` encodes an ACL that allows all principals
72// matched by "veyron/alice" to access methods with ReadLabel or WriteLabel, and all
73// principals matched by "veyron/bob/..." to access methods with ReadLabel.
74// * `{"In": {"...": "RW"}, "NotIn": {"veyron/alice": "W"}}` encodes an ACL that allows all principals
75// access to all ReadLabel or WriteLabel methods, EXCEPT that methods with a WriteLabel are not
76// accessible to veyron/alice and her delegates.
Suharsh Sivakumar4c041db2014-09-04 13:19:05 -070077// (Also see BlessingPattern.MatchedBy)
Tilak Sharma3ed30242014-08-11 11:45:55 -070078//
79// TODO(ataly, ashankar): Instead of reading the file on each call we should use the "inotify"
80// mechanism to watch the file. Eventually we should also support ACLs stored in the Veyron
81// store.
82func NewFileACLAuthorizer(filePath string) security.Authorizer { return fileACLAuthorizer(filePath) }
83
Asim Shankarbf6263f2014-10-01 12:32:30 -070084func matchesACL(blessings []string, label security.Label, acl security.ACL) error {
85 if len(blessings) == 0 && acl.CanAccess("", label) {
86 // No blessings, check if that satisfies the ACL (it will be if AllPrincipals appears in the ACL).
87 return nil
Tilak Sharma3ed30242014-08-11 11:45:55 -070088 }
Asim Shankarbf6263f2014-10-01 12:32:30 -070089 for _, b := range blessings {
90 if acl.CanAccess(b, label) {
Tilak Sharmad6ade0e2014-08-20 16:28:32 -070091 return nil
92 }
Tilak Sharma3ed30242014-08-11 11:45:55 -070093 }
94 return errACL
95}
96
97func loadACLFromFile(filePath string) (security.ACL, error) {
98 f, err := os.Open(filePath)
99 if err != nil {
Tilak Sharmab88a1112014-08-15 17:17:12 -0700100 return nullACL, err
Tilak Sharma3ed30242014-08-11 11:45:55 -0700101 }
102 defer f.Close()
Tilak Sharmad6ade0e2014-08-20 16:28:32 -0700103 return LoadACL(f)
Tilak Sharma3ed30242014-08-11 11:45:55 -0700104}