Adds support to ACL package for session policies.

This commit is contained in:
James Phillips 2016-12-12 20:20:28 -08:00
parent 99e810f9c7
commit 60d4322c49
No known key found for this signature in database
GPG Key ID: 77183E682AC5FC11
4 changed files with 215 additions and 0 deletions

View File

@ -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)
}

View File

@ -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)
}
}
}

View File

@ -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) {

View File

@ -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)