blob: 4ab195c8bbee08bc390ae903d14cd016dbfc5d82 [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 Shankar7cf29002014-10-09 00:38:37 -070032 if ctx.LocalID() != nil && ctx.RemoteID() != nil && reflect.DeepEqual(ctx.LocalID(), ctx.RemoteID()) {
33 return nil
Tilak Sharma3ed30242014-08-11 11:45:55 -070034 }
Asim Shankarbf6263f2014-10-01 12:32:30 -070035 var blessings []string
36 if ctx.RemoteBlessings() != nil {
37 blessings = ctx.RemoteBlessings().ForContext(ctx)
38 } else if ctx.RemoteID() != nil {
39 blessings = ctx.RemoteID().Names()
40 }
Asim Shankarbf6263f2014-10-01 12:32:30 -070041 return matchesACL(blessings, ctx.Label(), security.ACL(a))
Tilak Sharma3ed30242014-08-11 11:45:55 -070042}
43
44// NewACLAuthorizer creates an authorizer from the provided ACL. The
45// authorizer authorizes a request iff the identity at the remote end has a name
46// authorized by the provided ACL for the request's label, or the request
47// corresponds to a self-RPC.
48func NewACLAuthorizer(acl security.ACL) security.Authorizer { return aclAuthorizer(acl) }
49
50// fileACLAuthorizer implements Authorizer.
51type fileACLAuthorizer string
52
53// Authorize reads and decodes the fileACLAuthorizer's ACL file into a ACL and
54// then verifies the request according to an aclAuthorizer based on the ACL. If
55// reading or decoding the file fails then no requests are authorized.
56func (a fileACLAuthorizer) Authorize(ctx security.Context) error {
57 acl, err := loadACLFromFile(string(a))
58 if err != nil {
59 return err
60 }
61 return aclAuthorizer(acl).Authorize(ctx)
62}
63
64// NewFileACLAuthorizer creates an authorizer from the provided path to a file
65// containing a JSON-encoded ACL. Each call to "Authorize" involves reading and
66// decoding a ACL from the file and then authorizing the request according to the
67// ACL. The authorizer monitors the file so out of band changes to the contents of
68// the file are reflected in the ACL. If reading or decoding the file fails then
69// no requests are authorized.
70//
71// The JSON-encoding of a ACL is essentially a JSON object describing a map from
Asim Shankar6bc64582014-08-27 12:51:42 -070072// BlessingPatterns to encoded LabelSets (see LabelSet.MarshalJSON).
Tilak Sharma3ed30242014-08-11 11:45:55 -070073// Examples:
Asim Shankar4cf06752014-09-29 16:52:23 -070074// * `{"In": {"..." : "RW"}}` encodes an ACL that allows all principals to access all methods with
Tilak Sharma3ed30242014-08-11 11:45:55 -070075// ReadLabel or WriteLabel.
Asim Shankar4cf06752014-09-29 16:52:23 -070076// * `{"In":{"veyron/alice": "RW", "veyron/bob/...": "R"}}` encodes an ACL that allows all principals
77// matched by "veyron/alice" to access methods with ReadLabel or WriteLabel, and all
78// principals matched by "veyron/bob/..." to access methods with ReadLabel.
79// * `{"In": {"...": "RW"}, "NotIn": {"veyron/alice": "W"}}` encodes an ACL that allows all principals
80// access to all ReadLabel or WriteLabel methods, EXCEPT that methods with a WriteLabel are not
81// accessible to veyron/alice and her delegates.
Suharsh Sivakumar4c041db2014-09-04 13:19:05 -070082// (Also see BlessingPattern.MatchedBy)
Tilak Sharma3ed30242014-08-11 11:45:55 -070083//
84// TODO(ataly, ashankar): Instead of reading the file on each call we should use the "inotify"
85// mechanism to watch the file. Eventually we should also support ACLs stored in the Veyron
86// store.
87func NewFileACLAuthorizer(filePath string) security.Authorizer { return fileACLAuthorizer(filePath) }
88
Asim Shankarbf6263f2014-10-01 12:32:30 -070089func matchesACL(blessings []string, label security.Label, acl security.ACL) error {
90 if len(blessings) == 0 && acl.CanAccess("", label) {
91 // No blessings, check if that satisfies the ACL (it will be if AllPrincipals appears in the ACL).
92 return nil
Tilak Sharma3ed30242014-08-11 11:45:55 -070093 }
Asim Shankarbf6263f2014-10-01 12:32:30 -070094 for _, b := range blessings {
95 if acl.CanAccess(b, label) {
Tilak Sharmad6ade0e2014-08-20 16:28:32 -070096 return nil
97 }
Tilak Sharma3ed30242014-08-11 11:45:55 -070098 }
99 return errACL
100}
101
102func loadACLFromFile(filePath string) (security.ACL, error) {
103 f, err := os.Open(filePath)
104 if err != nil {
Tilak Sharmab88a1112014-08-15 17:17:12 -0700105 return nullACL, err
Tilak Sharma3ed30242014-08-11 11:45:55 -0700106 }
107 defer f.Close()
Tilak Sharmad6ade0e2014-08-20 16:28:32 -0700108 return LoadACL(f)
Tilak Sharma3ed30242014-08-11 11:45:55 -0700109}