Unverified Commit a58d313d authored by Jeff Mitchell's avatar Jeff Mitchell Committed by GitHub
Browse files

Batch tokens (#755)

parent 8db6aabe
Showing with 284 additions and 69 deletions
+284 -69
......@@ -271,4 +271,5 @@ type TokenCreateRequest struct {
DisplayName string `json:"display_name"`
NumUses int `json:"num_uses"`
Renewable *bool `json:"renewable,omitempty"`
Type string `json:"type"`
}
......@@ -73,46 +73,8 @@ func (c *Sys) DisableAuth(path string) error {
return err
}
// Structures for the requests/resposne are all down here. They aren't
// individually documented because the map almost directly to the raw HTTP API
// documentation. Please refer to that documentation for more details.
type EnableAuthOptions struct {
Type string `json:"type"`
Description string `json:"description"`
Config AuthConfigInput `json:"config"`
Local bool `json:"local"`
PluginName string `json:"plugin_name,omitempty"`
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
Options map[string]string `json:"options" mapstructure:"options"`
}
type AuthConfigInput struct {
DefaultLeaseTTL string `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL string `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"`
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
}
type AuthMount struct {
Type string `json:"type" mapstructure:"type"`
Description string `json:"description" mapstructure:"description"`
Accessor string `json:"accessor" mapstructure:"accessor"`
Config AuthConfigOutput `json:"config" mapstructure:"config"`
Local bool `json:"local" mapstructure:"local"`
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
Options map[string]string `json:"options" mapstructure:"options"`
}
type AuthConfigOutput struct {
DefaultLeaseTTL int `json:"default_lease_ttl" mapstructure:"default_lease_ttl"`
MaxLeaseTTL int `json:"max_lease_ttl" mapstructure:"max_lease_ttl"`
PluginName string `json:"plugin_name,omitempty" mapstructure:"plugin_name"`
AuditNonHMACRequestKeys []string `json:"audit_non_hmac_request_keys,omitempty" mapstructure:"audit_non_hmac_request_keys"`
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
}
// Rather than duplicate, we can use modern Go's type aliasing
type EnableAuthOptions = MountInput
type AuthConfigInput = MountConfigInput
type AuthMount = MountOutput
type AuthConfigOutput = MountConfigOutput
......@@ -132,10 +132,10 @@ type MountInput struct {
Type string `json:"type"`
Description string `json:"description"`
Config MountConfigInput `json:"config"`
Options map[string]string `json:"options"`
Local bool `json:"local"`
PluginName string `json:"plugin_name,omitempty"`
SealWrap bool `json:"seal_wrap" mapstructure:"seal_wrap"`
Options map[string]string `json:"options"`
}
type MountConfigInput struct {
......@@ -149,6 +149,7 @@ type MountConfigInput struct {
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
TokenType string `json:"token_type,omitempty" mapstructure:"token_type"`
}
type MountOutput struct {
......@@ -170,4 +171,5 @@ type MountConfigOutput struct {
AuditNonHMACResponseKeys []string `json:"audit_non_hmac_response_keys,omitempty" mapstructure:"audit_non_hmac_response_keys"`
ListingVisibility string `json:"listing_visibility,omitempty" mapstructure:"listing_visibility"`
PassthroughRequestHeaders []string `json:"passthrough_request_headers,omitempty" mapstructure:"passthrough_request_headers"`
TokenType string `json:"token_type,omitempty" mapstructure:"token_type"`
}
......@@ -134,6 +134,7 @@ func (f *AuditFormatter) FormatRequest(ctx context.Context, w io.Writer, config
Metadata: auth.Metadata,
EntityID: auth.EntityID,
RemainingUses: req.ClientTokenRemainingUses,
TokenType: auth.TokenType.String(),
},
Request: AuditRequest{
......@@ -304,6 +305,8 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
ExternalNamespacePolicies: resp.Auth.ExternalNamespacePolicies,
Metadata: resp.Auth.Metadata,
NumUses: resp.Auth.NumUses,
EntityID: resp.Auth.EntityID,
TokenType: resp.Auth.TokenType.String(),
}
}
......@@ -334,16 +337,17 @@ func (f *AuditFormatter) FormatResponse(ctx context.Context, w io.Writer, config
Type: "response",
Error: errString,
Auth: AuditAuth{
ClientToken: auth.ClientToken,
Accessor: auth.Accessor,
DisplayName: auth.DisplayName,
Policies: auth.Policies,
TokenPolicies: auth.TokenPolicies,
IdentityPolicies: auth.IdentityPolicies,
ExternalNamespacePolicies: auth.ExternalNamespacePolicies,
Metadata: auth.Metadata,
ClientToken: auth.ClientToken,
Accessor: auth.Accessor,
RemainingUses: req.ClientTokenRemainingUses,
EntityID: auth.EntityID,
TokenType: auth.TokenType.String(),
},
Request: AuditRequest{
......@@ -437,6 +441,7 @@ type AuditAuth struct {
NumUses int `json:"num_uses,omitempty"`
RemainingUses int `json:"remaining_uses,omitempty"`
EntityID string `json:"entity_id"`
TokenType string `json:"token_type"`
}
type AuditSecret struct {
......
......@@ -37,7 +37,13 @@ func TestFormatJSON_formatRequest(t *testing.T) {
ExpectedStr string
}{
"auth, request": {
&logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}},
&logical.Auth{
ClientToken: "foo",
Accessor: "bar",
DisplayName: "testtoken",
Policies: []string{"root"},
TokenType: logical.TokenTypeService,
},
&logical.Request{
Operation: logical.UpdateOperation,
Path: "/foo",
......@@ -56,7 +62,13 @@ func TestFormatJSON_formatRequest(t *testing.T) {
expectedResultStr,
},
"auth, request with prefix": {
&logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}},
&logical.Auth{
ClientToken: "foo",
Accessor: "bar",
DisplayName: "testtoken",
Policies: []string{"root"},
TokenType: logical.TokenTypeService,
},
&logical.Request{
Operation: logical.UpdateOperation,
Path: "/foo",
......@@ -127,5 +139,5 @@ func TestFormatJSON_formatRequest(t *testing.T) {
}
}
const testFormatJSONReqBasicStrFmt = `{"time":"2015-08-05T13:45:46Z","type":"request","auth":{"client_token":"%s","accessor":"bar","display_name":"testtoken","policies":["root"],"metadata":null},"request":{"operation":"update","path":"/foo","data":null,"wrap_ttl":60,"remote_address":"127.0.0.1","headers":{"foo":["bar"]}},"error":"this is an error"}
const testFormatJSONReqBasicStrFmt = `{"time":"2015-08-05T13:45:46Z","type":"request","auth":{"client_token":"%s","accessor":"bar","display_name":"testtoken","policies":["root"],"metadata":null,"entity_id":"","token_type":"service"},"request":{"operation":"update","path":"/foo","data":null,"wrap_ttl":60,"remote_address":"127.0.0.1","headers":{"foo":["bar"]}},"error":"this is an error"}
`
......@@ -36,7 +36,13 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
ExpectedStr string
}{
"auth, request": {
&logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}},
&logical.Auth{
ClientToken: "foo",
Accessor: "bar",
DisplayName: "testtoken",
Policies: []string{"root"},
TokenType: logical.TokenTypeService,
},
&logical.Request{
Operation: logical.UpdateOperation,
Path: "/foo",
......@@ -53,11 +59,17 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
errors.New("this is an error"),
"",
"",
fmt.Sprintf(`<json:object name="auth"><json:string name="accessor">bar</json:string><json:string name="client_token">%s</json:string><json:string name="display_name">testtoken</json:string><json:string name="entity_id"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:object name="namespace"><json:string name="id">root</json:string><json:string name="path"></json:string></json:object><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:boolean name="policy_override">false</json:boolean><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
fmt.Sprintf(`<json:object name="auth"><json:string name="accessor">bar</json:string><json:string name="client_token">%s</json:string><json:string name="display_name">testtoken</json:string><json:string name="entity_id"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array><json:string name="token_type">service</json:string></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:object name="namespace"><json:string name="id">root</json:string><json:string name="path"></json:string></json:object><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:boolean name="policy_override">false</json:boolean><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
fooSalted),
},
"auth, request with prefix": {
&logical.Auth{ClientToken: "foo", Accessor: "bar", DisplayName: "testtoken", Policies: []string{"root"}},
&logical.Auth{
ClientToken: "foo",
Accessor: "bar",
DisplayName: "testtoken",
Policies: []string{"root"},
TokenType: logical.TokenTypeService,
},
&logical.Request{
Operation: logical.UpdateOperation,
Path: "/foo",
......@@ -74,7 +86,7 @@ func TestFormatJSONx_formatRequest(t *testing.T) {
errors.New("this is an error"),
"",
"@cee: ",
fmt.Sprintf(`<json:object name="auth"><json:string name="accessor">bar</json:string><json:string name="client_token">%s</json:string><json:string name="display_name">testtoken</json:string><json:string name="entity_id"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:object name="namespace"><json:string name="id">root</json:string><json:string name="path"></json:string></json:object><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:boolean name="policy_override">false</json:boolean><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
fmt.Sprintf(`<json:object name="auth"><json:string name="accessor">bar</json:string><json:string name="client_token">%s</json:string><json:string name="display_name">testtoken</json:string><json:string name="entity_id"></json:string><json:null name="metadata" /><json:array name="policies"><json:string>root</json:string></json:array><json:string name="token_type">service</json:string></json:object><json:string name="error">this is an error</json:string><json:object name="request"><json:string name="client_token"></json:string><json:string name="client_token_accessor"></json:string><json:null name="data" /><json:object name="headers"><json:array name="foo"><json:string>bar</json:string></json:array></json:object><json:string name="id"></json:string><json:object name="namespace"><json:string name="id">root</json:string><json:string name="path"></json:string></json:object><json:string name="operation">update</json:string><json:string name="path">/foo</json:string><json:boolean name="policy_override">false</json:boolean><json:string name="remote_address">127.0.0.1</json:string><json:number name="wrap_ttl">60</json:number></json:object><json:string name="type">request</json:string>`,
fooSalted),
},
}
......
......@@ -304,6 +304,15 @@ func (b *backend) pathLoginUpdate(ctx context.Context, req *logical.Request, dat
BoundCIDRs: tokenBoundCIDRs,
}
switch role.TokenType {
case "default":
auth.TokenType = logical.TokenTypeDefault
case "batch":
auth.TokenType = logical.TokenTypeBatch
case "service":
auth.TokenType = logical.TokenTypeService
}
return &logical.Response{
Auth: auth,
}, nil
......
......@@ -84,6 +84,9 @@ type roleStorageEntry struct {
// SecretIDPrefix is the storage prefix for persisting secret IDs. This
// differs based on whether the secret IDs are cluster local or not.
SecretIDPrefix string `json:"secret_id_prefix" mapstructure:"secret_id_prefix"`
// TokenType is the type of token to generate
TokenType string `json:"token_type" mapstructure:"token_type"`
}
// roleIDStorageEntry represents the reverse mapping from RoleID to Role
......@@ -196,6 +199,11 @@ TTL will be set to the value of this parameter.`,
Description: `If set, the secret IDs generated using this role will be cluster local. This
can only be set during role creation and once set, it can't be reset later.`,
},
"token_type": &framework.FieldSchema{
Type: framework.TypeString,
Default: "default",
Description: `The type of token to generate ("service" or "batch"), or "default" to use the default`,
},
},
ExistenceCheck: b.pathRoleExistenceCheck,
Callbacks: map[logical.Operation]framework.OperationFunc{
......@@ -1007,6 +1015,30 @@ func (b *backend) pathRoleCreateUpdate(ctx context.Context, req *logical.Request
role.TokenMaxTTL = time.Second * time.Duration(data.Get("token_max_ttl").(int))
}
tokenType := role.TokenType
if tokenTypeRaw, ok := data.GetOk("token_type"); ok {
tokenType = tokenTypeRaw.(string)
switch tokenType {
case "":
tokenType = "default"
case "service", "batch", "default":
default:
return logical.ErrorResponse(fmt.Sprintf("invalid 'token_type' value %q", tokenType)), nil
}
} else if req.Operation == logical.CreateOperation {
tokenType = data.Get("token_type").(string)
}
role.TokenType = tokenType
if role.TokenType == "batch" {
if role.Period != 0 {
return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate periodic tokens"), nil
}
if role.TokenNumUses != 0 {
return logical.ErrorResponse("'token_type' cannot be 'batch' when role is set to generate tokens with limited use count"), nil
}
}
// Check that the TokenTTL value provided is less than the TokenMaxTTL.
// Sanitizing the TTL and MaxTTL is not required now and can be performed
// at credential issue time.
......@@ -1061,6 +1093,7 @@ func (b *backend) pathRoleRead(ctx context.Context, req *logical.Request, data *
"token_num_uses": role.TokenNumUses,
"token_ttl": role.TokenTTL / time.Second,
"local_secret_ids": false,
"token_type": role.TokenType,
}
if role.SecretIDPrefix == secretIDLocalPrefix {
......
......@@ -1159,6 +1159,7 @@ func TestAppRole_RoleCRUD(t *testing.T) {
"secret_id_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"},
"bound_cidr_list": []string{"127.0.0.1/32", "127.0.0.1/16"}, // returned for backwards compatibility
"token_bound_cidrs": []string{},
"token_type": "default",
}
var expectedStruct roleStorageEntry
......@@ -1637,6 +1638,7 @@ func TestAppRole_RoleWithTokenBoundCIDRsCRUD(t *testing.T) {
"token_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"},
"secret_id_bound_cidrs": []string{"127.0.0.1/32", "127.0.0.1/16"},
"bound_cidr_list": []string{"127.0.0.1/32", "127.0.0.1/16"}, // provided for backwards compatibility
"token_type": "default",
}
var expectedStruct roleStorageEntry
......
......@@ -30,6 +30,7 @@ type AuthEnableCommand struct {
flagOptions map[string]string
flagLocal bool
flagSealWrap bool
flagTokenType string
flagVersion int
}
......@@ -162,6 +163,12 @@ func (c *AuthEnableCommand) Flags() *FlagSets {
Usage: "Enable seal wrapping of critical values in the secrets engine.",
})
f.StringVar(&StringVar{
Name: flagNameTokenType,
Target: &c.flagTokenType,
Usage: "Sets a forced token type for the mount.",
})
f.IntVar(&IntVar{
Name: "version",
Target: &c.flagVersion,
......@@ -257,6 +264,10 @@ func (c *AuthEnableCommand) Run(args []string) int {
if fl.Name == flagNamePassthroughRequestHeaders {
authOpts.Config.PassthroughRequestHeaders = c.flagPassthroughRequestHeaders
}
if fl.Name == flagNameTokenType {
authOpts.Config.TokenType = c.flagTokenType
}
})
if err := client.Sys().EnableAuthWithOptions(authPath, authOpts); err != nil {
......
......@@ -143,7 +143,7 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri
}
}
out := []string{"Path | Type | Accessor | Plugin | Default TTL | Max TTL | Replication | Seal Wrap | Options | Description"}
out := []string{"Path | Type | Accessor | Plugin | Default TTL | Max TTL | Token Type | Replication | Seal Wrap | Options | Description"}
for _, path := range paths {
mount := auths[path]
......@@ -155,13 +155,14 @@ func (c *AuthListCommand) detailedMounts(auths map[string]*api.AuthMount) []stri
replication = "local"
}
out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s | %t | %v | %s",
out = append(out, fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s | %s | %t | %v | %s",
path,
mount.Type,
mount.Accessor,
mount.Config.PluginName,
defaultTTL,
maxTTL,
mount.Config.TokenType,
replication,
mount.SealWrap,
mount.Options,
......
......@@ -25,6 +25,7 @@ type AuthTuneCommand struct {
flagListingVisibility string
flagMaxLeaseTTL time.Duration
flagOptions map[string]string
flagTokenType string
flagVersion int
}
......@@ -112,6 +113,12 @@ func (c *AuthTuneCommand) Flags() *FlagSets {
"This can be specified multiple times.",
})
f.StringVar(&StringVar{
Name: flagNameTokenType,
Target: &c.flagTokenType,
Usage: "Sets a forced token type for the mount.",
})
f.IntVar(&IntVar{
Name: "version",
Target: &c.flagVersion,
......@@ -184,6 +191,10 @@ func (c *AuthTuneCommand) Run(args []string) int {
if fl.Name == flagNameListingVisibility {
mountConfigInput.ListingVisibility = c.flagListingVisibility
}
if fl.Name == flagNameTokenType {
mountConfigInput.TokenType = c.flagTokenType
}
})
// Append /auth (since that's where auths live) and a trailing slash to
......
......@@ -92,6 +92,8 @@ const (
flagNameListingVisibility = "listing-visibility"
// flagNamePassthroughRequestHeaders is the flag name used to set passthrough request headers to the backend
flagNamePassthroughRequestHeaders = "passthrough-request-headers"
// flagNameTokenType is the flag name used to force a specific token type
flagNameTokenType = "token-type"
)
var (
......
......@@ -26,6 +26,7 @@ type TokenCreateCommand struct {
flagNoDefaultPolicy bool
flagUseLimit int
flagRole string
flagType string
flagMetadata map[string]string
flagPolicies []string
......@@ -153,6 +154,13 @@ func (c *TokenCreateCommand) Flags() *FlagSets {
"must have permission for \"auth/token/create/<role>\".",
})
f.StringVar(&StringVar{
Name: "type",
Target: &c.flagType,
Default: "service",
Usage: `The type of token to create. Can be "service" or "batch".`,
})
f.StringMapVar(&StringMapVar{
Name: "metadata",
Target: &c.flagMetadata,
......@@ -213,6 +221,10 @@ func (c *TokenCreateCommand) Run(args []string) int {
}
}
if c.flagType == "batch" {
c.flagRenewable = false
}
client, err := c.Client()
if err != nil {
c.UI.Error(err.Error())
......@@ -231,6 +243,7 @@ func (c *TokenCreateCommand) Run(args []string) int {
Renewable: &c.flagRenewable,
ExplicitMaxTTL: c.flagExplicitMaxTTL.String(),
Period: c.flagPeriod.String(),
Type: c.flagType,
}
var secret *api.Secret
......
......@@ -104,7 +104,18 @@ func Canonicalize(nsPath string) string {
func SplitIDFromString(input string) (string, string) {
prefix := ""
slashIdx := strings.LastIndex(input, "/")
if slashIdx > 0 {
switch {
case strings.HasPrefix(input, "b."):
prefix = "b."
input = input[2:]
case strings.HasPrefix(input, "s."):
prefix = "s."
input = input[2:]
case slashIdx > 0:
// Leases will never have a b./s. to start
if slashIdx == len(input)-1 {
return input, ""
}
......
......@@ -48,6 +48,21 @@ func TestSplitIDFromString(t *testing.T) {
"",
"foo.foo/",
},
{
"b.foo",
"",
"b.foo",
},
{
"s.foo",
"",
"s.foo",
},
{
"t.foo",
"foo",
"t",
},
}
for _, c := range tcases {
......
......@@ -5,14 +5,33 @@ import (
"errors"
"fmt"
"math/rand"
"sync"
"time"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/helper/consts"
"github.com/hashicorp/vault/helper/xor"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/physical"
"github.com/hashicorp/vault/physical/inmem"
"github.com/hashicorp/vault/vault"
"github.com/mitchellh/go-testing-interface"
testing "github.com/mitchellh/go-testing-interface"
)
type ReplicatedTestClusters struct {
PerfPrimaryCluster *vault.TestCluster
PerfSecondaryCluster *vault.TestCluster
PerfPrimaryDRCluster *vault.TestCluster
PerfSecondaryDRCluster *vault.TestCluster
}
func (r *ReplicatedTestClusters) Cleanup() {
r.PerfPrimaryCluster.Cleanup()
r.PerfSecondaryCluster.Cleanup()
r.PerfPrimaryDRCluster.Cleanup()
r.PerfSecondaryDRCluster.Cleanup()
}
// Generates a root token on the target cluster.
func GenerateRoot(t testing.T, cluster *vault.TestCluster, drToken bool) string {
token, err := GenerateRootWithError(t, cluster, drToken)
......@@ -127,6 +146,65 @@ func WaitForReplicationState(t testing.T, c *vault.Core, state consts.Replicatio
}
}
func GetClusterAndCore(t testing.T, logger log.Logger) (*vault.TestCluster, *vault.TestClusterCore) {
inm, err := inmem.NewTransactionalInmem(nil, logger)
if err != nil {
t.Fatal(err)
}
inmha, err := inmem.NewInmemHA(nil, logger)
if err != nil {
t.Fatal(err)
}
coreConfig := &vault.CoreConfig{
Physical: inm,
HAPhysical: inmha.(physical.HABackend),
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
Logger: logger,
})
cluster.Start()
cores := cluster.Cores
core := cores[0]
vault.TestWaitActive(t, core.Core)
return cluster, core
}
func GetFourReplicatedClusters(t testing.T) *ReplicatedTestClusters {
ret := &ReplicatedTestClusters{}
logger := log.New(&log.LoggerOptions{
Mutex: &sync.Mutex{},
Level: log.Trace,
})
// Set this lower so that state populates quickly to standby nodes
vault.HeartbeatInterval = 2 * time.Second
ret.PerfPrimaryCluster, _ = GetClusterAndCore(t, logger.Named("perf-pri"))
ret.PerfSecondaryCluster, _ = GetClusterAndCore(t, logger.Named("perf-sec"))
ret.PerfPrimaryDRCluster, _ = GetClusterAndCore(t, logger.Named("perf-pri-dr"))
ret.PerfSecondaryDRCluster, _ = GetClusterAndCore(t, logger.Named("perf-sec-dr"))
SetupFourClusterReplication(t, ret.PerfPrimaryCluster, ret.PerfSecondaryCluster, ret.PerfPrimaryDRCluster, ret.PerfSecondaryDRCluster)
// Wait until poison pills have been read
time.Sleep(45 * time.Second)
EnsureCoresUnsealed(t, ret.PerfPrimaryCluster)
EnsureCoresUnsealed(t, ret.PerfSecondaryCluster)
EnsureCoresUnsealed(t, ret.PerfPrimaryDRCluster)
EnsureCoresUnsealed(t, ret.PerfSecondaryDRCluster)
return ret
}
func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDRSecondary, perfSecondaryDRSecondary *vault.TestCluster) {
// Enable dr primary
_, err := perfPrimary.Cores[0].Client.Logical().Write("sys/replication/dr/primary/enable", nil)
......@@ -137,7 +215,7 @@ func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDR
WaitForReplicationState(t, perfPrimary.Cores[0].Core, consts.ReplicationDRPrimary)
// Enable performance primary
_, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/primary/enable", nil)
_, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/performance/primary/enable", nil)
if err != nil {
t.Fatal(err)
}
......@@ -167,7 +245,7 @@ func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDR
EnsureCoresUnsealed(t, perfDRSecondary)
// get performance token
secret, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/primary/secondary-token", map[string]interface{}{
secret, err = perfPrimary.Cores[0].Client.Logical().Write("sys/replication/performance/primary/secondary-token", map[string]interface{}{
"id": "1",
})
if err != nil {
......@@ -177,7 +255,7 @@ func SetupFourClusterReplication(t testing.T, perfPrimary, perfSecondary, perfDR
token = secret.WrapInfo.Token
// enable performace secondary
secret, err = perfSecondary.Cores[0].Client.Logical().Write("sys/replication/secondary/enable", map[string]interface{}{
secret, err = perfSecondary.Cores[0].Client.Logical().Write("sys/replication/performance/secondary/enable", map[string]interface{}{
"token": token,
"ca_file": perfPrimary.CACertPEMFile,
})
......
......@@ -421,9 +421,16 @@ func handleRequestForwarding(core *vault.Core, handler http.Handler) http.Handle
return
}
path := ns.TrimmedPath(r.URL.Path[len("/v1/"):])
if !perfStandbyAlwaysForwardPaths.HasPath(path) {
switch {
case !perfStandbyAlwaysForwardPaths.HasPath(path):
handler.ServeHTTP(w, r)
return
case strings.HasPrefix(path, "auth/token/create/"):
isBatch, err := core.IsBatchTokenCreationRequest(r.Context(), path)
if err == nil && isBatch {
handler.ServeHTTP(w, r)
return
}
}
}
......
......@@ -13,6 +13,7 @@ import (
"testing"
"time"
"github.com/go-test/deep"
log "github.com/hashicorp/go-hclog"
"github.com/hashicorp/vault/helper/consts"
......@@ -58,8 +59,8 @@ func TestLogical(t *testing.T) {
testResponseBody(t, resp, &actual)
delete(actual, "lease_id")
expected["request_id"] = actual["request_id"]
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad:\nactual:\n%#v\nexpected:\n%#v", actual, expected)
if diff := deep.Equal(actual, expected); diff != nil {
t.Fatal(diff)
}
// DELETE
......@@ -163,6 +164,7 @@ func TestLogical_StandbyRedirect(t *testing.T) {
"explicit_max_ttl": json.Number("0"),
"expire_time": nil,
"entity_id": "",
"type": "service",
},
"warnings": nilWarnings,
"wrap_info": nil,
......@@ -177,8 +179,8 @@ func TestLogical_StandbyRedirect(t *testing.T) {
actual["data"] = actualDataMap
expected["request_id"] = actual["request_id"]
delete(actual, "lease_id")
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: got %#v; expected %#v", actual, expected)
if diff := deep.Equal(actual, expected); diff != nil {
t.Fatal(diff)
}
//// DELETE to standby
......@@ -214,6 +216,7 @@ func TestLogical_CreateToken(t *testing.T) {
"lease_duration": json.Number("0"),
"renewable": false,
"entity_id": "",
"token_type": "service",
},
"warnings": nilWarnings,
}
......
......@@ -5,6 +5,7 @@ import (
"reflect"
"testing"
"github.com/go-test/deep"
"github.com/hashicorp/vault/vault"
)
......@@ -32,6 +33,8 @@ func TestSysAuth(t *testing.T) {
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"plugin_name": "",
"token_type": "default-service",
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
......@@ -45,6 +48,8 @@ func TestSysAuth(t *testing.T) {
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"plugin_name": "",
"token_type": "default-service",
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
......@@ -98,6 +103,8 @@ func TestSysEnableAuth(t *testing.T) {
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"plugin_name": "",
"token_type": "default-service",
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
......@@ -110,6 +117,8 @@ func TestSysEnableAuth(t *testing.T) {
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"plugin_name": "",
"force_no_cache": false,
"token_type": "default-service",
},
"local": false,
"seal_wrap": false,
......@@ -123,6 +132,8 @@ func TestSysEnableAuth(t *testing.T) {
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"plugin_name": "",
"token_type": "default-service",
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
......@@ -135,6 +146,8 @@ func TestSysEnableAuth(t *testing.T) {
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"plugin_name": "",
"token_type": "default-service",
"force_no_cache": false,
},
"local": false,
"seal_wrap": false,
......@@ -153,8 +166,8 @@ func TestSysEnableAuth(t *testing.T) {
expected["data"].(map[string]interface{})[k].(map[string]interface{})["accessor"] = v.(map[string]interface{})["accessor"]
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual)
if diff := deep.Equal(actual, expected); diff != nil {
t.Fatal(diff)
}
}
......@@ -189,6 +202,8 @@ func TestSysDisableAuth(t *testing.T) {
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"plugin_name": "",
"token_type": "default-service",
"force_no_cache": false,
},
"description": "token based credentials",
"type": "token",
......@@ -202,6 +217,8 @@ func TestSysDisableAuth(t *testing.T) {
"default_lease_ttl": json.Number("0"),
"max_lease_ttl": json.Number("0"),
"plugin_name": "",
"token_type": "default-service",
"force_no_cache": false,
},
"description": "token based credentials",
"type": "token",
......@@ -222,8 +239,8 @@ func TestSysDisableAuth(t *testing.T) {
expected["data"].(map[string]interface{})[k].(map[string]interface{})["accessor"] = v.(map[string]interface{})["accessor"]
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: expected:%#v\nactual:%#v", expected, actual)
if diff := deep.Equal(actual, expected); diff != nil {
t.Fatal(diff)
}
}
......@@ -263,12 +280,14 @@ func TestSysTuneAuth_nonHMACKeys(t *testing.T) {
"force_no_cache": false,
"audit_non_hmac_request_keys": []interface{}{"foo"},
"audit_non_hmac_response_keys": []interface{}{"bar"},
"token_type": "default-service",
},
"default_lease_ttl": json.Number("2764800"),
"max_lease_ttl": json.Number("2764800"),
"force_no_cache": false,
"audit_non_hmac_request_keys": []interface{}{"foo"},
"audit_non_hmac_response_keys": []interface{}{"bar"},
"token_type": "default-service",
}
testResponseBody(t, resp, &actual)
expected["request_id"] = actual["request_id"]
......@@ -302,10 +321,12 @@ func TestSysTuneAuth_nonHMACKeys(t *testing.T) {
"default_lease_ttl": json.Number("2764800"),
"max_lease_ttl": json.Number("2764800"),
"force_no_cache": false,
"token_type": "default-service",
},
"default_lease_ttl": json.Number("2764800"),
"max_lease_ttl": json.Number("2764800"),
"force_no_cache": false,
"token_type": "default-service",
}
testResponseBody(t, resp, &actual)
expected["request_id"] = actual["request_id"]
......@@ -336,10 +357,12 @@ func TestSysTuneAuth_showUIMount(t *testing.T) {
"default_lease_ttl": json.Number("2764800"),
"max_lease_ttl": json.Number("2764800"),
"force_no_cache": false,
"token_type": "default-service",
},
"default_lease_ttl": json.Number("2764800"),
"max_lease_ttl": json.Number("2764800"),
"force_no_cache": false,
"token_type": "default-service",
}
testResponseBody(t, resp, &actual)
expected["request_id"] = actual["request_id"]
......@@ -370,11 +393,13 @@ func TestSysTuneAuth_showUIMount(t *testing.T) {
"max_lease_ttl": json.Number("2764800"),
"force_no_cache": false,
"listing_visibility": "unauth",
"token_type": "default-service",
},
"default_lease_ttl": json.Number("2764800"),
"max_lease_ttl": json.Number("2764800"),
"force_no_cache": false,
"listing_visibility": "unauth",
"token_type": "default-service",
}
testResponseBody(t, resp, &actual)
expected["request_id"] = actual["request_id"]
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment