open-nomad/internal/testing/apitests/acl_test.go

185 lines
6.6 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package apitests
import (
"net/url"
"testing"
"time"
capOIDC "github.com/hashicorp/cap/oidc"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/ci"
"github.com/shoenig/test/must"
)
func TestACLOIDC_GetAuthURL(t *testing.T) {
ci.Parallel(t)
testClient, testServer, _ := makeACLClient(t, nil, nil)
defer testServer.Stop()
// Set up the test OIDC provider.
oidcTestProvider := capOIDC.StartTestProvider(t)
defer oidcTestProvider.Stop()
oidcTestProvider.SetAllowedRedirectURIs([]string{"http://127.0.0.1:4649/oidc/callback"})
// Generate and upsert an ACL auth method for use. Certain values must be
// taken from the cap OIDC provider just like real world use.
mockedAuthMethod := api.ACLAuthMethod{
Name: "api-test-auth-method",
Type: api.ACLAuthMethodTypeOIDC,
TokenLocality: api.ACLAuthMethodTokenLocalityGlobal,
MaxTokenTTL: 10 * time.Hour,
Default: true,
Config: &api.ACLAuthMethodConfig{
OIDCDiscoveryURL: oidcTestProvider.Addr(),
OIDCClientID: "mock",
OIDCClientSecret: "verysecretsecret",
BoundAudiences: []string{"mock"},
AllowedRedirectURIs: []string{"http://127.0.0.1:4649/oidc/callback"},
DiscoveryCaPem: []string{oidcTestProvider.CACert()},
SigningAlgs: []string{"ES256"},
ClaimMappings: map[string]string{"foo": "bar"},
ListClaimMappings: map[string]string{"foo": "bar"},
},
}
createdAuthMethod, writeMeta, err := testClient.ACLAuthMethods().Create(&mockedAuthMethod, nil)
must.NoError(t, err)
must.NotNil(t, createdAuthMethod)
assertWriteMeta(t, writeMeta)
// Generate and make the request.
authURLRequest := api.ACLOIDCAuthURLRequest{
AuthMethodName: createdAuthMethod.Name,
RedirectURI: createdAuthMethod.Config.AllowedRedirectURIs[0],
ClientNonce: "fpSPuaodKevKfDU3IeXb",
}
authURLResp, _, err := testClient.ACLAuth().GetAuthURL(&authURLRequest, nil)
must.NoError(t, err)
// The response URL comes encoded, so decode this and check we have each
// component we expect.
escapedURL, err := url.PathUnescape(authURLResp.AuthURL)
must.NoError(t, err)
must.StrContains(t, escapedURL, "/authorize?client_id=mock")
must.StrContains(t, escapedURL, "&nonce=fpSPuaodKevKfDU3IeXb")
must.StrContains(t, escapedURL, "&redirect_uri=http://127.0.0.1:4649/oidc/callback")
must.StrContains(t, escapedURL, "&response_type=code")
must.StrContains(t, escapedURL, "&scope=openid")
must.StrContains(t, escapedURL, "&state=st_")
}
func TestACLOIDC_CompleteAuth(t *testing.T) {
ci.Parallel(t)
testClient, testServer, _ := makeACLClient(t, nil, nil)
defer testServer.Stop()
// Set up the test OIDC provider.
oidcTestProvider := capOIDC.StartTestProvider(t)
defer oidcTestProvider.Stop()
oidcTestProvider.SetAllowedRedirectURIs([]string{"http://127.0.0.1:4649/oidc/callback"})
// Generate and upsert an ACL auth method for use. Certain values must be
// taken from the cap OIDC provider just like real world use.
mockedAuthMethod := api.ACLAuthMethod{
Name: "api-test-auth-method",
Type: api.ACLAuthMethodTypeOIDC,
TokenLocality: api.ACLAuthMethodTokenLocalityGlobal,
MaxTokenTTL: 10 * time.Hour,
Default: true,
Config: &api.ACLAuthMethodConfig{
OIDCDiscoveryURL: oidcTestProvider.Addr(),
OIDCClientID: "mock",
OIDCClientSecret: "verysecretsecret",
BoundAudiences: []string{"mock"},
AllowedRedirectURIs: []string{"http://127.0.0.1:4649/oidc/callback"},
DiscoveryCaPem: []string{oidcTestProvider.CACert()},
SigningAlgs: []string{"ES256"},
ClaimMappings: map[string]string{},
ListClaimMappings: map[string]string{
"http://nomad.internal/roles": "roles",
"http://nomad.internal/policies": "policies",
},
},
}
createdAuthMethod, writeMeta, err := testClient.ACLAuthMethods().Create(&mockedAuthMethod, nil)
must.NoError(t, err)
must.NotNil(t, createdAuthMethod)
assertWriteMeta(t, writeMeta)
// Set our custom data and some expected values, so we can make the call
// and use the test provider.
oidcTestProvider.SetExpectedAuthNonce("fpSPuaodKevKfDU3IeXb")
oidcTestProvider.SetExpectedAuthCode("codeABC")
oidcTestProvider.SetCustomAudience("mock")
oidcTestProvider.SetExpectedState("st_someweirdstateid")
oidcTestProvider.SetCustomClaims(map[string]interface{}{
"azp": "mock",
"http://nomad.internal/policies": []string{"engineering"},
"http://nomad.internal/roles": []string{"engineering"},
})
// Upsert an ACL policy and role, so that we can reference this within our
// OIDC claims.
mockedACLPolicy := api.ACLPolicy{
Name: "api-oidc-login-test",
Rules: `namespace "default" { policy = "write"}`,
}
_, err = testClient.ACLPolicies().Upsert(&mockedACLPolicy, nil)
must.NoError(t, err)
mockedACLRole := api.ACLRole{
Name: "api-oidc-login-test",
Policies: []*api.ACLRolePolicyLink{{Name: mockedACLPolicy.Name}},
}
createRoleResp, _, err := testClient.ACLRoles().Create(&mockedACLRole, nil)
must.NoError(t, err)
must.NotNil(t, createRoleResp)
// Generate and upsert two binding rules, so we can test both ACL Policy
// and Role claim mapping.
mockedBindingRule1 := api.ACLBindingRule{
AuthMethod: mockedAuthMethod.Name,
Selector: "engineering in list.policies",
BindType: api.ACLBindingRuleBindTypePolicy,
BindName: mockedACLPolicy.Name,
}
createBindingRole1Resp, _, err := testClient.ACLBindingRules().Create(&mockedBindingRule1, nil)
must.NoError(t, err)
must.NotNil(t, createBindingRole1Resp)
mockedBindingRule2 := api.ACLBindingRule{
AuthMethod: mockedAuthMethod.Name,
Selector: "engineering in list.roles",
BindType: api.ACLBindingRuleBindTypeRole,
BindName: mockedACLRole.Name,
}
createBindingRole2Resp, _, err := testClient.ACLBindingRules().Create(&mockedBindingRule2, nil)
must.NoError(t, err)
must.NotNil(t, createBindingRole2Resp)
// Generate and make the request.
authURLRequest := api.ACLOIDCCompleteAuthRequest{
AuthMethodName: createdAuthMethod.Name,
RedirectURI: createdAuthMethod.Config.AllowedRedirectURIs[0],
ClientNonce: "fpSPuaodKevKfDU3IeXb",
State: "st_someweirdstateid",
Code: "codeABC",
}
completeAuthResp, _, err := testClient.ACLAuth().CompleteAuth(&authURLRequest, nil)
must.NoError(t, err)
must.NotNil(t, completeAuthResp)
must.Len(t, 1, completeAuthResp.Policies)
must.Eq(t, mockedACLPolicy.Name, completeAuthResp.Policies[0])
must.Len(t, 1, completeAuthResp.Roles)
must.Eq(t, mockedACLRole.Name, completeAuthResp.Roles[0].Name)
must.Eq(t, createRoleResp.ID, completeAuthResp.Roles[0].ID)
}