mirror of https://github.com/status-im/consul.git
acl: support for user events
This commit is contained in:
parent
0381e1a253
commit
0c624350eb
55
acl/acl.go
55
acl/acl.go
|
@ -52,6 +52,12 @@ type ACL interface {
|
|||
// ServiceRead checks for permission to read a given service
|
||||
ServiceRead(string) bool
|
||||
|
||||
// EventRead determines if a specific event can be queried.
|
||||
EventRead(string) bool
|
||||
|
||||
// EventWrite determines if a specific event may be fired.
|
||||
EventWrite(string) bool
|
||||
|
||||
// ACLList checks for permission to list all the ACLs
|
||||
ACLList() bool
|
||||
|
||||
|
@ -87,6 +93,14 @@ func (s *StaticACL) ServiceWrite(string) bool {
|
|||
return s.defaultAllow
|
||||
}
|
||||
|
||||
func (s *StaticACL) EventRead(string) bool {
|
||||
return s.defaultAllow
|
||||
}
|
||||
|
||||
func (s *StaticACL) EventWrite(string) bool {
|
||||
return s.defaultAllow
|
||||
}
|
||||
|
||||
func (s *StaticACL) ACLList() bool {
|
||||
return s.allowManage
|
||||
}
|
||||
|
@ -136,6 +150,9 @@ type PolicyACL struct {
|
|||
|
||||
// serviceRules contains the service policies
|
||||
serviceRules *radix.Tree
|
||||
|
||||
// eventRules contains the user event policies
|
||||
eventRules *radix.Tree
|
||||
}
|
||||
|
||||
// New is used to construct a policy based ACL from a set of policies
|
||||
|
@ -145,6 +162,7 @@ func New(parent ACL, policy *Policy) (*PolicyACL, error) {
|
|||
parent: parent,
|
||||
keyRules: radix.New(),
|
||||
serviceRules: radix.New(),
|
||||
eventRules: radix.New(),
|
||||
}
|
||||
|
||||
// Load the key policy
|
||||
|
@ -156,6 +174,12 @@ func New(parent ACL, policy *Policy) (*PolicyACL, error) {
|
|||
for _, sp := range policy.Services {
|
||||
p.serviceRules.Insert(sp.Name, sp.Policy)
|
||||
}
|
||||
|
||||
// Load the event policy
|
||||
for _, ep := range policy.Events {
|
||||
p.eventRules.Insert(ep.Event, ep.Policy)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
|
@ -266,6 +290,37 @@ func (p *PolicyACL) ServiceWrite(name string) bool {
|
|||
return p.parent.ServiceWrite(name)
|
||||
}
|
||||
|
||||
// EventRead is used to determine if the policy allows for a
|
||||
// specific user event to be read.
|
||||
func (p *PolicyACL) EventRead(name string) bool {
|
||||
// Longest-prefix match on event names
|
||||
if _, rule, ok := p.eventRules.LongestPrefix(name); ok {
|
||||
switch rule {
|
||||
case EventPolicyRead:
|
||||
return true
|
||||
case EventPolicyWrite:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing matched, use parent
|
||||
return p.parent.EventRead(name)
|
||||
}
|
||||
|
||||
// EventWrite is used to determine if new events can be created
|
||||
// (fired) by the policy.
|
||||
func (p *PolicyACL) EventWrite(name string) bool {
|
||||
// Longest-prefix match event names
|
||||
if _, rule, ok := p.eventRules.LongestPrefix(name); ok {
|
||||
return rule == EventPolicyWrite
|
||||
}
|
||||
|
||||
// No match, use parent
|
||||
return p.parent.EventWrite(name)
|
||||
}
|
||||
|
||||
// ACLList checks if listing of ACLs is allowed
|
||||
func (p *PolicyACL) ACLList() bool {
|
||||
return p.parent.ACLList()
|
||||
|
|
|
@ -66,6 +66,18 @@ func TestStaticACL(t *testing.T) {
|
|||
if none.ServiceWrite("foobar") {
|
||||
t.Fatalf("should not allow")
|
||||
}
|
||||
if none.EventRead("foobar") {
|
||||
t.Fatalf("should not allow")
|
||||
}
|
||||
if none.EventRead("") {
|
||||
t.Fatalf("should not allow")
|
||||
}
|
||||
if none.EventWrite("foobar") {
|
||||
t.Fatalf("should not allow")
|
||||
}
|
||||
if none.EventWrite("") {
|
||||
t.Fatalf("should not allow")
|
||||
}
|
||||
if none.ACLList() {
|
||||
t.Fatalf("should not noneow")
|
||||
}
|
||||
|
@ -132,6 +144,20 @@ func TestPolicyACL(t *testing.T) {
|
|||
Policy: ServicePolicyWrite,
|
||||
},
|
||||
},
|
||||
Events: []*EventPolicy{
|
||||
&EventPolicy{
|
||||
Event: "",
|
||||
Policy: EventPolicyRead,
|
||||
},
|
||||
&EventPolicy{
|
||||
Event: "foo",
|
||||
Policy: EventPolicyWrite,
|
||||
},
|
||||
&EventPolicy{
|
||||
Event: "bar",
|
||||
Policy: EventPolicyDeny,
|
||||
},
|
||||
},
|
||||
}
|
||||
acl, err := New(all, policy)
|
||||
if err != nil {
|
||||
|
@ -188,6 +214,27 @@ func TestPolicyACL(t *testing.T) {
|
|||
t.Fatalf("Write fail: %#v", c)
|
||||
}
|
||||
}
|
||||
|
||||
type eventcase struct {
|
||||
inp string
|
||||
read bool
|
||||
write bool
|
||||
}
|
||||
eventcases := []eventcase{
|
||||
{"foo", true, true},
|
||||
{"foobar", true, true},
|
||||
{"bar", false, false},
|
||||
{"barbaz", false, false},
|
||||
{"baz", true, false},
|
||||
}
|
||||
for _, c := range eventcases {
|
||||
if c.read != acl.EventRead(c.inp) {
|
||||
t.Fatalf("Event fail: %#v", c)
|
||||
}
|
||||
if c.write != acl.EventWrite(c.inp) {
|
||||
t.Fatalf("Event fail: %#v", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPolicyACL_Parent(t *testing.T) {
|
||||
|
|
|
@ -13,6 +13,9 @@ const (
|
|||
ServicePolicyDeny = "deny"
|
||||
ServicePolicyRead = "read"
|
||||
ServicePolicyWrite = "write"
|
||||
EventPolicyRead = "read"
|
||||
EventPolicyWrite = "write"
|
||||
EventPolicyDeny = "deny"
|
||||
)
|
||||
|
||||
// Policy is used to represent the policy specified by
|
||||
|
@ -21,6 +24,7 @@ type Policy struct {
|
|||
ID string `hcl:"-"`
|
||||
Keys []*KeyPolicy `hcl:"key,expand"`
|
||||
Services []*ServicePolicy `hcl:"service,expand"`
|
||||
Events []*EventPolicy `hcl:"event,expand"`
|
||||
}
|
||||
|
||||
// KeyPolicy represents a policy for a key
|
||||
|
@ -43,6 +47,16 @@ func (k *ServicePolicy) GoString() string {
|
|||
return fmt.Sprintf("%#v", *k)
|
||||
}
|
||||
|
||||
// EventPolicy represents a user event policy.
|
||||
type EventPolicy struct {
|
||||
Event string `hcl:",key"`
|
||||
Policy string
|
||||
}
|
||||
|
||||
func (e *EventPolicy) GoString() string {
|
||||
return fmt.Sprintf("%#v", *e)
|
||||
}
|
||||
|
||||
// Parse is used to parse the specified ACL rules into an
|
||||
// intermediary set of policies, before being compiled into
|
||||
// the ACL
|
||||
|
@ -80,5 +94,16 @@ func Parse(rules string) (*Policy, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Validate the user event policies
|
||||
for _, ep := range p.Events {
|
||||
switch ep.Policy {
|
||||
case EventPolicyRead:
|
||||
case EventPolicyWrite:
|
||||
case EventPolicyDeny:
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid event policy: %#v", ep)
|
||||
}
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
|
|
@ -24,6 +24,15 @@ service "" {
|
|||
}
|
||||
service "foo" {
|
||||
policy = "read"
|
||||
}
|
||||
event "" {
|
||||
policy = "read"
|
||||
}
|
||||
event "foo" {
|
||||
policy = "write"
|
||||
}
|
||||
event "bar" {
|
||||
policy = "deny"
|
||||
}
|
||||
`
|
||||
exp := &Policy{
|
||||
|
@ -55,6 +64,20 @@ service "foo" {
|
|||
Policy: ServicePolicyRead,
|
||||
},
|
||||
},
|
||||
Events: []*EventPolicy{
|
||||
&EventPolicy{
|
||||
Event: "",
|
||||
Policy: EventPolicyRead,
|
||||
},
|
||||
&EventPolicy{
|
||||
Event: "foo",
|
||||
Policy: EventPolicyWrite,
|
||||
},
|
||||
&EventPolicy{
|
||||
Event: "bar",
|
||||
Policy: EventPolicyDeny,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
out, err := Parse(inp)
|
||||
|
@ -90,6 +113,17 @@ func TestParse_JSON(t *testing.T) {
|
|||
"foo": {
|
||||
"policy": "read"
|
||||
}
|
||||
},
|
||||
"event": {
|
||||
"": {
|
||||
"policy": "read"
|
||||
},
|
||||
"foo": {
|
||||
"policy": "write"
|
||||
},
|
||||
"bar": {
|
||||
"policy": "deny"
|
||||
}
|
||||
}
|
||||
}`
|
||||
exp := &Policy{
|
||||
|
@ -121,6 +155,20 @@ func TestParse_JSON(t *testing.T) {
|
|||
Policy: ServicePolicyRead,
|
||||
},
|
||||
},
|
||||
Events: []*EventPolicy{
|
||||
&EventPolicy{
|
||||
Event: "",
|
||||
Policy: EventPolicyRead,
|
||||
},
|
||||
&EventPolicy{
|
||||
Event: "foo",
|
||||
Policy: EventPolicyWrite,
|
||||
},
|
||||
&EventPolicy{
|
||||
Event: "bar",
|
||||
Policy: EventPolicyDeny,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
out, err := Parse(inp)
|
||||
|
|
Loading…
Reference in New Issue