Convert map to bitmap

This commit is contained in:
Jeff Mitchell 2016-01-12 17:08:10 -05:00
parent da87d490eb
commit 87fba5dad0
3 changed files with 67 additions and 62 deletions

View File

@ -5,24 +5,6 @@ import (
"github.com/hashicorp/vault/logical"
)
var (
// permittedPolicyLevel is used to map each logical operation
// into the set of capabilities that allow the operation.
permittedPolicyLevels = map[logical.Operation][]string{
logical.CreateOperation: []string{CreateCapability},
logical.ReadOperation: []string{ReadCapability},
logical.UpdateOperation: []string{UpdateCapability},
logical.DeleteOperation: []string{DeleteCapability},
logical.ListOperation: []string{ListCapability},
logical.RevokeOperation: []string{UpdateCapability},
logical.RenewOperation: []string{UpdateCapability},
logical.RollbackOperation: []string{UpdateCapability},
// Help is special-cased to always be allowed, so we don't need anything in this list
logical.HelpOperation: []string{},
}
)
// ACL is used to wrap a set of policies to provide
// an efficient interface for access control.
type ACL struct {
@ -71,11 +53,11 @@ func NewACL(policies []*Policy) (*ACL, error) {
existing := raw.(*PathCapabilities)
switch {
case existing.CapabilitiesMap[DenyCapability]:
case existing.CapabilitiesBitmap&DenyCapabilityInt > 0:
// If we are explicitly denied in the existing capability set,
// don't save anything else
case pc.CapabilitiesMap[DenyCapability]:
case pc.CapabilitiesBitmap&DenyCapabilityInt > 0:
// If this new policy explicitly denies, only save the deny value
tree.Insert(pc.Prefix, pc)
@ -83,18 +65,17 @@ func NewACL(policies []*Policy) (*ACL, error) {
// Insert the capabilities in this new policy into the existing
// value; since it's a pointer we can just modify the
// underlying data
for k, _ := range pc.CapabilitiesMap {
existing.CapabilitiesMap[k] = true
}
existing.CapabilitiesBitmap |= pc.CapabilitiesBitmap
}
}
}
return a, nil
}
// AllowOperation is used to check if the given operation is permitted
func (a *ACL) AllowOperation(op logical.Operation, path string) (opAllowed bool, sudoPriv bool) {
// AllowOperation is used to check if the given operation is permitted. The
// first bool indicates if an op is allowed, the second whether sudo priviliges
// exist for that op and path.
func (a *ACL) AllowOperation(op logical.Operation, path string) (allowed bool, sudo bool) {
// Fast-path root
if a.root {
return true, true
@ -125,5 +106,26 @@ CHECK:
// Check if the minimum permissions are met
// If "deny" has been explicitly set, only deny will be in the map, so we
// only need to check for the existence of other values
return policy.CapabilitiesMap[op.String()], policy.CapabilitiesMap[SudoCapability]
sudo = policy.CapabilitiesBitmap&SudoCapabilityInt > 0
switch op.String() {
case "read":
allowed = policy.CapabilitiesBitmap&ReadCapabilityInt > 0
case "list":
allowed = policy.CapabilitiesBitmap&ListCapabilityInt > 0
case "update":
allowed = policy.CapabilitiesBitmap&UpdateCapabilityInt > 0
case "delete":
allowed = policy.CapabilitiesBitmap&DeleteCapabilityInt > 0
case "create":
allowed = policy.CapabilitiesBitmap&CreateCapabilityInt > 0
case "revoke":
allowed = policy.CapabilitiesBitmap&UpdateCapabilityInt > 0
case "renew":
allowed = policy.CapabilitiesBitmap&UpdateCapabilityInt > 0
case "rollback":
allowed = policy.CapabilitiesBitmap&UpdateCapabilityInt > 0
default:
return false, false
}
return
}

View File

@ -8,12 +8,12 @@ import (
)
const (
DenyCapability = "deny"
CreateCapability = "create"
ReadCapability = "read"
UpdateCapability = "update"
DeleteCapability = "delete"
ListCapability = "list"
DenyCapability = "deny"
SudoCapability = "sudo"
// Backwards compatibility
@ -23,6 +23,28 @@ const (
OldSudoPathPolicy = "sudo"
)
const (
DenyCapabilityInt uint32 = 1 << iota
CreateCapabilityInt
ReadCapabilityInt
UpdateCapabilityInt
DeleteCapabilityInt
ListCapabilityInt
SudoCapabilityInt
)
var (
cap2Int = map[string]uint32{
DenyCapability: DenyCapabilityInt,
CreateCapability: CreateCapabilityInt,
ReadCapability: ReadCapabilityInt,
UpdateCapability: UpdateCapabilityInt,
DeleteCapability: DeleteCapabilityInt,
ListCapability: ListCapabilityInt,
SudoCapability: SudoCapabilityInt,
}
)
// Policy is used to represent the policy specified by
// an ACL configuration.
type Policy struct {
@ -33,11 +55,11 @@ type Policy struct {
// Capability represents a policy for a path in the namespace
type PathCapabilities struct {
Prefix string `hcl:",key"`
Policy string
Capabilities []string
CapabilitiesMap map[string]bool `hcl:"-"`
Glob bool
Prefix string `hcl:",key"`
Policy string
Capabilities []string
CapabilitiesBitmap uint32 `hcl:"-"`
Glob bool
}
// Parse is used to parse the specified ACL rules into an
@ -61,7 +83,7 @@ func Parse(rules string) (*Policy, error) {
// Map old-style policies into capabilities
switch pc.Policy {
case OldDenyPathPolicy:
pc.Capabilities = append(pc.Capabilities, DenyCapability)
pc.Capabilities = []string{DenyCapability}
case OldReadPathPolicy:
pc.Capabilities = append(pc.Capabilities, []string{ReadCapability, ListCapability}...)
case OldWritePathPolicy:
@ -71,18 +93,16 @@ func Parse(rules string) (*Policy, error) {
}
// Initialize the map
pc.CapabilitiesMap = make(map[string]bool, len(pc.Capabilities))
pc.CapabilitiesBitmap = 0
for _, cap := range pc.Capabilities {
switch cap {
// If it's deny, don't include any other capability
case DenyCapability:
pc.Capabilities = []string{DenyCapability}
pc.CapabilitiesMap = map[string]bool{
DenyCapability: true,
}
pc.CapabilitiesBitmap = DenyCapabilityInt
goto PathFinished
case CreateCapability, ReadCapability, UpdateCapability, DeleteCapability, ListCapability, SudoCapability:
pc.CapabilitiesMap[cap] = true
pc.CapabilitiesBitmap |= cap2Int[cap]
default:
return nil, fmt.Errorf("Invalid capability: %#v", pc)
}

View File

@ -20,9 +20,7 @@ func TestPolicy_Parse(t *testing.T) {
&PathCapabilities{"", "deny",
[]string{
"deny",
}, map[string]bool{
"deny": true,
}, true},
}, DenyCapabilityInt, true},
&PathCapabilities{"stage/", "sudo",
[]string{
"create",
@ -31,38 +29,23 @@ func TestPolicy_Parse(t *testing.T) {
"delete",
"list",
"sudo",
}, map[string]bool{
"create": true,
"read": true,
"update": true,
"delete": true,
"list": true,
"sudo": true,
}, true},
}, CreateCapabilityInt | ReadCapabilityInt | UpdateCapabilityInt |
DeleteCapabilityInt | ListCapabilityInt | SudoCapabilityInt, true},
&PathCapabilities{"prod/version", "read",
[]string{
"read",
"list",
}, map[string]bool{
"read": true,
"list": true,
}, false},
}, ReadCapabilityInt | ListCapabilityInt, false},
&PathCapabilities{"foo/bar", "read",
[]string{
"read",
"list",
}, map[string]bool{
"read": true,
"list": true,
}, false},
}, ReadCapabilityInt | ListCapabilityInt, false},
&PathCapabilities{"foo/bar", "",
[]string{
"create",
"sudo",
}, map[string]bool{
"create": true,
"sudo": true,
}, false},
}, CreateCapabilityInt | SudoCapabilityInt, false},
}
if !reflect.DeepEqual(p.Paths, expect) {
ret := fmt.Sprintf("bad:\nexpected:\n")