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