d29d3412d8
This adds new OIDC endpoints on the RPC endpoint. These two RPCs handle generating the OIDC provider URL and then completing the login by exchanging the provider token with an internal Nomad token. The RPC endpoints both do double forwarding. The initial forward is to ensure we are talking to the regional leader; the second then takes into account whether the auth method generates local or global tokens. If it creates global tokens, we must then forward onto the federated regional leader.
265 lines
7.5 KiB
Go
265 lines
7.5 KiB
Go
package mock
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hashicorp/nomad/helper/uuid"
|
|
testing "github.com/mitchellh/go-testing-interface"
|
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// StateStore defines the methods required from state.StateStore but avoids a
|
|
// circular dependency.
|
|
type StateStore interface {
|
|
UpsertACLPolicies(msgType structs.MessageType, index uint64, policies []*structs.ACLPolicy) error
|
|
UpsertACLTokens(msgType structs.MessageType, index uint64, tokens []*structs.ACLToken) error
|
|
}
|
|
|
|
// NamespacePolicy is a helper for generating the policy hcl for a given
|
|
// namespace. Either policy or capabilities may be nil but not both.
|
|
func NamespacePolicy(namespace string, policy string, capabilities []string) string {
|
|
policyHCL := fmt.Sprintf("namespace %q {", namespace)
|
|
if policy != "" {
|
|
policyHCL += fmt.Sprintf("\n\tpolicy = %q", policy)
|
|
}
|
|
if len(capabilities) != 0 {
|
|
for i, s := range capabilities {
|
|
if !strings.HasPrefix(s, "\"") {
|
|
capabilities[i] = strconv.Quote(s)
|
|
}
|
|
}
|
|
|
|
policyHCL += fmt.Sprintf("\n\tcapabilities = [%v]", strings.Join(capabilities, ","))
|
|
}
|
|
policyHCL += "\n}"
|
|
return policyHCL
|
|
}
|
|
|
|
// NamespacePolicyWithVariables is a helper for generating the policy hcl for a given
|
|
// namespace. Either policy or capabilities may be nil but not both.
|
|
func NamespacePolicyWithVariables(namespace string, policy string, capabilities []string, svars map[string][]string) string {
|
|
policyHCL := fmt.Sprintf("namespace %q {", namespace)
|
|
if policy != "" {
|
|
policyHCL += fmt.Sprintf("\n\tpolicy = %q", policy)
|
|
}
|
|
if len(capabilities) != 0 {
|
|
for i, s := range capabilities {
|
|
if !strings.HasPrefix(s, "\"") {
|
|
capabilities[i] = strconv.Quote(s)
|
|
}
|
|
}
|
|
policyHCL += fmt.Sprintf("\n\tcapabilities = [%v]", strings.Join(capabilities, ","))
|
|
}
|
|
|
|
policyHCL += VariablePolicy(svars)
|
|
policyHCL += "\n}"
|
|
return policyHCL
|
|
}
|
|
|
|
// VariablePolicy is a helper for generating the policy hcl for a given
|
|
// variable block inside of a namespace.
|
|
func VariablePolicy(svars map[string][]string) string {
|
|
policyHCL := ""
|
|
if len(svars) > 0 {
|
|
policyHCL = "\n\n\tvariables {"
|
|
for p, c := range svars {
|
|
for i, s := range c {
|
|
if !strings.HasPrefix(s, "\"") {
|
|
c[i] = strconv.Quote(s)
|
|
}
|
|
}
|
|
policyHCL += fmt.Sprintf("\n\t\tpath %q { capabilities = [%v]}", p, strings.Join(c, ","))
|
|
}
|
|
policyHCL += "\n\t}"
|
|
}
|
|
return policyHCL
|
|
}
|
|
|
|
// HostVolumePolicy is a helper for generating the policy hcl for a given
|
|
// host-volume. Either policy or capabilities may be nil but not both.
|
|
func HostVolumePolicy(vol string, policy string, capabilities []string) string {
|
|
policyHCL := fmt.Sprintf("host_volume %q {", vol)
|
|
if policy != "" {
|
|
policyHCL += fmt.Sprintf("\n\tpolicy = %q", policy)
|
|
}
|
|
if len(capabilities) != 0 {
|
|
for i, s := range capabilities {
|
|
if !strings.HasPrefix(s, "\"") {
|
|
capabilities[i] = strconv.Quote(s)
|
|
}
|
|
}
|
|
|
|
policyHCL += fmt.Sprintf("\n\tcapabilities = [%v]", strings.Join(capabilities, ","))
|
|
}
|
|
policyHCL += "\n}"
|
|
return policyHCL
|
|
}
|
|
|
|
// AgentPolicy is a helper for generating the hcl for a given agent policy.
|
|
func AgentPolicy(policy string) string {
|
|
return fmt.Sprintf("agent {\n\tpolicy = %q\n}\n", policy)
|
|
}
|
|
|
|
// NodePolicy is a helper for generating the hcl for a given node policy.
|
|
func NodePolicy(policy string) string {
|
|
return fmt.Sprintf("node {\n\tpolicy = %q\n}\n", policy)
|
|
}
|
|
|
|
// QuotaPolicy is a helper for generating the hcl for a given quota policy.
|
|
func QuotaPolicy(policy string) string {
|
|
return fmt.Sprintf("quota {\n\tpolicy = %q\n}\n", policy)
|
|
}
|
|
|
|
// PluginPolicy is a helper for generating the hcl for a given plugin policy.
|
|
func PluginPolicy(policy string) string {
|
|
return fmt.Sprintf("plugin {\n\tpolicy = %q\n}\n", policy)
|
|
}
|
|
|
|
// CreatePolicy creates a policy with the given name and rule.
|
|
func CreatePolicy(t testing.T, state StateStore, index uint64, name, rule string) {
|
|
t.Helper()
|
|
|
|
// Create the ACLPolicy
|
|
policy := &structs.ACLPolicy{
|
|
Name: name,
|
|
Rules: rule,
|
|
}
|
|
policy.SetHash()
|
|
assert.Nil(t, state.UpsertACLPolicies(structs.MsgTypeTestSetup, index, []*structs.ACLPolicy{policy}))
|
|
}
|
|
|
|
// CreateToken creates a local, client token for the given policies
|
|
func CreateToken(t testing.T, state StateStore, index uint64, policies []string) *structs.ACLToken {
|
|
t.Helper()
|
|
|
|
// Create the ACLToken
|
|
token := ACLToken()
|
|
token.Policies = policies
|
|
token.SetHash()
|
|
assert.Nil(t, state.UpsertACLTokens(structs.MsgTypeTestSetup, index, []*structs.ACLToken{token}))
|
|
return token
|
|
}
|
|
|
|
// CreatePolicyAndToken creates a policy and then returns a token configured for
|
|
// just that policy. CreatePolicyAndToken uses the given index and index+1.
|
|
func CreatePolicyAndToken(t testing.T, state StateStore, index uint64, name, rule string) *structs.ACLToken {
|
|
CreatePolicy(t, state, index, name, rule)
|
|
return CreateToken(t, state, index+1, []string{name})
|
|
}
|
|
|
|
func ACLRole() *structs.ACLRole {
|
|
role := structs.ACLRole{
|
|
ID: uuid.Generate(),
|
|
Name: fmt.Sprintf("acl-role-%s", uuid.Short()),
|
|
Description: "mocked-test-acl-role",
|
|
Policies: []*structs.ACLRolePolicyLink{
|
|
{Name: "mocked-test-policy-1"},
|
|
{Name: "mocked-test-policy-2"},
|
|
},
|
|
CreateIndex: 10,
|
|
ModifyIndex: 10,
|
|
}
|
|
role.SetHash()
|
|
return &role
|
|
}
|
|
|
|
func ACLPolicy() *structs.ACLPolicy {
|
|
ap := &structs.ACLPolicy{
|
|
Name: fmt.Sprintf("policy-%s", uuid.Generate()),
|
|
Description: "Super cool policy!",
|
|
Rules: `
|
|
namespace "default" {
|
|
policy = "write"
|
|
}
|
|
node {
|
|
policy = "read"
|
|
}
|
|
agent {
|
|
policy = "read"
|
|
}
|
|
`,
|
|
CreateIndex: 10,
|
|
ModifyIndex: 20,
|
|
}
|
|
ap.SetHash()
|
|
return ap
|
|
}
|
|
|
|
func ACLToken() *structs.ACLToken {
|
|
tk := &structs.ACLToken{
|
|
AccessorID: uuid.Generate(),
|
|
SecretID: uuid.Generate(),
|
|
Name: "my cool token " + uuid.Generate(),
|
|
Type: "client",
|
|
Policies: []string{"foo", "bar"},
|
|
Global: false,
|
|
CreateTime: time.Now().UTC(),
|
|
CreateIndex: 10,
|
|
ModifyIndex: 20,
|
|
}
|
|
tk.SetHash()
|
|
return tk
|
|
}
|
|
|
|
func ACLManagementToken() *structs.ACLToken {
|
|
return &structs.ACLToken{
|
|
AccessorID: uuid.Generate(),
|
|
SecretID: uuid.Generate(),
|
|
Name: "management " + uuid.Generate(),
|
|
Type: "management",
|
|
Global: true,
|
|
CreateTime: time.Now().UTC(),
|
|
CreateIndex: 10,
|
|
ModifyIndex: 20,
|
|
}
|
|
}
|
|
|
|
func ACLAuthMethod() *structs.ACLAuthMethod {
|
|
maxTokenTTL, _ := time.ParseDuration("3600s")
|
|
method := structs.ACLAuthMethod{
|
|
Name: fmt.Sprintf("acl-auth-method-%s", uuid.Short()),
|
|
Type: "OIDC",
|
|
TokenLocality: "local",
|
|
MaxTokenTTL: maxTokenTTL,
|
|
Default: false,
|
|
Config: &structs.ACLAuthMethodConfig{
|
|
OIDCDiscoveryURL: "http://example.com",
|
|
OIDCClientID: "mock",
|
|
OIDCClientSecret: "very secret secret",
|
|
OIDCScopes: []string{"groups"},
|
|
BoundAudiences: []string{"audience1", "audience2"},
|
|
AllowedRedirectURIs: []string{"foo", "bar"},
|
|
DiscoveryCaPem: []string{"foo"},
|
|
SigningAlgs: []string{"bar"},
|
|
ClaimMappings: map[string]string{"foo": "bar"},
|
|
ListClaimMappings: map[string]string{"foo": "bar"},
|
|
},
|
|
CreateTime: time.Now().UTC(),
|
|
CreateIndex: 10,
|
|
ModifyIndex: 10,
|
|
}
|
|
method.SetHash()
|
|
method.Canonicalize()
|
|
return &method
|
|
}
|
|
|
|
func ACLBindingRule() *structs.ACLBindingRule {
|
|
return &structs.ACLBindingRule{
|
|
ID: uuid.Short(),
|
|
Description: "mocked-acl-binding-rule",
|
|
AuthMethod: "auth0",
|
|
Selector: "engineering in list.roles",
|
|
BindType: "role",
|
|
BindName: "eng-ro",
|
|
CreateTime: time.Now().UTC(),
|
|
ModifyTime: time.Now().UTC(),
|
|
CreateIndex: 10,
|
|
ModifyIndex: 10,
|
|
}
|
|
}
|