sso: allow binding rules to create management ACL tokens. (#15860)
* sso: allow binding rules to create management ACL tokens. * docs: update binding rule docs to detail management type addition.
This commit is contained in:
parent
0bb408bc10
commit
5d33891910
|
@ -799,8 +799,8 @@ type ACLBindingRule struct {
|
|||
Selector string
|
||||
|
||||
// BindType adjusts how this binding rule is applied at login time. The
|
||||
// valid values are ACLBindingRuleBindTypeRole and
|
||||
// ACLBindingRuleBindTypePolicy.
|
||||
// valid values are ACLBindingRuleBindTypeRole,
|
||||
// ACLBindingRuleBindTypePolicy, and ACLBindingRuleBindTypeManagement.
|
||||
BindType string
|
||||
|
||||
// BindName is the target of the binding. Can be lightly templated using
|
||||
|
@ -826,6 +826,10 @@ const (
|
|||
// within the ACLBindingRule.BindName parameter, and will be the policy
|
||||
// name.
|
||||
ACLBindingRuleBindTypePolicy = "policy"
|
||||
|
||||
// ACLBindingRuleBindTypeManagement is the ACL binding rule bind type that
|
||||
// will generate management ACL tokens when matched.
|
||||
ACLBindingRuleBindTypeManagement = "management"
|
||||
)
|
||||
|
||||
// ACLBindingRuleListStub is the stub object returned when performing a listing
|
||||
|
|
|
@ -53,11 +53,12 @@ Create Options:
|
|||
|
||||
-bind-type
|
||||
Specifies adjusts how this binding rule is applied at login time to internal
|
||||
Nomad objects. Valid options are "role" and "policy".
|
||||
Nomad objects. Valid options are "role", "policy", or "management".
|
||||
|
||||
-bind-name
|
||||
Specifies is the target of the binding used on selector match. This can be
|
||||
lightly templated using HIL ${foo} syntax.
|
||||
lightly templated using HIL ${foo} syntax. If the bind type is set to
|
||||
"management", this should not be set.
|
||||
|
||||
-json
|
||||
Output the ACL binding rule in a JSON format.
|
||||
|
@ -74,10 +75,14 @@ func (a *ACLBindingRuleCreateCommand) AutocompleteFlags() complete.Flags {
|
|||
"-description": complete.PredictAnything,
|
||||
"-auth-method": complete.PredictAnything,
|
||||
"-selector": complete.PredictAnything,
|
||||
"-bind-type": complete.PredictSet("role", "policy"),
|
||||
"-bind-name": complete.PredictAnything,
|
||||
"-json": complete.PredictNothing,
|
||||
"-t": complete.PredictAnything,
|
||||
"-bind-type": complete.PredictSet(
|
||||
api.ACLBindingRuleBindTypeRole,
|
||||
api.ACLBindingRuleBindTypePolicy,
|
||||
api.ACLBindingRuleBindTypeManagement,
|
||||
),
|
||||
"-bind-name": complete.PredictAnything,
|
||||
"-json": complete.PredictNothing,
|
||||
"-t": complete.PredictAnything,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -120,10 +125,6 @@ func (a *ACLBindingRuleCreateCommand) Run(args []string) int {
|
|||
a.Ui.Error("ACL binding rule auth method must be specified using the -auth-method flag")
|
||||
return 1
|
||||
}
|
||||
if a.bindName == "" {
|
||||
a.Ui.Error("ACL binding rule bind name must be specified using the -bind-name flag")
|
||||
return 1
|
||||
}
|
||||
if a.bindType == "" {
|
||||
a.Ui.Error("ACL binding rule bind type must be specified using the -bind-type flag")
|
||||
return 1
|
||||
|
|
|
@ -48,13 +48,6 @@ func TestACLBindingRuleCreateCommand_Run(t *testing.T) {
|
|||
ui.OutputWriter.Reset()
|
||||
ui.ErrorWriter.Reset()
|
||||
|
||||
must.Eq(t, 1, cmd.Run([]string{"-address=" + url, "-auth-method=auth0"}))
|
||||
must.StrContains(t, ui.ErrorWriter.String(),
|
||||
"ACL binding rule bind name must be specified using the -bind-name flag")
|
||||
|
||||
ui.OutputWriter.Reset()
|
||||
ui.ErrorWriter.Reset()
|
||||
|
||||
must.Eq(t, 1, cmd.Run([]string{"-address=" + url, "-auth-method=auth0", "-bind-name=engineering"}))
|
||||
must.StrContains(t, ui.ErrorWriter.String(),
|
||||
"ACL binding rule bind type must be specified using the -bind-type flag")
|
||||
|
|
|
@ -53,11 +53,12 @@ Update Options:
|
|||
|
||||
-bind-type
|
||||
Specifies adjusts how this binding rule is applied at login time to internal
|
||||
Nomad objects. Valid options are "role" and "policy".
|
||||
Nomad objects. Valid options are "role", "policy", or "management".
|
||||
|
||||
-bind-name
|
||||
Specifies is the target of the binding used on selector match. This can be
|
||||
lightly templated using HIL ${foo} syntax.
|
||||
lightly templated using HIL ${foo} syntax. If the bind type is set to
|
||||
management, this should not be set.
|
||||
|
||||
-json
|
||||
Output the ACL binding rule in a JSON format.
|
||||
|
@ -74,10 +75,14 @@ func (a *ACLBindingRuleUpdateCommand) AutocompleteFlags() complete.Flags {
|
|||
complete.Flags{
|
||||
"-description": complete.PredictAnything,
|
||||
"-selector": complete.PredictAnything,
|
||||
"-bind-type": complete.PredictSet("role", "policy"),
|
||||
"-bind-name": complete.PredictAnything,
|
||||
"-json": complete.PredictNothing,
|
||||
"-t": complete.PredictAnything,
|
||||
"-bind-type": complete.PredictSet(
|
||||
api.ACLBindingRuleBindTypeRole,
|
||||
api.ACLBindingRuleBindTypePolicy,
|
||||
api.ACLBindingRuleBindTypeManagement,
|
||||
),
|
||||
"-bind-name": complete.PredictAnything,
|
||||
"-json": complete.PredictNothing,
|
||||
"-t": complete.PredictAnything,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -138,10 +143,6 @@ func (a *ACLBindingRuleUpdateCommand) Run(args []string) int {
|
|||
switch a.noMerge {
|
||||
case true:
|
||||
|
||||
if a.bindName == "" {
|
||||
a.Ui.Error("ACL binding rule bind name must be specified using the -bind-name flag")
|
||||
return 1
|
||||
}
|
||||
if a.bindType == "" {
|
||||
a.Ui.Error("ACL binding rule bind type must be specified using the -bind-type flag")
|
||||
return 1
|
||||
|
@ -159,7 +160,7 @@ func (a *ACLBindingRuleUpdateCommand) Run(args []string) int {
|
|||
// Check that the operator specified at least one flag to update the ACL
|
||||
// binding rule with.
|
||||
if a.description == "" && a.selector == "" && a.bindType == "" && a.bindName == "" {
|
||||
a.Ui.Error("Please provide all required flags to update the ACL binding rule")
|
||||
a.Ui.Error("Please provide at least one update for the ACL binding rule")
|
||||
a.Ui.Error(commandErrorText(a))
|
||||
return 1
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ func TestACLBindingRuleUpdateCommand_Run(t *testing.T) {
|
|||
// Try a merge update without setting any parameters to update.
|
||||
code = cmd.Run([]string{"-address=" + url, "-token=" + rootACLToken.SecretID, aclBindingRule.ID})
|
||||
must.Eq(t, 1, code)
|
||||
must.StrContains(t, ui.ErrorWriter.String(), "Please provide all required flags to update the ACL binding rule")
|
||||
must.StrContains(t, ui.ErrorWriter.String(), "Please provide at least one update for the ACL binding rule")
|
||||
|
||||
ui.OutputWriter.Reset()
|
||||
ui.ErrorWriter.Reset()
|
||||
|
@ -95,15 +95,6 @@ func TestACLBindingRuleUpdateCommand_Run(t *testing.T) {
|
|||
ui.OutputWriter.Reset()
|
||||
ui.ErrorWriter.Reset()
|
||||
|
||||
// Try updating the role using no-merge without setting the required flags.
|
||||
code = cmd.Run([]string{"-address=" + url, "-token=" + rootACLToken.SecretID, "-no-merge", aclBindingRule.ID})
|
||||
must.Eq(t, 1, code)
|
||||
must.StrContains(t, ui.ErrorWriter.String(),
|
||||
"ACL binding rule bind name must be specified using the -bind-name flag")
|
||||
|
||||
ui.OutputWriter.Reset()
|
||||
ui.ErrorWriter.Reset()
|
||||
|
||||
code = cmd.Run([]string{
|
||||
"-address=" + url, "-token=" + rootACLToken.SecretID, "-no-merge", "-bind-name=engineering-updated", aclBindingRule.ID})
|
||||
must.Eq(t, 1, code)
|
||||
|
|
|
@ -35,8 +35,9 @@ type BinderStateStore interface {
|
|||
// Bindings contains the ACL roles and policies to be assigned to the created
|
||||
// token.
|
||||
type Bindings struct {
|
||||
Roles []*structs.ACLTokenRoleLink
|
||||
Policies []string
|
||||
Management bool
|
||||
Roles []*structs.ACLTokenRoleLink
|
||||
Policies []string
|
||||
}
|
||||
|
||||
// None indicates that the resulting bindings would not give the created token
|
||||
|
@ -110,6 +111,11 @@ func (b *Binder) Bind(authMethod *structs.ACLAuthMethod, identity *Identity) (*B
|
|||
if policy != nil {
|
||||
bindings.Policies = append(bindings.Policies, policy.Name)
|
||||
}
|
||||
case structs.ACLBindingRuleBindTypeManagement:
|
||||
bindings.Management = true
|
||||
bindings.Policies = nil
|
||||
bindings.Roles = nil
|
||||
return &bindings, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,6 +140,8 @@ func computeBindName(bindType, bindName string, claimMappings map[string]string)
|
|||
valid = structs.ValidPolicyName.MatchString(bindName)
|
||||
case structs.ACLBindingRuleBindTypeRole:
|
||||
valid = structs.ValidACLRoleName.MatchString(bindName)
|
||||
case structs.ACLManagementToken:
|
||||
valid = true
|
||||
default:
|
||||
return "", false, fmt.Errorf("unknown binding rule bind type: %s", bindType)
|
||||
}
|
||||
|
|
|
@ -58,6 +58,13 @@ func TestBinder_Bind(t *testing.T) {
|
|||
BindName: otherRole.Name,
|
||||
AuthMethod: authMethod.Name,
|
||||
},
|
||||
{
|
||||
ID: uuid.Generate(),
|
||||
Selector: "role==admin",
|
||||
BindType: structs.ACLBindingRuleBindTypeManagement,
|
||||
BindName: "",
|
||||
AuthMethod: authMethod.Name,
|
||||
},
|
||||
}
|
||||
must.NoError(t, testStore.UpsertACLBindingRules(0, bindingRules, true))
|
||||
|
||||
|
@ -69,16 +76,16 @@ func TestBinder_Bind(t *testing.T) {
|
|||
wantErr bool
|
||||
}{
|
||||
{
|
||||
"empty identity",
|
||||
authMethod,
|
||||
&Identity{},
|
||||
&Bindings{},
|
||||
false,
|
||||
name: "empty identity",
|
||||
authMethod: authMethod,
|
||||
identity: &Identity{},
|
||||
want: &Bindings{},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
"role",
|
||||
authMethod,
|
||||
&Identity{
|
||||
name: "role",
|
||||
authMethod: authMethod,
|
||||
identity: &Identity{
|
||||
Claims: map[string]string{
|
||||
"role": "engineer",
|
||||
"language": "go",
|
||||
|
@ -87,8 +94,20 @@ func TestBinder_Bind(t *testing.T) {
|
|||
"editor": "vim",
|
||||
},
|
||||
},
|
||||
&Bindings{Roles: []*structs.ACLTokenRoleLink{{ID: targetRole.ID}}},
|
||||
false,
|
||||
want: &Bindings{Roles: []*structs.ACLTokenRoleLink{{ID: targetRole.ID}}},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "management",
|
||||
authMethod: authMethod,
|
||||
identity: &Identity{
|
||||
Claims: map[string]string{
|
||||
"role": "admin",
|
||||
},
|
||||
ClaimMappings: map[string]string{},
|
||||
},
|
||||
want: &Bindings{Management: true},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
@ -116,22 +135,31 @@ func Test_computeBindName(t *testing.T) {
|
|||
wantErr bool
|
||||
}{
|
||||
{
|
||||
"valid bind name and type",
|
||||
structs.ACLBindingRuleBindTypeRole,
|
||||
"cluster-admin",
|
||||
map[string]string{"cluster-admin": "root"},
|
||||
"cluster-admin",
|
||||
true,
|
||||
false,
|
||||
name: "valid bind name and type",
|
||||
bindType: structs.ACLBindingRuleBindTypeRole,
|
||||
bindName: "cluster-admin",
|
||||
claimMappings: map[string]string{"cluster-admin": "root"},
|
||||
wantName: "cluster-admin",
|
||||
wantTrue: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
"invalid type",
|
||||
"amazing",
|
||||
"cluster-admin",
|
||||
map[string]string{"cluster-admin": "root"},
|
||||
"",
|
||||
false,
|
||||
true,
|
||||
name: "valid management",
|
||||
bindType: structs.ACLBindingRuleBindTypeManagement,
|
||||
bindName: "",
|
||||
claimMappings: map[string]string{"cluster-admin": "root"},
|
||||
wantName: "",
|
||||
wantTrue: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "invalid type",
|
||||
bindType: "amazing",
|
||||
bindName: "cluster-admin",
|
||||
claimMappings: map[string]string{"cluster-admin": "root"},
|
||||
wantName: "",
|
||||
wantTrue: false,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
|
|
@ -2693,7 +2693,7 @@ func (a *ACL) OIDCCompleteAuth(
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tokenBindings.None() {
|
||||
if tokenBindings.None() && !tokenBindings.Management {
|
||||
return structs.NewErrRPCCoded(http.StatusBadRequest, "no role or policy bindings matched")
|
||||
}
|
||||
|
||||
|
@ -2701,17 +2701,22 @@ func (a *ACL) OIDCCompleteAuth(
|
|||
// logic, so we do not want to call Raft directly or copy that here. In the
|
||||
// future we should try and extract out the logic into an interface, or at
|
||||
// least a separate function.
|
||||
token := structs.ACLToken{
|
||||
Name: "OIDC-" + authMethod.Name,
|
||||
Global: authMethod.TokenLocalityIsGlobal(),
|
||||
ExpirationTTL: authMethod.MaxTokenTTL,
|
||||
}
|
||||
|
||||
if tokenBindings.Management {
|
||||
token.Type = structs.ACLManagementToken
|
||||
} else {
|
||||
token.Type = structs.ACLClientToken
|
||||
token.Policies = tokenBindings.Policies
|
||||
token.Roles = tokenBindings.Roles
|
||||
}
|
||||
|
||||
tokenUpsertRequest := structs.ACLTokenUpsertRequest{
|
||||
Tokens: []*structs.ACLToken{
|
||||
{
|
||||
Name: "OIDC-" + authMethod.Name,
|
||||
Type: structs.ACLClientToken,
|
||||
Policies: tokenBindings.Policies,
|
||||
Roles: tokenBindings.Roles,
|
||||
Global: authMethod.TokenLocalityIsGlobal(),
|
||||
ExpirationTTL: authMethod.MaxTokenTTL,
|
||||
},
|
||||
},
|
||||
Tokens: []*structs.ACLToken{&token},
|
||||
WriteRequest: structs.WriteRequest{
|
||||
Region: a.srv.Region(),
|
||||
AuthToken: a.srv.getLeaderAcl(),
|
||||
|
|
|
@ -3701,4 +3701,35 @@ func TestACL_OIDCCompleteAuth(t *testing.T) {
|
|||
must.Len(t, 1, completeAuthResp4.ACLToken.Roles)
|
||||
must.Eq(t, mockACLRole.Name, completeAuthResp4.ACLToken.Roles[0].Name)
|
||||
must.Eq(t, mockACLRole.ID, completeAuthResp4.ACLToken.Roles[0].ID)
|
||||
|
||||
// Create a binding rule which generates management tokens. This should
|
||||
// override the other rules, giving us a management token when we next
|
||||
// log in.
|
||||
mockBindingRule3 := mock.ACLBindingRule()
|
||||
mockBindingRule3.AuthMethod = mockedAuthMethod.Name
|
||||
mockBindingRule3.BindType = structs.ACLBindingRuleBindTypeManagement
|
||||
mockBindingRule3.Selector = "engineering in list.policies"
|
||||
mockBindingRule3.BindName = ""
|
||||
|
||||
must.NoError(t, testServer.fsm.State().UpsertACLBindingRules(
|
||||
50, []*structs.ACLBindingRule{mockBindingRule3}, true))
|
||||
|
||||
completeAuthReq5 := structs.ACLOIDCCompleteAuthRequest{
|
||||
AuthMethodName: mockedAuthMethod.Name,
|
||||
ClientNonce: "fsSPuaodKevKfDU3IeXa",
|
||||
State: "st_someweirdstateid",
|
||||
Code: "codeABC",
|
||||
RedirectURI: mockedAuthMethod.Config.AllowedRedirectURIs[0],
|
||||
WriteRequest: structs.WriteRequest{
|
||||
Region: DefaultRegion,
|
||||
},
|
||||
}
|
||||
|
||||
var completeAuthResp5 structs.ACLOIDCCompleteAuthResponse
|
||||
err = msgpackrpc.CallWithCodec(codec, structs.ACLOIDCCompleteAuthRPCMethod, &completeAuthReq5, &completeAuthResp5)
|
||||
must.NoError(t, err)
|
||||
must.NotNil(t, completeAuthResp4.ACLToken)
|
||||
must.Len(t, 0, completeAuthResp5.ACLToken.Policies)
|
||||
must.Len(t, 0, completeAuthResp5.ACLToken.Roles)
|
||||
must.Eq(t, structs.ACLManagementToken, completeAuthResp5.ACLToken.Type)
|
||||
}
|
||||
|
|
|
@ -966,8 +966,8 @@ type ACLBindingRule struct {
|
|||
Selector string
|
||||
|
||||
// BindType adjusts how this binding rule is applied at login time. The
|
||||
// valid values are ACLBindingRuleBindTypeRole and
|
||||
// ACLBindingRuleBindTypePolicy.
|
||||
// valid values are ACLBindingRuleBindTypeRole,
|
||||
// ACLBindingRuleBindTypePolicy, and ACLBindingRuleBindTypeManagement.
|
||||
BindType string
|
||||
|
||||
// BindName is the target of the binding. Can be lightly templated using
|
||||
|
@ -998,6 +998,10 @@ const (
|
|||
// within the ACLBindingRule.BindName parameter, and will be the policy
|
||||
// name.
|
||||
ACLBindingRuleBindTypePolicy = "policy"
|
||||
|
||||
// ACLBindingRuleBindTypeManagement is the ACL binding rule bind type that
|
||||
// will generate management ACL tokens when matched.
|
||||
ACLBindingRuleBindTypeManagement = "management"
|
||||
)
|
||||
|
||||
// Canonicalize performs basic canonicalization on the ACL token object. It is
|
||||
|
@ -1028,21 +1032,26 @@ func (a *ACLBindingRule) Validate() error {
|
|||
if a.AuthMethod == "" {
|
||||
mErr.Errors = append(mErr.Errors, errors.New("auth method is missing"))
|
||||
}
|
||||
if a.BindName == "" {
|
||||
mErr.Errors = append(mErr.Errors, errors.New("bind name is missing"))
|
||||
}
|
||||
if len(a.Description) > maxACLBindingRuleDescriptionLength {
|
||||
mErr.Errors = append(mErr.Errors, fmt.Errorf("description longer than %d", maxACLRoleDescriptionLength))
|
||||
}
|
||||
|
||||
if a.BindType == "" {
|
||||
// Depending on the bind type, we have some specific validation. Catching
|
||||
// the empty string also provides easier to understand feedback to the
|
||||
// user.
|
||||
switch a.BindType {
|
||||
case "":
|
||||
mErr.Errors = append(mErr.Errors, errors.New("bind type is missing"))
|
||||
} else {
|
||||
switch a.BindType {
|
||||
case ACLBindingRuleBindTypeRole, ACLBindingRuleBindTypePolicy: // fall-through.
|
||||
default:
|
||||
mErr.Errors = append(mErr.Errors, fmt.Errorf("unsupported bind type: %q", a.BindType))
|
||||
case ACLBindingRuleBindTypeRole, ACLBindingRuleBindTypePolicy:
|
||||
if a.BindName == "" {
|
||||
mErr.Errors = append(mErr.Errors, errors.New("bind name is missing"))
|
||||
}
|
||||
case ACLBindingRuleBindTypeManagement:
|
||||
if a.BindName != "" {
|
||||
mErr.Errors = append(mErr.Errors, errors.New("bind name should be empty"))
|
||||
}
|
||||
default:
|
||||
mErr.Errors = append(mErr.Errors, fmt.Errorf("unsupported bind type: %q", a.BindType))
|
||||
}
|
||||
|
||||
return mErr.ErrorOrNil()
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/nomad/ci"
|
||||
"github.com/hashicorp/nomad/helper/pointer"
|
||||
"github.com/hashicorp/nomad/helper/uuid"
|
||||
|
@ -1192,25 +1194,115 @@ func TestACLBindingRule_Canonicalize(t *testing.T) {
|
|||
func TestACLBindingRule_Validate(t *testing.T) {
|
||||
ci.Parallel(t)
|
||||
|
||||
// Quite possibly the most invalid binding rule to have ever existed.
|
||||
totallyInvalidACLBindingRule := ACLBindingRule{
|
||||
Description: uuid.Generate() + uuid.Generate() + uuid.Generate() + uuid.Generate() +
|
||||
uuid.Generate() + uuid.Generate() + uuid.Generate() + uuid.Generate(),
|
||||
AuthMethod: "",
|
||||
BindType: "",
|
||||
BindName: "",
|
||||
testCases := []struct {
|
||||
name string
|
||||
inputACLBindingRule *ACLBindingRule
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "valid policy type rule",
|
||||
inputACLBindingRule: &ACLBindingRule{
|
||||
Description: "some short description",
|
||||
AuthMethod: "auth0",
|
||||
Selector: "group-name in list.groups",
|
||||
BindType: ACLBindingRuleBindTypePolicy,
|
||||
BindName: "some-policy-name",
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid policy type rule",
|
||||
inputACLBindingRule: &ACLBindingRule{
|
||||
Description: "some short description",
|
||||
AuthMethod: "auth0",
|
||||
Selector: "group-name in list.groups",
|
||||
BindType: ACLBindingRuleBindTypePolicy,
|
||||
BindName: "",
|
||||
},
|
||||
expectedError: &multierror.Error{
|
||||
Errors: []error{
|
||||
errors.New("bind name is missing"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid role type rule",
|
||||
inputACLBindingRule: &ACLBindingRule{
|
||||
Description: "some short description",
|
||||
AuthMethod: "auth0",
|
||||
Selector: "group-name in list.groups",
|
||||
BindType: ACLBindingRuleBindTypeRole,
|
||||
BindName: "some-role-name",
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid role type rule",
|
||||
inputACLBindingRule: &ACLBindingRule{
|
||||
Description: "some short description",
|
||||
AuthMethod: "auth0",
|
||||
Selector: "group-name in list.groups",
|
||||
BindType: ACLBindingRuleBindTypeRole,
|
||||
BindName: "",
|
||||
},
|
||||
expectedError: &multierror.Error{
|
||||
Errors: []error{
|
||||
errors.New("bind name is missing"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid management type rule",
|
||||
inputACLBindingRule: &ACLBindingRule{
|
||||
Description: "some short description",
|
||||
AuthMethod: "auth0",
|
||||
Selector: "group-name in list.groups",
|
||||
BindType: ACLBindingRuleBindTypeManagement,
|
||||
BindName: "",
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid management type rule",
|
||||
inputACLBindingRule: &ACLBindingRule{
|
||||
Description: "some short description",
|
||||
AuthMethod: "auth0",
|
||||
Selector: "group-name in list.groups",
|
||||
BindType: ACLBindingRuleBindTypeManagement,
|
||||
BindName: "some-name",
|
||||
},
|
||||
expectedError: &multierror.Error{
|
||||
Errors: []error{
|
||||
errors.New("bind name should be empty"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid all",
|
||||
inputACLBindingRule: &ACLBindingRule{
|
||||
Description: uuid.Generate() + uuid.Generate() + uuid.Generate() +
|
||||
uuid.Generate() + uuid.Generate() + uuid.Generate() +
|
||||
uuid.Generate() + uuid.Generate(),
|
||||
AuthMethod: "",
|
||||
Selector: "group-name in list.groups",
|
||||
BindType: "",
|
||||
BindName: "",
|
||||
},
|
||||
expectedError: &multierror.Error{
|
||||
Errors: []error{
|
||||
errors.New("auth method is missing"),
|
||||
errors.New("description longer than 256"),
|
||||
errors.New("bind type is missing"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
err := totallyInvalidACLBindingRule.Validate()
|
||||
must.StrContains(t, err.Error(), "auth method is missing")
|
||||
must.StrContains(t, err.Error(), "bind name is missing")
|
||||
must.StrContains(t, err.Error(), "description longer than 256")
|
||||
must.StrContains(t, err.Error(), "bind type is missing")
|
||||
|
||||
// Update the bind type, so we get the alternative error when this is not
|
||||
// empty, but incorrectly set.
|
||||
totallyInvalidACLBindingRule.BindType = "service"
|
||||
err = totallyInvalidACLBindingRule.Validate()
|
||||
must.StrContains(t, err.Error(), `unsupported bind type: "service"`)
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
must.Eq(t, tc.expectedError, tc.inputACLBindingRule.Validate())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestACLBindingRule_Merge(t *testing.T) {
|
||||
|
|
|
@ -130,11 +130,12 @@ The table below shows this endpoint's support for
|
|||
optional and when not set, provides a catch-all rule.
|
||||
|
||||
- `BindType` `(string: <required>)` - Adjusts how this binding rule is applied
|
||||
at login time. Valid values are `role` and `policy`.
|
||||
at login time. Valid values are `role`, `policy`, and `management`.
|
||||
|
||||
- `BindName` `(string: <required>)` - Target of the binding. Can be lightly
|
||||
templated using HIL ${foo} syntax from available field names. How it is used
|
||||
depends on the BindType.
|
||||
templated using HIL ${foo} syntax from available field names. If the bind
|
||||
type is set to `management`, this should not be set. How it is used depends
|
||||
on the BindType.
|
||||
|
||||
### Sample Payload
|
||||
|
||||
|
@ -202,11 +203,11 @@ queries](/nomad/api-docs#blocking-queries) and [required ACLs](/nomad/api-docs#a
|
|||
optional and when not set, provides a catch-all rule.
|
||||
|
||||
- `BindType` `(string: "")` - Adjusts how this binding rule is applied at login
|
||||
time. Valid values are `role` and `policy`.
|
||||
time. Valid values are `role`, `policy`, and `management`.
|
||||
|
||||
- `BindName` `(string: "")` - Target of the binding. Can be lightly templated
|
||||
using HIL ${foo} syntax from available field names. How it is used depends on
|
||||
the BindType.
|
||||
the BindType.
|
||||
|
||||
### Sample Payload
|
||||
|
||||
|
|
|
@ -33,10 +33,11 @@ via flags detailed below.
|
|||
attributes returned from the auth method during login.
|
||||
|
||||
- `-bind-type`: Specifies adjusts how this binding rule is applied at login time
|
||||
to internal Nomad objects. Valid options are `role` and `policy`.
|
||||
to internal Nomad objects. Valid options are `role`, `policy`, and `management`.
|
||||
|
||||
- `-bind-name`: Specifies is the target of the binding used on selector match.
|
||||
This can be lightly templated using HIL `${foo}` syntax.
|
||||
This can be lightly templated using HIL `${foo}` syntax. If the bind type is
|
||||
set to `management`, this should not be set.
|
||||
|
||||
- `-json`: Output the ACL binding-rule in a JSON format.
|
||||
|
||||
|
|
|
@ -29,10 +29,11 @@ The `acl binding-rule update` command requires an existing rule's ID.
|
|||
attributes returned from the binding rule during login.
|
||||
|
||||
- `-bind-type`: Specifies adjusts how this binding rule is applied at login time
|
||||
to internal Nomad objects. Valid options are `role` and `policy`.
|
||||
to internal Nomad objects. Valid options are `role`, `policy`, and `management`.
|
||||
|
||||
- `-bind-name`: Specifies is the target of the binding used on selector match.
|
||||
This can be lightly templated using HIL `${foo}` syntax.
|
||||
This can be lightly templated using HIL `${foo}` syntax. If the bind type is
|
||||
set to `management`, this should not be set.
|
||||
|
||||
- `-json`: Output the ACL binding-rule in a JSON format.
|
||||
|
||||
|
|
Loading…
Reference in New Issue