mirror of https://github.com/status-im/consul.git
Adds support to ACL package for session policies.
This commit is contained in:
parent
99e810f9c7
commit
60d4322c49
60
acl/acl.go
60
acl/acl.go
|
@ -95,6 +95,13 @@ type ACL interface {
|
|||
// service
|
||||
ServiceWrite(string) bool
|
||||
|
||||
// SessionRead checks for permission to read sessions for a given node.
|
||||
SessionRead(string) bool
|
||||
|
||||
// SessionWrite checks for permission to create sessions for a given
|
||||
// node.
|
||||
SessionWrite(string) bool
|
||||
|
||||
// Snapshot checks for permission to take and restore snapshots.
|
||||
Snapshot() bool
|
||||
}
|
||||
|
@ -175,6 +182,14 @@ func (s *StaticACL) ServiceWrite(string) bool {
|
|||
return s.defaultAllow
|
||||
}
|
||||
|
||||
func (s *StaticACL) SessionRead(string) bool {
|
||||
return s.defaultAllow
|
||||
}
|
||||
|
||||
func (s *StaticACL) SessionWrite(string) bool {
|
||||
return s.defaultAllow
|
||||
}
|
||||
|
||||
func (s *StaticACL) Snapshot() bool {
|
||||
return s.allowManage
|
||||
}
|
||||
|
@ -224,6 +239,9 @@ type PolicyACL struct {
|
|||
// serviceRules contains the service policies
|
||||
serviceRules *radix.Tree
|
||||
|
||||
// sessionRules contains the session policies
|
||||
sessionRules *radix.Tree
|
||||
|
||||
// eventRules contains the user event policies
|
||||
eventRules *radix.Tree
|
||||
|
||||
|
@ -247,6 +265,7 @@ func New(parent ACL, policy *Policy) (*PolicyACL, error) {
|
|||
keyRules: radix.New(),
|
||||
nodeRules: radix.New(),
|
||||
serviceRules: radix.New(),
|
||||
sessionRules: radix.New(),
|
||||
eventRules: radix.New(),
|
||||
preparedQueryRules: radix.New(),
|
||||
}
|
||||
|
@ -266,6 +285,11 @@ func New(parent ACL, policy *Policy) (*PolicyACL, error) {
|
|||
p.serviceRules.Insert(sp.Name, sp.Policy)
|
||||
}
|
||||
|
||||
// Load the session policy
|
||||
for _, sp := range policy.Sessions {
|
||||
p.sessionRules.Insert(sp.Node, sp.Policy)
|
||||
}
|
||||
|
||||
// Load the event policy
|
||||
for _, ep := range policy.Events {
|
||||
p.eventRules.Insert(ep.Event, ep.Policy)
|
||||
|
@ -547,3 +571,39 @@ func (p *PolicyACL) ServiceWrite(name string) bool {
|
|||
// No matching rule, use the parent.
|
||||
return p.parent.ServiceWrite(name)
|
||||
}
|
||||
|
||||
// SessionRead checks for permission to read sessions for a given node.
|
||||
func (p *PolicyACL) SessionRead(node string) bool {
|
||||
// Check for an exact rule or catch-all
|
||||
_, rule, ok := p.sessionRules.LongestPrefix(node)
|
||||
|
||||
if ok {
|
||||
switch rule {
|
||||
case PolicyRead, PolicyWrite:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// No matching rule, use the parent.
|
||||
return p.parent.SessionRead(node)
|
||||
}
|
||||
|
||||
// SessionWrite checks for permission to create sessions for a given node.
|
||||
func (p *PolicyACL) SessionWrite(node string) bool {
|
||||
// Check for an exact rule or catch-all
|
||||
_, rule, ok := p.sessionRules.LongestPrefix(node)
|
||||
|
||||
if ok {
|
||||
switch rule {
|
||||
case PolicyWrite:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// No matching rule, use the parent.
|
||||
return p.parent.SessionWrite(node)
|
||||
}
|
||||
|
|
101
acl/acl_test.go
101
acl/acl_test.go
|
@ -83,6 +83,12 @@ func TestStaticACL(t *testing.T) {
|
|||
if !all.ServiceWrite("foobar") {
|
||||
t.Fatalf("should allow")
|
||||
}
|
||||
if !all.SessionRead("foobar") {
|
||||
t.Fatalf("should allow")
|
||||
}
|
||||
if !all.SessionWrite("foobar") {
|
||||
t.Fatalf("should allow")
|
||||
}
|
||||
if all.Snapshot() {
|
||||
t.Fatalf("should not allow")
|
||||
}
|
||||
|
@ -141,6 +147,12 @@ func TestStaticACL(t *testing.T) {
|
|||
if none.ServiceWrite("foobar") {
|
||||
t.Fatalf("should not allow")
|
||||
}
|
||||
if none.SessionRead("foobar") {
|
||||
t.Fatalf("should not allow")
|
||||
}
|
||||
if none.SessionWrite("foobar") {
|
||||
t.Fatalf("should not allow")
|
||||
}
|
||||
if none.Snapshot() {
|
||||
t.Fatalf("should not allow")
|
||||
}
|
||||
|
@ -193,6 +205,12 @@ func TestStaticACL(t *testing.T) {
|
|||
if !manage.ServiceWrite("foobar") {
|
||||
t.Fatalf("should allow")
|
||||
}
|
||||
if !manage.SessionRead("foobar") {
|
||||
t.Fatalf("should allow")
|
||||
}
|
||||
if !manage.SessionWrite("foobar") {
|
||||
t.Fatalf("should allow")
|
||||
}
|
||||
if !manage.Snapshot() {
|
||||
t.Fatalf("should allow")
|
||||
}
|
||||
|
@ -661,3 +679,86 @@ func TestPolicyACL_Node(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPolicyACL_Session(t *testing.T) {
|
||||
deny := DenyAll()
|
||||
policyRoot := &Policy{
|
||||
Sessions: []*SessionPolicy{
|
||||
&SessionPolicy{
|
||||
Node: "root-nope",
|
||||
Policy: PolicyDeny,
|
||||
},
|
||||
&SessionPolicy{
|
||||
Node: "root-ro",
|
||||
Policy: PolicyRead,
|
||||
},
|
||||
&SessionPolicy{
|
||||
Node: "root-rw",
|
||||
Policy: PolicyWrite,
|
||||
},
|
||||
&SessionPolicy{
|
||||
Node: "override",
|
||||
Policy: PolicyDeny,
|
||||
},
|
||||
},
|
||||
}
|
||||
root, err := New(deny, policyRoot)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
policy := &Policy{
|
||||
Sessions: []*SessionPolicy{
|
||||
&SessionPolicy{
|
||||
Node: "child-nope",
|
||||
Policy: PolicyDeny,
|
||||
},
|
||||
&SessionPolicy{
|
||||
Node: "child-ro",
|
||||
Policy: PolicyRead,
|
||||
},
|
||||
&SessionPolicy{
|
||||
Node: "child-rw",
|
||||
Policy: PolicyWrite,
|
||||
},
|
||||
&SessionPolicy{
|
||||
Node: "override",
|
||||
Policy: PolicyWrite,
|
||||
},
|
||||
},
|
||||
}
|
||||
acl, err := New(root, policy)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
type sessioncase struct {
|
||||
inp string
|
||||
read bool
|
||||
write bool
|
||||
}
|
||||
cases := []sessioncase{
|
||||
{"nope", false, false},
|
||||
{"root-nope", false, false},
|
||||
{"root-ro", true, false},
|
||||
{"root-rw", true, true},
|
||||
{"root-nope-prefix", false, false},
|
||||
{"root-ro-prefix", true, false},
|
||||
{"root-rw-prefix", true, true},
|
||||
{"child-nope", false, false},
|
||||
{"child-ro", true, false},
|
||||
{"child-rw", true, true},
|
||||
{"child-nope-prefix", false, false},
|
||||
{"child-ro-prefix", true, false},
|
||||
{"child-rw-prefix", true, true},
|
||||
{"override", true, true},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if c.read != acl.SessionRead(c.inp) {
|
||||
t.Fatalf("Read fail: %#v", c)
|
||||
}
|
||||
if c.write != acl.SessionWrite(c.inp) {
|
||||
t.Fatalf("Write fail: %#v", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ type Policy struct {
|
|||
Keys []*KeyPolicy `hcl:"key,expand"`
|
||||
Nodes []*NodePolicy `hcl:"node,expand"`
|
||||
Services []*ServicePolicy `hcl:"service,expand"`
|
||||
Sessions []*SessionPolicy `hcl:"session,expand"`
|
||||
Events []*EventPolicy `hcl:"event,expand"`
|
||||
PreparedQueries []*PreparedQueryPolicy `hcl:"query,expand"`
|
||||
Keyring string `hcl:"keyring"`
|
||||
|
@ -55,6 +56,17 @@ func (s *ServicePolicy) GoString() string {
|
|||
return fmt.Sprintf("%#v", *s)
|
||||
}
|
||||
|
||||
// SessionPolicy represents a policy for making sessions tied to specific node
|
||||
// name prefixes.
|
||||
type SessionPolicy struct {
|
||||
Node string `hcl:",key"`
|
||||
Policy string
|
||||
}
|
||||
|
||||
func (s *SessionPolicy) GoString() string {
|
||||
return fmt.Sprintf("%#v", *s)
|
||||
}
|
||||
|
||||
// EventPolicy represents a user event policy.
|
||||
type EventPolicy struct {
|
||||
Event string `hcl:",key"`
|
||||
|
@ -125,6 +137,13 @@ func Parse(rules string) (*Policy, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Validate the session policies
|
||||
for _, sp := range p.Sessions {
|
||||
if !isPolicyValid(sp.Policy) {
|
||||
return nil, fmt.Errorf("Invalid session policy: %#v", sp)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the user event policies
|
||||
for _, ep := range p.Events {
|
||||
if !isPolicyValid(ep.Policy) {
|
||||
|
|
|
@ -46,6 +46,12 @@ service "" {
|
|||
service "foo" {
|
||||
policy = "read"
|
||||
}
|
||||
session "foo" {
|
||||
policy = "write"
|
||||
}
|
||||
session "bar" {
|
||||
policy = "deny"
|
||||
}
|
||||
query "" {
|
||||
policy = "read"
|
||||
}
|
||||
|
@ -129,6 +135,16 @@ query "bar" {
|
|||
Policy: PolicyRead,
|
||||
},
|
||||
},
|
||||
Sessions: []*SessionPolicy{
|
||||
&SessionPolicy{
|
||||
Node: "foo",
|
||||
Policy: PolicyWrite,
|
||||
},
|
||||
&SessionPolicy{
|
||||
Node: "bar",
|
||||
Policy: PolicyDeny,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
out, err := Parse(inp)
|
||||
|
@ -199,6 +215,14 @@ func TestACLPolicy_Parse_JSON(t *testing.T) {
|
|||
"foo": {
|
||||
"policy": "read"
|
||||
}
|
||||
},
|
||||
"session": {
|
||||
"foo": {
|
||||
"policy": "write"
|
||||
},
|
||||
"bar": {
|
||||
"policy": "deny"
|
||||
}
|
||||
}
|
||||
}`
|
||||
exp := &Policy{
|
||||
|
@ -274,6 +298,16 @@ func TestACLPolicy_Parse_JSON(t *testing.T) {
|
|||
Policy: PolicyRead,
|
||||
},
|
||||
},
|
||||
Sessions: []*SessionPolicy{
|
||||
&SessionPolicy{
|
||||
Node: "foo",
|
||||
Policy: PolicyWrite,
|
||||
},
|
||||
&SessionPolicy{
|
||||
Node: "bar",
|
||||
Policy: PolicyDeny,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
out, err := Parse(inp)
|
||||
|
@ -331,6 +365,7 @@ func TestACLPolicy_Bad_Policy(t *testing.T) {
|
|||
`operator = "nope"`,
|
||||
`query "" { policy = "nope" }`,
|
||||
`service "" { policy = "nope" }`,
|
||||
`session "" { policy = "nope" }`,
|
||||
}
|
||||
for _, c := range cases {
|
||||
_, err := Parse(c)
|
||||
|
|
Loading…
Reference in New Issue