Skip to content

Commit

Permalink
move job attachment fields on ACLPolicy out to their own struct
Browse files Browse the repository at this point in the history
  • Loading branch information
tgross committed Aug 22, 2022
1 parent df45144 commit 22920e1
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 78 deletions.
24 changes: 15 additions & 9 deletions api/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,21 @@ type ACLPolicyListStub struct {

// ACLPolicy is used to represent an ACL policy
type ACLPolicy struct {
Name string
Description string
Rules string
JobNamespace string
JobID string
Group string
Task string
CreateIndex uint64
ModifyIndex uint64
Name string
Description string
Rules string
JobACL *JobACL

CreateIndex uint64
ModifyIndex uint64
}

// JobACL represents an ACL policy's attachment to a job, group, or task.
type JobACL struct {
Namespace string
JobID string
Group string
Task string
}

// ACLToken represents a client token which is used to Authenticate
Expand Down
10 changes: 5 additions & 5 deletions command/acl_bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,12 @@ func formatACLPolicy(policy *api.ACLPolicy) string {

formattedOut := formatKV(output)

if policy.JobNamespace != "" {
if policy.JobACL != nil {
output := []string{
fmt.Sprintf("Namespace|%v", policy.JobNamespace),
fmt.Sprintf("JobID|%v", policy.JobID),
fmt.Sprintf("Group|%v", policy.Group),
fmt.Sprintf("Task|%v", policy.Task),
fmt.Sprintf("Namespace|%v", policy.JobACL.Namespace),
fmt.Sprintf("JobID|%v", policy.JobACL.JobID),
fmt.Sprintf("Group|%v", policy.JobACL.Group),
fmt.Sprintf("Task|%v", policy.JobACL.Task),
}
formattedOut += "\n\n[bold]Associated Workload[reset]\n"
formattedOut += formatKV(output)
Expand Down
18 changes: 11 additions & 7 deletions command/acl_policy_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,17 @@ func (c *ACLPolicyApplyCommand) Run(args []string) int {

// Construct the policy
ap := &api.ACLPolicy{
Name: policyName,
Description: description,
Rules: string(rawPolicy),
JobNamespace: namespace,
JobID: jobID,
Group: group,
Task: task,
Name: policyName,
Description: description,
Rules: string(rawPolicy),
}
if namespace != "" {
ap.JobACL = &api.JobACL{
Namespace: namespace,
JobID: jobID,
Group: group,
Task: task,
}
}

// Get the HTTP client
Expand Down
11 changes: 7 additions & 4 deletions nomad/acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,18 @@ func (s *Server) resolvePoliciesForClaims(claims *structs.IdentityClaims) ([]*st
break
}
policy := raw.(*structs.ACLPolicy)
if policy.JobACL == nil {
continue
}

switch {
case policy.Group == "":
case policy.JobACL.Group == "":
policies = append(policies, policy)
case policy.Group != alloc.TaskGroup:
case policy.JobACL.Group != alloc.TaskGroup:
continue // don't bother checking task
case policy.Task == "":
case policy.JobACL.Task == "":
policies = append(policies, policy)
case policy.Task == claims.TaskName:
case policy.JobACL.Task == claims.TaskName:
policies = append(policies, policy)
}
}
Expand Down
54 changes: 34 additions & 20 deletions nomad/acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,44 +158,58 @@ func TestResolveClaims(t *testing.T) {

// policy for job
policy1 := mock.ACLPolicy()
policy1.JobNamespace = claims.Namespace
policy1.JobID = claims.JobID
policy1.JobACL = &structs.JobACL{
Namespace: claims.Namespace,
JobID: claims.JobID,
}

// policy for job and group
policy2 := mock.ACLPolicy()
policy2.JobNamespace = claims.Namespace
policy2.JobID = claims.JobID
policy2.Group = alloc.Job.TaskGroups[0].Name
policy2.JobACL = &structs.JobACL{
Namespace: claims.Namespace,
JobID: claims.JobID,
Group: alloc.Job.TaskGroups[0].Name,
}

// policy for job and group and task
policy3 := mock.ACLPolicy()
policy3.JobNamespace = claims.Namespace
policy3.JobID = claims.JobID
policy3.Group = alloc.Job.TaskGroups[0].Name
policy3.Task = claims.TaskName
policy3.JobACL = &structs.JobACL{
Namespace: claims.Namespace,
JobID: claims.JobID,
Group: alloc.Job.TaskGroups[0].Name,
Task: claims.TaskName,
}

// policy for job and group but different task
policy4 := mock.ACLPolicy()
policy4.JobNamespace = claims.Namespace
policy4.JobID = claims.JobID
policy4.Group = alloc.Job.TaskGroups[0].Name
policy4.Task = "another"
policy4.JobACL = &structs.JobACL{
Namespace: claims.Namespace,
JobID: claims.JobID,
Group: alloc.Job.TaskGroups[0].Name,
Task: "another",
}

// policy for job but different group
policy5 := mock.ACLPolicy()
policy5.JobNamespace = claims.Namespace
policy5.JobID = claims.JobID
policy5.Group = "another"
policy5.JobACL = &structs.JobACL{
Namespace: claims.Namespace,
JobID: claims.JobID,
Group: "another",
}

// policy for same namespace but different job
policy6 := mock.ACLPolicy()
policy6.JobNamespace = claims.Namespace
policy6.JobID = "another"
policy6.JobACL = &structs.JobACL{
Namespace: claims.Namespace,
JobID: "another",
}

// policy for same job in different namespace
policy7 := mock.ACLPolicy()
policy7.JobNamespace = "another"
policy7.JobID = claims.JobID
policy7.JobACL = &structs.JobACL{
Namespace: "another",
JobID: claims.JobID,
}

index++
err := store.UpsertACLPolicies(structs.MsgTypeTestSetup, index, []*structs.ACLPolicy{
Expand Down
8 changes: 5 additions & 3 deletions nomad/secure_variables_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,11 @@ func TestSecureVariablesEndpoint_auth(t *testing.T) {
path "nomad/jobs/*" { capabilities = ["list"] }
path "other/path" { capabilities = ["read"] }
}}`
policy.JobNamespace = ns
policy.JobID = jobID
policy.Group = alloc1.TaskGroup
policy.JobACL = &structs.JobACL{
Namespace: ns,
JobID: jobID,
Group: alloc1.TaskGroup,
}
policy.SetHash()
err = store.UpsertACLPolicies(structs.MsgTypeTestSetup, 1100, []*structs.ACLPolicy{policy})
must.NoError(t, err)
Expand Down
75 changes: 65 additions & 10 deletions nomad/state/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -779,21 +779,76 @@ func aclPolicyTableSchema() *memdb.TableSchema {
Name: "job",
AllowMissing: true,
Unique: false,
Indexer: &memdb.CompoundIndex{
Indexes: []memdb.Indexer{
&memdb.StringFieldIndex{
Field: "JobNamespace",
},
&memdb.StringFieldIndex{
Field: "JobID",
},
},
},
Indexer: &ACLPolicyJobACLFieldIndex{},
},
},
}
}

// ACLPolicyJobACLFieldIndex is used to extract the policy's JobACL field and
// build an index on it.
type ACLPolicyJobACLFieldIndex struct{}

// FromObject is used to extract an index value from an
// object or to indicate that the index value is missing.
func (a *ACLPolicyJobACLFieldIndex) FromObject(obj interface{}) (bool, []byte, error) {
policy, ok := obj.(*structs.ACLPolicy)
if !ok {
return false, nil, fmt.Errorf("object %#v is not an ACLPolicy", obj)
}

if policy.JobACL == nil {
return false, nil, nil
}

ns := policy.JobACL.Namespace
if ns == "" {
return false, nil, nil
}
jobID := policy.JobACL.JobID
if jobID == "" {
return false, nil, fmt.Errorf(
"object %#v is not a valid ACLPolicy: JobACL.JobID without Namespace", obj)
}

val := ns + "\x00" + jobID + "\x00"
return true, []byte(val), nil
}

// FromArgs is used to build an exact index lookup based on arguments
func (a *ACLPolicyJobACLFieldIndex) FromArgs(args ...interface{}) ([]byte, error) {
if len(args) != 2 {
return nil, fmt.Errorf("must provide two arguments")
}
arg0, ok := args[0].(string)
if !ok {
return nil, fmt.Errorf("argument must be a string: %#v", args[0])
}
arg1, ok := args[1].(string)
if !ok {
return nil, fmt.Errorf("argument must be a string: %#v", args[0])
}

// Add the null character as a terminator
arg0 += "\x00" + arg1 + "\x00"
return []byte(arg0), nil
}

// PrefixFromArgs returns a prefix that should be used for scanning based on the arguments
func (a *ACLPolicyJobACLFieldIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
val, err := a.FromArgs(args...)
if err != nil {
return nil, err
}

// Strip the null terminator, the rest is a prefix
n := len(val)
if n > 0 {
return val[:n-1], nil
}
return val, nil
}

// aclTokenTableSchema returns the MemDB schema for the tokens table.
// This table is used to store the bearer tokens which are used to authenticate
func aclTokenTableSchema() *memdb.TableSchema {
Expand Down
49 changes: 29 additions & 20 deletions nomad/structs/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -11718,17 +11718,21 @@ type ACLPolicy struct {
Description string // Human readable
Rules string // HCL or JSON format
RulesJSON *acl.Policy // Generated from Rules on read
JobACL *JobACL
Hash []byte

JobNamespace string // namespace of the job this policy is attached to
JobID string // ID of the job this policy is attached to
Group string // ID of the group this policy is attached to
Task string // ID of the task this policy is attached to

CreateIndex uint64
ModifyIndex uint64
}

// JobACL represents an ACL policy's attachment to a job, group, or task.
type JobACL struct {
Namespace string // namespace of the job
JobID string // ID of the job
Group string // ID of the group
Task string // ID of the task
}

// SetHash is used to compute and set the hash of the ACL policy
func (a *ACLPolicy) SetHash() []byte {
// Initialize a 256bit Blake2 hash (32 bytes)
Expand All @@ -11741,10 +11745,13 @@ func (a *ACLPolicy) SetHash() []byte {
_, _ = hash.Write([]byte(a.Name))
_, _ = hash.Write([]byte(a.Description))
_, _ = hash.Write([]byte(a.Rules))
_, _ = hash.Write([]byte(a.JobNamespace))
_, _ = hash.Write([]byte(a.JobID))
_, _ = hash.Write([]byte(a.Group))
_, _ = hash.Write([]byte(a.Task))

if a.JobACL != nil {
_, _ = hash.Write([]byte(a.JobACL.Namespace))
_, _ = hash.Write([]byte(a.JobACL.JobID))
_, _ = hash.Write([]byte(a.JobACL.Group))
_, _ = hash.Write([]byte(a.JobACL.Task))
}

// Finalize the hash
hashVal := hash.Sum(nil)
Expand Down Expand Up @@ -11778,17 +11785,19 @@ func (a *ACLPolicy) Validate() error {
err := fmt.Errorf("description longer than %d", maxPolicyDescriptionLength)
mErr.Errors = append(mErr.Errors, err)
}
if a.JobNamespace == "" && a.JobID != "" {
err := fmt.Errorf("namespace must be set to set job ID")
mErr.Errors = append(mErr.Errors, err)
}
if a.JobID == "" && a.Group != "" {
err := fmt.Errorf("job ID must be set to set group")
mErr.Errors = append(mErr.Errors, err)
}
if a.Group == "" && a.Task != "" {
err := fmt.Errorf("group must be set to set task")
mErr.Errors = append(mErr.Errors, err)
if a.JobACL != nil {
if a.JobACL.Namespace == "" && a.JobACL.JobID != "" {
err := fmt.Errorf("namespace must be set to set job ID")
mErr.Errors = append(mErr.Errors, err)
}
if a.JobACL.JobID == "" && a.JobACL.Group != "" {
err := fmt.Errorf("job ID must be set to set group")
mErr.Errors = append(mErr.Errors, err)
}
if a.JobACL.Group == "" && a.JobACL.Task != "" {
err := fmt.Errorf("group must be set to set task")
mErr.Errors = append(mErr.Errors, err)
}
}

return mErr.ErrorOrNil()
Expand Down

0 comments on commit 22920e1

Please sign in to comment.