Merge branch 'master' into jo-upgrade-copy
This commit is contained in:
commit
80262092af
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -8,21 +8,27 @@ DEPRECATIONS/CHANGES:
|
|||
|
||||
FEATURES:
|
||||
|
||||
* Azure Key Vault Auto Unseal/Seal Wrap Support (Enterprise): Azure Key Vault
|
||||
can now be used a support seal for Auto Unseal and Seal Wrapping.
|
||||
* Cert auth CIDR restrictions: When using the `cert` auth method you can now
|
||||
limit authentication to specific CIDRs; these will also be encoded in
|
||||
resultant tokens to limit their use.
|
||||
* Userpass auth CIDR restrictions: When using the `userpass` auth method you
|
||||
can now limit authentication to specific CIDRs; these will also be encoded
|
||||
in resultant tokens to limit their use.
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
* api: Close renewer's doneCh when the renewer is stopped, so that programs
|
||||
expecting a final value through doneCh behave correctly [GH-4472]
|
||||
* auth/cert: Break out `allowed_names` into component parts and add
|
||||
`allowed_uri_sans` [GH-4231]
|
||||
* cli: `vault login` now supports a `-no-print` flag to suppress printing
|
||||
token information but still allow storing into the token helper [GH-4454]
|
||||
* core/pkcs11 (enterprise): Add support for CKM_AES_CBS_PAD, CKM_RSA_PKCS, and
|
||||
* core/pkcs11 (enterprise): Add support for CKM_AES_CBC_PAD, CKM_RSA_PKCS, and
|
||||
CKM_RSA_PKCS_OAEP mechanisms
|
||||
* core/pkcs11 (enterprise): HSM slots can now be selected by token label instead
|
||||
of just slot number
|
||||
* core/seal (enterprise): Lazily rewrap data when seal keys are rotated
|
||||
* expiration: Allow revoke-prefix and revoke-force to work on single leases as
|
||||
well as prefixes [GH-4450]
|
||||
|
||||
|
@ -39,12 +45,19 @@ BUG FIXES:
|
|||
parameters were used [GH-4582]
|
||||
* secret/gcp: Make `bound_region` able to use short names
|
||||
* secret/kv: Fix response wrapping for KV v2 [GH-4511]
|
||||
* secret/kv: Fix address flag not being honored correctly [GH-4617]
|
||||
* secret/pki: Fix `key_type` not being allowed to be set to `any` [GH-4595]
|
||||
* secret/pki: Fix path length parameter being ignored when using
|
||||
`use_csr_values` and signing an intermediate CA cert [GH-4459]
|
||||
* storage/dynamodb: Fix listing when one child is left within a nested path
|
||||
[GH-4570]
|
||||
* ui: Fix HMAC algorithm in transit [GH-4604]
|
||||
* ui: Fix unwrap of auth responses via the UI's unwrap tool [GH-4611]
|
||||
* replication: Fix error while running plugins on a newly created replication
|
||||
secondary
|
||||
* replication: Fix issue with token store lookups after a secondary's mount table
|
||||
is invalidated.
|
||||
* replication: Improve startup time when a large merkle index is in use.
|
||||
|
||||
## 0.10.1/0.9.7 (April 25th, 2018)
|
||||
|
||||
|
|
|
@ -388,11 +388,12 @@ func (c *Client) SetAddress(addr string) error {
|
|||
c.modifyLock.Lock()
|
||||
defer c.modifyLock.Unlock()
|
||||
|
||||
var err error
|
||||
if c.addr, err = url.Parse(addr); err != nil {
|
||||
parsedAddr, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return errwrap.Wrapf("failed to set address: {{err}}", err)
|
||||
}
|
||||
|
||||
c.addr = parsedAddr
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -411,7 +412,8 @@ func (c *Client) SetLimiter(rateLimit float64, burst int) {
|
|||
c.modifyLock.RLock()
|
||||
c.config.modifyLock.Lock()
|
||||
defer c.config.modifyLock.Unlock()
|
||||
defer c.modifyLock.RUnlock()
|
||||
c.modifyLock.RUnlock()
|
||||
|
||||
c.config.Limiter = rate.NewLimiter(rate.Limit(rateLimit), burst)
|
||||
}
|
||||
|
||||
|
@ -544,14 +546,20 @@ func (c *Client) SetPolicyOverride(override bool) {
|
|||
// doesn't need to be called externally.
|
||||
func (c *Client) NewRequest(method, requestPath string) *Request {
|
||||
c.modifyLock.RLock()
|
||||
defer c.modifyLock.RUnlock()
|
||||
addr := c.addr
|
||||
token := c.token
|
||||
mfaCreds := c.mfaCreds
|
||||
wrappingLookupFunc := c.wrappingLookupFunc
|
||||
headers := c.headers
|
||||
policyOverride := c.policyOverride
|
||||
c.modifyLock.RUnlock()
|
||||
|
||||
// if SRV records exist (see https://tools.ietf.org/html/draft-andrews-http-srv-02), lookup the SRV
|
||||
// record and take the highest match; this is not designed for high-availability, just discovery
|
||||
var host string = c.addr.Host
|
||||
if c.addr.Port() == "" {
|
||||
var host string = addr.Host
|
||||
if addr.Port() == "" {
|
||||
// Internet Draft specifies that the SRV record is ignored if a port is given
|
||||
_, addrs, err := net.LookupSRV("http", "tcp", c.addr.Hostname())
|
||||
_, addrs, err := net.LookupSRV("http", "tcp", addr.Hostname())
|
||||
if err == nil && len(addrs) > 0 {
|
||||
host = fmt.Sprintf("%s:%d", addrs[0].Target, addrs[0].Port)
|
||||
}
|
||||
|
@ -560,12 +568,12 @@ func (c *Client) NewRequest(method, requestPath string) *Request {
|
|||
req := &Request{
|
||||
Method: method,
|
||||
URL: &url.URL{
|
||||
User: c.addr.User,
|
||||
Scheme: c.addr.Scheme,
|
||||
User: addr.User,
|
||||
Scheme: addr.Scheme,
|
||||
Host: host,
|
||||
Path: path.Join(c.addr.Path, requestPath),
|
||||
Path: path.Join(addr.Path, requestPath),
|
||||
},
|
||||
ClientToken: c.token,
|
||||
ClientToken: token,
|
||||
Params: make(map[string][]string),
|
||||
}
|
||||
|
||||
|
@ -579,21 +587,19 @@ func (c *Client) NewRequest(method, requestPath string) *Request {
|
|||
lookupPath = requestPath
|
||||
}
|
||||
|
||||
req.MFAHeaderVals = c.mfaCreds
|
||||
req.MFAHeaderVals = mfaCreds
|
||||
|
||||
if c.wrappingLookupFunc != nil {
|
||||
req.WrapTTL = c.wrappingLookupFunc(method, lookupPath)
|
||||
if wrappingLookupFunc != nil {
|
||||
req.WrapTTL = wrappingLookupFunc(method, lookupPath)
|
||||
} else {
|
||||
req.WrapTTL = DefaultWrappingLookupFunc(method, lookupPath)
|
||||
}
|
||||
if c.config.Timeout != 0 {
|
||||
c.config.HttpClient.Timeout = c.config.Timeout
|
||||
}
|
||||
if c.headers != nil {
|
||||
req.Headers = c.headers
|
||||
|
||||
if headers != nil {
|
||||
req.Headers = headers
|
||||
}
|
||||
|
||||
req.PolicyOverride = c.policyOverride
|
||||
req.PolicyOverride = policyOverride
|
||||
|
||||
return req
|
||||
}
|
||||
|
@ -602,18 +608,23 @@ func (c *Client) NewRequest(method, requestPath string) *Request {
|
|||
// a Vault server not configured with this client. This is an advanced operation
|
||||
// that generally won't need to be called externally.
|
||||
func (c *Client) RawRequest(r *Request) (*Response, error) {
|
||||
|
||||
c.modifyLock.RLock()
|
||||
c.config.modifyLock.RLock()
|
||||
defer c.config.modifyLock.RUnlock()
|
||||
|
||||
if c.config.Limiter != nil {
|
||||
c.config.Limiter.Wait(context.Background())
|
||||
}
|
||||
|
||||
token := c.token
|
||||
|
||||
c.config.modifyLock.RLock()
|
||||
limiter := c.config.Limiter
|
||||
maxRetries := c.config.MaxRetries
|
||||
backoff := c.config.Backoff
|
||||
httpClient := c.config.HttpClient
|
||||
timeout := c.config.Timeout
|
||||
c.config.modifyLock.RUnlock()
|
||||
|
||||
c.modifyLock.RUnlock()
|
||||
|
||||
if limiter != nil {
|
||||
limiter.Wait(context.Background())
|
||||
}
|
||||
|
||||
// Sanity check the token before potentially erroring from the API
|
||||
idx := strings.IndexFunc(token, func(c rune) bool {
|
||||
return !unicode.IsPrint(c)
|
||||
|
@ -632,16 +643,23 @@ START:
|
|||
return nil, fmt.Errorf("nil request created")
|
||||
}
|
||||
|
||||
backoff := c.config.Backoff
|
||||
// Set the timeout, if any
|
||||
var cancelFunc context.CancelFunc
|
||||
if timeout != 0 {
|
||||
var ctx context.Context
|
||||
ctx, cancelFunc = context.WithTimeout(context.Background(), timeout)
|
||||
req.Request = req.Request.WithContext(ctx)
|
||||
}
|
||||
|
||||
if backoff == nil {
|
||||
backoff = retryablehttp.LinearJitterBackoff
|
||||
}
|
||||
|
||||
client := &retryablehttp.Client{
|
||||
HTTPClient: c.config.HttpClient,
|
||||
HTTPClient: httpClient,
|
||||
RetryWaitMin: 1000 * time.Millisecond,
|
||||
RetryWaitMax: 1500 * time.Millisecond,
|
||||
RetryMax: c.config.MaxRetries,
|
||||
RetryMax: maxRetries,
|
||||
CheckRetry: retryablehttp.DefaultRetryPolicy,
|
||||
Backoff: backoff,
|
||||
ErrorHandler: retryablehttp.PassthroughErrorHandler,
|
||||
|
@ -649,6 +667,9 @@ START:
|
|||
|
||||
var result *Response
|
||||
resp, err := client.Do(req)
|
||||
if cancelFunc != nil {
|
||||
cancelFunc()
|
||||
}
|
||||
if resp != nil {
|
||||
result = &Response{Response: resp}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -244,22 +243,10 @@ func TestClientTimeoutSetting(t *testing.T) {
|
|||
defer os.Setenv(EnvVaultClientTimeout, oldClientTimeout)
|
||||
config := DefaultConfig()
|
||||
config.ReadEnvironment()
|
||||
client, err := NewClient(config)
|
||||
_, err := NewClient(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = client.NewRequest("PUT", "/")
|
||||
if client.config.HttpClient.Timeout != time.Second*10 {
|
||||
t.Fatalf("error setting client timeout using env variable")
|
||||
}
|
||||
|
||||
// Setting custom client timeout for a new request
|
||||
client.SetClientTimeout(time.Second * 20)
|
||||
_ = client.NewRequest("PUT", "/")
|
||||
if client.config.HttpClient.Timeout != time.Second*20 {
|
||||
t.Fatalf("error setting client timeout using SetClientTimeout")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type roundTripperFunc func(*http.Request) (*http.Response, error)
|
||||
|
|
|
@ -843,9 +843,9 @@ func TestBackend_CertWrites(t *testing.T) {
|
|||
tc := logicaltest.TestCase{
|
||||
Backend: testFactory(t),
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepCert(t, "aaa", ca1, "foo", "", "", false),
|
||||
testAccStepCert(t, "bbb", ca2, "foo", "", "", false),
|
||||
testAccStepCert(t, "ccc", ca3, "foo", "", "", true),
|
||||
testAccStepCert(t, "aaa", ca1, "foo", allowed{}, false),
|
||||
testAccStepCert(t, "bbb", ca2, "foo", allowed{}, false),
|
||||
testAccStepCert(t, "ccc", ca3, "foo", allowed{}, true),
|
||||
},
|
||||
}
|
||||
tc.Steps = append(tc.Steps, testAccStepListCerts(t, []string{"aaa", "bbb"})...)
|
||||
|
@ -866,7 +866,7 @@ func TestBackend_basic_CA(t *testing.T) {
|
|||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
Backend: testFactory(t),
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepCert(t, "web", ca, "foo", "", "", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCertLease(t, "web", ca, "foo"),
|
||||
testAccStepCertTTL(t, "web", ca, "foo"),
|
||||
|
@ -875,9 +875,9 @@ func TestBackend_basic_CA(t *testing.T) {
|
|||
testAccStepLogin(t, connState),
|
||||
testAccStepCertNoLease(t, "web", ca, "foo"),
|
||||
testAccStepLoginDefaultLease(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "*.example.com", "", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{names: "*.example.com"}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "*.invalid.com", "", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{names: "*.invalid.com"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
},
|
||||
})
|
||||
|
@ -926,20 +926,45 @@ func TestBackend_basic_singleCert(t *testing.T) {
|
|||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
Backend: testFactory(t),
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepCert(t, "web", ca, "foo", "", "", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "example.com", "", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com"}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "invalid", "", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{names: "invalid"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "", "1.2.3.4:invalid", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{ext: "1.2.3.4:invalid"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Test a self-signed client with custom extensions (root CA) that is trusted
|
||||
func TestBackend_extensions_singleCert(t *testing.T) {
|
||||
func TestBackend_common_name_singleCert(t *testing.T) {
|
||||
connState, err := testConnState("test-fixtures/root/rootcacert.pem",
|
||||
"test-fixtures/root/rootcakey.pem", "test-fixtures/root/rootcacert.pem")
|
||||
if err != nil {
|
||||
t.Fatalf("error testing connection state: %v", err)
|
||||
}
|
||||
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
Backend: testFactory(t),
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{common_names: "example.com"}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{common_names: "invalid"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{ext: "1.2.3.4:invalid"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Test a self-signed client with custom ext (root CA) that is trusted
|
||||
func TestBackend_ext_singleCert(t *testing.T) {
|
||||
connState, err := testConnState(
|
||||
"test-fixtures/root/rootcawextcert.pem",
|
||||
"test-fixtures/root/rootcawextkey.pem",
|
||||
|
@ -955,39 +980,132 @@ func TestBackend_extensions_singleCert(t *testing.T) {
|
|||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
Backend: testFactory(t),
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepCert(t, "web", ca, "foo", "", "2.1.1.1:A UTF8String Extension", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{ext: "2.1.1.1:A UTF8String Extension"}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "", "2.1.1.1:*,2.1.1.2:A UTF8*", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{ext: "2.1.1.1:*,2.1.1.2:A UTF8*"}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "", "1.2.3.45:*", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{ext: "1.2.3.45:*"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "", "2.1.1.1:The Wrong Value", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{ext: "2.1.1.1:The Wrong Value"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "", "2.1.1.1:*,2.1.1.2:The Wrong Value", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{ext: "2.1.1.1:*,2.1.1.2:The Wrong Value"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "", "2.1.1.1:", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{ext: "2.1.1.1:"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "", "2.1.1.1:,2.1.1.2:*", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{ext: "2.1.1.1:,2.1.1.2:*"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "example.com", "2.1.1.1:A UTF8String Extension", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com", ext: "2.1.1.1:A UTF8String Extension"}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "example.com", "2.1.1.1:*,2.1.1.2:A UTF8*", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com", ext: "2.1.1.1:*,2.1.1.2:A UTF8*"}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "example.com", "1.2.3.45:*", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com", ext: "1.2.3.45:*"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "example.com", "2.1.1.1:The Wrong Value", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com", ext: "2.1.1.1:The Wrong Value"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "example.com", "2.1.1.1:*,2.1.1.2:The Wrong Value", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{names: "example.com", ext: "2.1.1.1:*,2.1.1.2:The Wrong Value"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "invalid", "2.1.1.1:A UTF8String Extension", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{names: "invalid", ext: "2.1.1.1:A UTF8String Extension"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "invalid", "2.1.1.1:*,2.1.1.2:A UTF8*", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{names: "invalid", ext: "2.1.1.1:*,2.1.1.2:A UTF8*"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "invalid", "1.2.3.45:*", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{names: "invalid", ext: "1.2.3.45:*"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "invalid", "2.1.1.1:The Wrong Value", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{names: "invalid", ext: "2.1.1.1:The Wrong Value"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", "invalid", "2.1.1.1:*,2.1.1.2:The Wrong Value", false),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{names: "invalid", ext: "2.1.1.1:*,2.1.1.2:The Wrong Value"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Test a self-signed client with URI alt names (root CA) that is trusted
|
||||
func TestBackend_dns_singleCert(t *testing.T) {
|
||||
connState, err := testConnState(
|
||||
"test-fixtures/root/rootcawdnscert.pem",
|
||||
"test-fixtures/root/rootcawdnskey.pem",
|
||||
"test-fixtures/root/rootcacert.pem",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error testing connection state: %v", err)
|
||||
}
|
||||
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
Backend: testFactory(t),
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{dns: "example.com"}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{dns: "*ample.com"}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{dns: "notincert.com"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{dns: "abc"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{dns: "*.example.com"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Test a self-signed client with URI alt names (root CA) that is trusted
|
||||
func TestBackend_email_singleCert(t *testing.T) {
|
||||
connState, err := testConnState(
|
||||
"test-fixtures/root/rootcawemailcert.pem",
|
||||
"test-fixtures/root/rootcawemailkey.pem",
|
||||
"test-fixtures/root/rootcacert.pem",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error testing connection state: %v", err)
|
||||
}
|
||||
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
Backend: testFactory(t),
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{emails: "valid@example.com"}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{emails: "*@example.com"}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{emails: "invalid@notincert.com"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{emails: "abc"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{emails: "*.example.com"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Test a self-signed client with URI alt names (root CA) that is trusted
|
||||
func TestBackend_uri_singleCert(t *testing.T) {
|
||||
connState, err := testConnState(
|
||||
"test-fixtures/root/rootcawuricert.pem",
|
||||
"test-fixtures/root/rootcawurikey.pem",
|
||||
"test-fixtures/root/rootcacert.pem",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("error testing connection state: %v", err)
|
||||
}
|
||||
ca, err := ioutil.ReadFile("test-fixtures/root/rootcacert.pem")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
Backend: testFactory(t),
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{uris: "spiffe://example.com/*"}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{uris: "spiffe://example.com/host"}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{uris: "spiffe://example.com/invalid"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{uris: "abc"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
testAccStepCert(t, "web", ca, "foo", allowed{uris: "http://www.google.com"}, false),
|
||||
testAccStepLoginInvalid(t, connState),
|
||||
},
|
||||
})
|
||||
|
@ -1007,9 +1125,9 @@ func TestBackend_mixed_constraints(t *testing.T) {
|
|||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
Backend: testFactory(t),
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepCert(t, "1unconstrained", ca, "foo", "", "", false),
|
||||
testAccStepCert(t, "2matching", ca, "foo", "*.example.com,whatever", "", false),
|
||||
testAccStepCert(t, "3invalid", ca, "foo", "invalid", "", false),
|
||||
testAccStepCert(t, "1unconstrained", ca, "foo", allowed{}, false),
|
||||
testAccStepCert(t, "2matching", ca, "foo", allowed{names: "*.example.com,whatever"}, false),
|
||||
testAccStepCert(t, "3invalid", ca, "foo", allowed{names: "invalid"}, false),
|
||||
testAccStepLogin(t, connState),
|
||||
// Assumes CertEntries are processed in alphabetical order (due to store.List), so we only match 2matching if 1unconstrained doesn't match
|
||||
testAccStepLoginWithName(t, connState, "2matching"),
|
||||
|
@ -1314,8 +1432,17 @@ func testAccStepListCerts(
|
|||
}
|
||||
}
|
||||
|
||||
type allowed struct {
|
||||
names string // allowed names in the certificate, looks at common, name, dns, email [depricated]
|
||||
common_names string // allowed common names in the certificate
|
||||
dns string // allowed dns names in the SAN extension of the certificate
|
||||
emails string // allowed email names in SAN extension of the certificate
|
||||
uris string // allowed uris in SAN extension of the certificate
|
||||
ext string // required extensions in the certificate
|
||||
}
|
||||
|
||||
func testAccStepCert(
|
||||
t *testing.T, name string, cert []byte, policies string, allowedNames string, requiredExtensions string, expectError bool) logicaltest.TestStep {
|
||||
t *testing.T, name string, cert []byte, policies string, testData allowed, expectError bool) logicaltest.TestStep {
|
||||
return logicaltest.TestStep{
|
||||
Operation: logical.UpdateOperation,
|
||||
Path: "certs/" + name,
|
||||
|
@ -1324,8 +1451,12 @@ func testAccStepCert(
|
|||
"certificate": string(cert),
|
||||
"policies": policies,
|
||||
"display_name": name,
|
||||
"allowed_names": allowedNames,
|
||||
"required_extensions": requiredExtensions,
|
||||
"allowed_names": testData.names,
|
||||
"allowed_common_names": testData.common_names,
|
||||
"allowed_dns_sans": testData.dns,
|
||||
"allowed_email_sans": testData.emails,
|
||||
"allowed_uri_sans": testData.uris,
|
||||
"required_extensions": testData.ext,
|
||||
"lease": 1000,
|
||||
},
|
||||
Check: func(resp *logical.Response) error {
|
||||
|
|
|
@ -45,7 +45,33 @@ Must be x509 PEM encoded.`,
|
|||
"allowed_names": &framework.FieldSchema{
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: `A comma-separated list of names.
|
||||
At least one must exist in either the Common Name or SANs. Supports globbing.`,
|
||||
At least one must exist in either the Common Name or SANs. Supports globbing.
|
||||
This parameter is deprecated, please use allowed_common_names, allowed_dns_sans,
|
||||
allowed_email_sans, allowed_uri_sans.`,
|
||||
},
|
||||
|
||||
"allowed_common_names": &framework.FieldSchema{
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: `A comma-separated list of names.
|
||||
At least one must exist in the Common Name. Supports globbing.`,
|
||||
},
|
||||
|
||||
"allowed_dns_sans": &framework.FieldSchema{
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: `A comma-separated list of DNS names.
|
||||
At least one must exist in the SANs. Supports globbing.`,
|
||||
},
|
||||
|
||||
"allowed_email_sans": &framework.FieldSchema{
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: `A comma-separated list of Email Addresses.
|
||||
At least one must exist in the SANs. Supports globbing.`,
|
||||
},
|
||||
|
||||
"allowed_uri_sans": &framework.FieldSchema{
|
||||
Type: framework.TypeCommaStringSlice,
|
||||
Description: `A comma-separated list of URIs.
|
||||
At least one must exist in the SANs. Supports globbing.`,
|
||||
},
|
||||
|
||||
"required_extensions": &framework.FieldSchema{
|
||||
|
@ -77,12 +103,14 @@ seconds. Defaults to system/backend default TTL.`,
|
|||
Description: `TTL for tokens issued by this backend.
|
||||
Defaults to system/backend default TTL time.`,
|
||||
},
|
||||
|
||||
"max_ttl": &framework.FieldSchema{
|
||||
Type: framework.TypeDurationSecond,
|
||||
Description: `Duration in either an integer number of seconds (3600) or
|
||||
an integer time unit (60m) after which the
|
||||
issued token can no longer be renewed.`,
|
||||
},
|
||||
|
||||
"period": &framework.FieldSchema{
|
||||
Type: framework.TypeDurationSecond,
|
||||
Description: `If set, indicates that the token generated using this role
|
||||
|
@ -158,6 +186,11 @@ func (b *backend) pathCertRead(ctx context.Context, req *logical.Request, d *fra
|
|||
"max_ttl": cert.MaxTTL / time.Second,
|
||||
"period": cert.Period / time.Second,
|
||||
"allowed_names": cert.AllowedNames,
|
||||
"allowed_common_names": cert.AllowedCommonNames,
|
||||
"allowed_dns_sans": cert.AllowedDNSSANs,
|
||||
"allowed_email_sans": cert.AllowedEmailSANs,
|
||||
"allowed_uri_sans": cert.AllowedURISANs,
|
||||
"required_extensions": cert.RequiredExtensions,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
@ -168,6 +201,10 @@ func (b *backend) pathCertWrite(ctx context.Context, req *logical.Request, d *fr
|
|||
displayName := d.Get("display_name").(string)
|
||||
policies := policyutil.ParsePolicies(d.Get("policies"))
|
||||
allowedNames := d.Get("allowed_names").([]string)
|
||||
allowedCommonNames := d.Get("allowed_common_names").([]string)
|
||||
allowedDNSSANs := d.Get("allowed_dns_sans").([]string)
|
||||
allowedEmailSANs := d.Get("allowed_email_sans").([]string)
|
||||
allowedURISANs := d.Get("allowed_uri_sans").([]string)
|
||||
requiredExtensions := d.Get("required_extensions").([]string)
|
||||
|
||||
var resp logical.Response
|
||||
|
@ -246,6 +283,10 @@ func (b *backend) pathCertWrite(ctx context.Context, req *logical.Request, d *fr
|
|||
DisplayName: displayName,
|
||||
Policies: policies,
|
||||
AllowedNames: allowedNames,
|
||||
AllowedCommonNames: allowedCommonNames,
|
||||
AllowedDNSSANs: allowedDNSSANs,
|
||||
AllowedEmailSANs: allowedEmailSANs,
|
||||
AllowedURISANs: allowedURISANs,
|
||||
RequiredExtensions: requiredExtensions,
|
||||
TTL: ttl,
|
||||
MaxTTL: maxTTL,
|
||||
|
@ -278,6 +319,10 @@ type CertEntry struct {
|
|||
MaxTTL time.Duration
|
||||
Period time.Duration
|
||||
AllowedNames []string
|
||||
AllowedCommonNames []string
|
||||
AllowedDNSSANs []string
|
||||
AllowedEmailSANs []string
|
||||
AllowedURISANs []string
|
||||
RequiredExtensions []string
|
||||
BoundCIDRs []*sockaddr.SockAddrMarshaler
|
||||
}
|
||||
|
|
|
@ -253,6 +253,10 @@ func (b *backend) verifyCredentials(ctx context.Context, req *logical.Request, d
|
|||
func (b *backend) matchesConstraints(clientCert *x509.Certificate, trustedChain []*x509.Certificate, config *ParsedCert) bool {
|
||||
return !b.checkForChainInCRLs(trustedChain) &&
|
||||
b.matchesNames(clientCert, config) &&
|
||||
b.matchesCommonName(clientCert, config) &&
|
||||
b.matchesDNSSANs(clientCert, config) &&
|
||||
b.matchesEmailSANs(clientCert, config) &&
|
||||
b.matchesURISANs(clientCert, config) &&
|
||||
b.matchesCertificateExtensions(clientCert, config)
|
||||
}
|
||||
|
||||
|
@ -280,10 +284,85 @@ func (b *backend) matchesNames(clientCert *x509.Certificate, config *ParsedCert)
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// matchesCommonName verifies that the certificate matches at least one configured
|
||||
// allowed common name
|
||||
func (b *backend) matchesCommonName(clientCert *x509.Certificate, config *ParsedCert) bool {
|
||||
// Default behavior (no names) is to allow all names
|
||||
if len(config.Entry.AllowedCommonNames) == 0 {
|
||||
return true
|
||||
}
|
||||
// At least one pattern must match at least one name if any patterns are specified
|
||||
for _, allowedCommonName := range config.Entry.AllowedCommonNames {
|
||||
if glob.Glob(allowedCommonName, clientCert.Subject.CommonName) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// matchesDNSSANs verifies that the certificate matches at least one configured
|
||||
// allowed dns entry in the subject alternate name extension
|
||||
func (b *backend) matchesDNSSANs(clientCert *x509.Certificate, config *ParsedCert) bool {
|
||||
// Default behavior (no names) is to allow all names
|
||||
if len(config.Entry.AllowedDNSSANs) == 0 {
|
||||
return true
|
||||
}
|
||||
// At least one pattern must match at least one name if any patterns are specified
|
||||
for _, allowedDNS := range config.Entry.AllowedDNSSANs {
|
||||
for _, name := range clientCert.DNSNames {
|
||||
if glob.Glob(allowedDNS, name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// matchesEmailSANs verifies that the certificate matches at least one configured
|
||||
// allowed email in the subject alternate name extension
|
||||
func (b *backend) matchesEmailSANs(clientCert *x509.Certificate, config *ParsedCert) bool {
|
||||
// Default behavior (no names) is to allow all names
|
||||
if len(config.Entry.AllowedEmailSANs) == 0 {
|
||||
return true
|
||||
}
|
||||
// At least one pattern must match at least one name if any patterns are specified
|
||||
for _, allowedEmail := range config.Entry.AllowedEmailSANs {
|
||||
for _, email := range clientCert.EmailAddresses {
|
||||
if glob.Glob(allowedEmail, email) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// matchesURISANs verifies that the certificate matches at least one configured
|
||||
// allowed uri in the subject alternate name extension
|
||||
func (b *backend) matchesURISANs(clientCert *x509.Certificate, config *ParsedCert) bool {
|
||||
// Default behavior (no names) is to allow all names
|
||||
if len(config.Entry.AllowedURISANs) == 0 {
|
||||
return true
|
||||
}
|
||||
// At least one pattern must match at least one name if any patterns are specified
|
||||
for _, allowedURI := range config.Entry.AllowedURISANs {
|
||||
for _, name := range clientCert.URIs {
|
||||
if glob.Glob(allowedURI, name.String()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// matchesCertificateExtensions verifies that the certificate matches configured
|
||||
// required extensions
|
||||
func (b *backend) matchesCertificateExtensions(clientCert *x509.Certificate, config *ParsedCert) bool {
|
||||
|
|
|
@ -1 +1 @@
|
|||
92223EAFBBEE17A3
|
||||
92223EAFBBEE17AF
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
[ req ]
|
||||
default_bits = 2048
|
||||
encrypt_key = no
|
||||
prompt = no
|
||||
default_md = sha256
|
||||
req_extensions = req_v3
|
||||
distinguished_name = dn
|
||||
|
||||
[ dn ]
|
||||
CN = example.com
|
||||
|
||||
[ req_v3 ]
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[ alt_names ]
|
||||
IP.1 = 127.0.0.1
|
||||
DNS.1 = example.com
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIEijCCAnICAQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3
|
||||
DQEBAQUAA4ICDwAwggIKAoICAQDUJ6s97BFxR295bCjpwQ85Vo8DnBFa/awNH107
|
||||
QFn/zw0ZDdJMLtEBc/bw7pTYw5ulKbiZDFrmzPEY+QZlo+t1TeWgPRJg0CbYNukS
|
||||
aNv0vKXjDXYwbrCyOvZucy8hte6IKjZfH+kAsgbbUxfD75BCKsxMxbVHkg0W9Ma2
|
||||
pnZj/kpvQE5lkMj5mDvtWdfCRsVg4zL6jhRHkPZ6fOkF3mrfTbQu3oyOcbKLEE/G
|
||||
t3QRKw3uv0vMDmhg62ZPvD1k70UMjUV2MVqEPZuWY7/bbW8OsfzMyBOGY9LLp7QS
|
||||
krxWYRj6SPUR4f1bZq7pRbqOfS0okq/XDLf1k6Na5cT6iNdyjEVdSJl7vR7kSreX
|
||||
8hkwK46Oup8v/vJLu/cRDCpAas0gJJkJDPt5114V0/Xww7EFxs5GijXP8i5RLlgK
|
||||
/nRscbK+fgjQOnQ5cp0pcP8HAriy2vil7E0fQvMvt5QTyINEYgiYaCIT9WGRC8Xo
|
||||
WcoUGI2vyrGy6RU6A3/TKeBLtikaSPjFKa1dFTAHfrUkTBpfqc+sbiJ334Bvucg5
|
||||
WyS8oAC5Vf++iMnETSdzx1k0/QARVLD38PO8wPaPU1M2XaSA+RHTB9SGFc4VTauT
|
||||
B167NLlmgJHYuhp+KM1RTy1TEoDlJh2qKj21BLcR1GJ0KgDze6Vpf9xdRTdqMpo2
|
||||
h20wdQIDAQABoC8wLQYJKoZIhvcNAQkOMSAwHjAcBgNVHREEFTAThwR/AAABggtl
|
||||
eGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAgEAGtds+2IUVKzw9bi130mBbb9K
|
||||
CrXw2NXSp+LJwneIEy0bjAaNr7zCGQsj7q57qFwjc7vLTtGRheP5myrAOq00lp8J
|
||||
1sGZSETS/y4yeITLZSYWVq2dtF/hY9I+X3uOoibdsQgzYqhBcUr4oTDapf1ZEs0i
|
||||
wA2J5IcasfaBpWFc9wRN79BBACLGyCbX6VwUISrGe3Hgzkeqtg97cU62ecQsgXiZ
|
||||
LdQgERvC0wSfAmI4lGulXi1oYYSRxXQ8pEKEAMeJrJVQfvhdbS/o4Bdf3Yj6ibtD
|
||||
tFSdKLcdRCfMQBEHNpSh665LfBbwU55Fh89tBdGmf6uqsimUY6AxNncnLsc1Kq6F
|
||||
oINXix3GsBNmCahDeHdGOlNjw0Lpl0m6bnu6LXSDwwuNWAEdDfEmxR+5T/GkGxcG
|
||||
TTWPwEkpnCe4VmGl9Y10uPSvqneNsdNWjDVK4BeW4VSf9Lp1Zeme1dYFvpyzow+r
|
||||
4ogpvMPf5vy5I/0HCEf1KlaPyhs8ZGK6YBGaeEDYSaysAWJfYm8eiqwUuKYj/FUe
|
||||
G3KkaFpOGsQHFNRtG8GukV3r2AK97HFHKNfygZ2xvk5isXz2ZsNX1/J0+GGjalJl
|
||||
cWBBEiXFM94XJHE9rACsL2UKn8cWCh9lHNLlePOkQuoNY9CUd63xx4Hg97XWP3+U
|
||||
DhpG7CADsKcPJfbMgrk=
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -0,0 +1,23 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzzCCAregAwIBAgIJAJIiPq+77herMA0GCSqGSIb3DQEBBQUAMBYxFDASBgNV
|
||||
BAMTC2V4YW1wbGUuY29tMB4XDTE4MDQyNjEyMDEzMFoXDTE5MDQyNjEyMDEzMFow
|
||||
FjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
|
||||
ggIKAoICAQDUJ6s97BFxR295bCjpwQ85Vo8DnBFa/awNH107QFn/zw0ZDdJMLtEB
|
||||
c/bw7pTYw5ulKbiZDFrmzPEY+QZlo+t1TeWgPRJg0CbYNukSaNv0vKXjDXYwbrCy
|
||||
OvZucy8hte6IKjZfH+kAsgbbUxfD75BCKsxMxbVHkg0W9Ma2pnZj/kpvQE5lkMj5
|
||||
mDvtWdfCRsVg4zL6jhRHkPZ6fOkF3mrfTbQu3oyOcbKLEE/Gt3QRKw3uv0vMDmhg
|
||||
62ZPvD1k70UMjUV2MVqEPZuWY7/bbW8OsfzMyBOGY9LLp7QSkrxWYRj6SPUR4f1b
|
||||
Zq7pRbqOfS0okq/XDLf1k6Na5cT6iNdyjEVdSJl7vR7kSreX8hkwK46Oup8v/vJL
|
||||
u/cRDCpAas0gJJkJDPt5114V0/Xww7EFxs5GijXP8i5RLlgK/nRscbK+fgjQOnQ5
|
||||
cp0pcP8HAriy2vil7E0fQvMvt5QTyINEYgiYaCIT9WGRC8XoWcoUGI2vyrGy6RU6
|
||||
A3/TKeBLtikaSPjFKa1dFTAHfrUkTBpfqc+sbiJ334Bvucg5WyS8oAC5Vf++iMnE
|
||||
TSdzx1k0/QARVLD38PO8wPaPU1M2XaSA+RHTB9SGFc4VTauTB167NLlmgJHYuhp+
|
||||
KM1RTy1TEoDlJh2qKj21BLcR1GJ0KgDze6Vpf9xdRTdqMpo2h20wdQIDAQABoyAw
|
||||
HjAcBgNVHREEFTAThwR/AAABggtleGFtcGxlLmNvbTANBgkqhkiG9w0BAQUFAAOC
|
||||
AQEA2JswcCYtHvOm2QmSEVeFcCeVNkzr35FXATamJv0oMMjjUFix78MW03EW6vJa
|
||||
E52e3pBvRdy+k2fuq/RtHIUKzB6jNbv0Vds26Dq+pmGeoaQZOW94/Wht7f9pZgBi
|
||||
IRPBg9oACtyNAuDsCOdetOyvyoU29sjUOUoQZbEXF+FK4lRJrEmZUJHbp/BOD58V
|
||||
mQRtjTMjQlZZropqBQmooMRYU0qgWHaIjyoQpu2MgEj3+/1b1IX6SCfRuit0auh/
|
||||
YI3/cCtyAG/DpZ6zfyXuyY+iN+l8B6t0nXyV3g8JgBWYPGJv1hgVIgnnqlwuL517
|
||||
mEAT5RnHCNJQNuzS1dwfuBrX3w==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDUJ6s97BFxR295
|
||||
bCjpwQ85Vo8DnBFa/awNH107QFn/zw0ZDdJMLtEBc/bw7pTYw5ulKbiZDFrmzPEY
|
||||
+QZlo+t1TeWgPRJg0CbYNukSaNv0vKXjDXYwbrCyOvZucy8hte6IKjZfH+kAsgbb
|
||||
UxfD75BCKsxMxbVHkg0W9Ma2pnZj/kpvQE5lkMj5mDvtWdfCRsVg4zL6jhRHkPZ6
|
||||
fOkF3mrfTbQu3oyOcbKLEE/Gt3QRKw3uv0vMDmhg62ZPvD1k70UMjUV2MVqEPZuW
|
||||
Y7/bbW8OsfzMyBOGY9LLp7QSkrxWYRj6SPUR4f1bZq7pRbqOfS0okq/XDLf1k6Na
|
||||
5cT6iNdyjEVdSJl7vR7kSreX8hkwK46Oup8v/vJLu/cRDCpAas0gJJkJDPt5114V
|
||||
0/Xww7EFxs5GijXP8i5RLlgK/nRscbK+fgjQOnQ5cp0pcP8HAriy2vil7E0fQvMv
|
||||
t5QTyINEYgiYaCIT9WGRC8XoWcoUGI2vyrGy6RU6A3/TKeBLtikaSPjFKa1dFTAH
|
||||
frUkTBpfqc+sbiJ334Bvucg5WyS8oAC5Vf++iMnETSdzx1k0/QARVLD38PO8wPaP
|
||||
U1M2XaSA+RHTB9SGFc4VTauTB167NLlmgJHYuhp+KM1RTy1TEoDlJh2qKj21BLcR
|
||||
1GJ0KgDze6Vpf9xdRTdqMpo2h20wdQIDAQABAoICAQDJxszUQQC554I7TsZ+xBJx
|
||||
q0Sr3zSWgOuxM2Jdpy+x38AKUx3vPRulsSBtN8yzeR9Ab7TVQ231U3f/E2GlK8kW
|
||||
sTazN0KSd4ZqX5c+3iJM21s+3p/JIo3FhdS5aa2q9zjdoqBByry135wr3xScUu22
|
||||
MLRMVEG8x0jRy45vS1UQd1teAiBN8u1ijgp5DNjrOpohMxVaPeVFx7bU+pY58bdd
|
||||
mK7FYP73v2VbY/EsA3FNntBKgQBbHFzjyR9uuI7/v53BeV9WMUxwt5OR7l8cGDHn
|
||||
HRtdvPDtAWYMMf1PKOYdlY3HBbqn/nMUCk5TKPFs8dsQWqsI8lzIIVndauj0i0+0
|
||||
M/lVMXu4x48o5FfLa4HjkpcDxAU6QDHA9thaDkasZebixVH/p1ZJkLORl5jDLYkU
|
||||
Av+B3i1efITwNYgosZNjPpw0PyYh9PV9JvB87d5wFpgISfZyRXpBVGeJbt6gg++8
|
||||
8/5A/GzSpGy0FhLcP3vuVTcX2VOexjqeaoi4U3cHrbWv/wNj5a4BNk5EJT8fVeSb
|
||||
+Emqydl9u3n2E315GPC8kwxdE3r3hGrWdZQn9byGvqzwDaLWXQLQWvQN4GOpGTrP
|
||||
Yxj2Oi8s1MJHkppj4eo52O4J7cBlAJn3RFmlCKGOoWJZMdPktp/gWeT+xIGSaa21
|
||||
qB+l/ZFEWLPMxdTBMGFmYQKCAQEA8DgozaZBXr7mb1H25EbW9YmtLc61JMapQrZb
|
||||
ObygiGR6RZsxCXEvGdhvvmwpO8rA9wAOWqI8NV5GU8EvuRuwvGoX4HqbXkB6ZcyC
|
||||
6RuZzki2lrKVGUaLc1v6MyhX4IzrqTYWDgQvwd9lMcUGR7r007KPE5ft4v3/TuxQ
|
||||
qPKxQE7NO2xnTloUchd5g0/d975GZi0g6XDecFOuj43Pi0c/wRcFH6zfVirdcm+M
|
||||
yP9CsJ/LUgtV1voLqyhfybwHvzpxJ0l25Fw+P85I4czosBp+FaFAwogxZEDnY8Fr
|
||||
Hqcwdc7vwGDjTbtflDsUdppt2h8nD8bBZGysG8+P8HAt3i5D+QKCAQEA4heKueRQ
|
||||
Y8nTZlmRSRtB6usRAeymQBJjO+yWwu/06Efg8VW5QRwtP0sx+syrLaQDy8MT07II
|
||||
XQZmq55xATWbHCxULiceIY2KG5LHCovVotYAll8ov58exJva19C7/41uVrkl3H9j
|
||||
xFLX0Bn3zMFKBOxKhygP2xqqEJdb1JJt27c2CbXvXOzqIZ4RCaNQdBdrlEiXQihR
|
||||
JCGMUBfrYIwALQFzYuPGULhg77YcAi5owCPnfK+dDOOvMmW8BwPnRUc14WFIVV+m
|
||||
dbY22WonLNPP055W5755Xl9RHKW1bcmIH6E4QZpMrlnd1UzPBQq1PJtcO3uRc5T6
|
||||
CMQSUmwMGSQ3XQKCAQBiuVHborY+8AnYOkFTc+GoK5rmtosvwA2UA0neoqz/IPw3
|
||||
Wx5+GOwYnSDfi6gukJdZa8Z6bS59aG9SwJSSaNTrulZxxTHRPIKRD8nFb7h4VN3l
|
||||
dSNdreZl1KkxGSV0fbXkZvwNap8N+HeoSqbYF/fCgSHYFZqIrYadsvU7WfKK0Vf7
|
||||
UgPq6Y55jTg9RTeeN67LE0Txa5efZmTZTpi7Tt7exk0uxWdMDHXSMBIWEQIhgKqY
|
||||
31u57C2bfA5R5FrytlwGn2SjWV2j7214jzQaG+kxjoIE8OALqbjvAHC7uk5qPE/A
|
||||
KpGAQr93Ngik7baz7BWroC2eziK1k0o+sHvJUg5RAoIBABF+ftZ5axr9j+T4gzxj
|
||||
5orV24AJnqeQhKsrWFMHHC0o+qfR2T7HflzKZbihQ5GJgl2u34be3LTN/P3Eibvt
|
||||
OO5KI81aa4NvH0OY7NvNDB/IbU01WcLR/iB6asmONi3E9MezFdHk7YRQYLCSgdEP
|
||||
F7ofyniAyhFLE+OqwolFN0jr+TtxH29SSZ+GSo0zXNNOyJ01rLaKxhSEoAXGhAj5
|
||||
bD4PQa1iMIMocR+7OJmWm7ZaUNwd/onzyCefJZhpXejHZMzmqSEqAIhVLBNQmm1m
|
||||
iks2kkTmQR/jQjR0QgCXunewEtlIpixLedW6Vr5uIK3q240it5N48IvjGAPWpmz/
|
||||
l2UCggEBALRlARlBdYcPhWbcyhq9suq2PJqHVvNdP9vVTZpPgmWgxmj9mPQ731Q/
|
||||
UpRlBIk6U0qAxBXP9kzPqSivWhY8Jto80Jdr+/80PkdANDkpnXoDlxPk095sD2uN
|
||||
Jv5FffFgMZH9MGpPTEuZ571/YtVi+1qFt0i3oazpF/g8gU23f2oxaX4xzsltVl8J
|
||||
rWXYzmYE0i5Qiy81+zZ9dZlnmlKhcYpD6m2t/0hRAoNaoxOUV7WFcIzYIxpKvzYL
|
||||
QTDL/Se2Ooc0xLQvM1oZ9/1NE2hpGQ/ipASEPlx9KO5ktYW7+LwdcSCMXtx84I/D
|
||||
VQpWjPdILMpiVrB/9NsENTNv2DUvc+o=
|
||||
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,17 @@
|
|||
[ req ]
|
||||
default_bits = 2048
|
||||
encrypt_key = no
|
||||
prompt = no
|
||||
default_md = sha256
|
||||
distinguished_name = dn
|
||||
req_extensions = req_v3
|
||||
|
||||
[ req_v3 ]
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[ dn ]
|
||||
CN = example.com
|
||||
|
||||
[ alt_names ]
|
||||
IP.1 = 127.0.0.1
|
||||
email = valid@example.com
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIEkDCCAngCAQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3
|
||||
DQEBAQUAA4ICDwAwggIKAoICAQDO7stcdJQwUtVeJriwpAswDAirO827peSlgdxs
|
||||
fW8X9M2hE9ihvESEILb7TRMRiFlDyQYg1BxKMrJ0DZmixFi8RvUCZbH6TFOUMsk+
|
||||
w1FhpzjuqAqxNQ51s7u30sfruJg7XN3YJLEPelom62wvzhvLXJFLQZlQCDrMx+PC
|
||||
ofWs4IA7jR8JaXZjIGdkEU0GgRPy8zKPUe3dUBHi2UR4eKT4cRCn4IwCrFx4BQjV
|
||||
AxNKNDGpe+fTVOzII/UX+FppDdGZZ4g0y3E1mQUEKkff4dKCK7vhlGJR9D+5v/V0
|
||||
/stwP72aXczijuVtnXXyli+oj24NaijoqQluNCD3MvV/INovLL2Tyk54H3/GvpU1
|
||||
+sJbpE2+UPh+Rh8DNkT6RPRguymJO8MSsdLt/qvVD8BlZ7I9V3XZlDKosCRTUyxf
|
||||
jjFpa+VzB3nt7uFtIXZ9HNGhQIpOULvkFGizWV+tS8PpGdTFVzDjyWg0HUKWn8g8
|
||||
IiWR9S40h6mHjVuTuxA9tlO69PuTjGK7MlAvFTDaPC8seau1LUiqtQ+prnSLI0h1
|
||||
6GfI9W2G7BKKVPloErODhLcsOcwRcmaJVW+yBda3te8+cMBIvtQYKAYSCtg8qXws
|
||||
xyfPLo4GChbGGRbRCuM3mB1lG1qHEivJ0vynsgolp0t8jaXSFVBVgYj+C6Vd9/hl
|
||||
ieUcOwIDAQABoDUwMwYJKoZIhvcNAQkOMSYwJDAiBgNVHREEGzAZhwR/AAABgRF2
|
||||
YWxpZEBleGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAgEAe1u3wYMMKaZ3d5Wz
|
||||
jKH971CF3sl+KYl+oV0ekD8dbYe+szERgajn6Y5IYVxxi5o9UgeAWqnyHCiqsW8T
|
||||
MQdUwMa67Ym/pHKSVoBlGePAKHqock0+iSsVBMcPpU9RkxdSW2aVtdb0DGfyB952
|
||||
t3dSb0LaITu30fe8p7lxrL0DKESbwd4N2XQE1F5Vf+1OodvpJinn4Wqzn45hqRf0
|
||||
imxrCgVjT5VtR+NRzKCK3Msmh+cJGpR3zgXwGKqgHLWzhvSoQwRWYE3apMK5xLk7
|
||||
N1sWVxEKK5+L/CDaMNGQFx5lPiCN3bUudCq4uSZcPLO5AuDpSeLKnknBrWA6HcbB
|
||||
WvnwtKmHeVe2qogPViKGuwE16rnPlp9hysPl2ckmtqEsXRagIAh5fMI3OoRbZmVV
|
||||
jfJm21U4YkUWuMKet3EU1StT6T8T6O7QEFA4w4s5+m3dsjDZ9iTuK9/dCs1xnIke
|
||||
4uJYmh3YrNl8IjMffJuWxA+/de3JO1UljC2EAFxa5KAc24+qyeWwky4tMv72gTOp
|
||||
6q3k2wnsrK5B1errRV37OLgxtoh1I3Rgp+0B77SOK/PpD/JJazJG5O9bBJOvHJc0
|
||||
STW9Td2CzgC2lKGfvkX6UYgVy/9HDq7/EKXP/G2f3kRik2NPUhGcnAH9nyL9SvpP
|
||||
+T4CZ+FumDj5DulARk6arSq+uy4=
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -0,0 +1,23 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIID1TCCAr2gAwIBAgIJAJIiPq+77hevMA0GCSqGSIb3DQEBBQUAMBYxFDASBgNV
|
||||
BAMTC2V4YW1wbGUuY29tMB4XDTE4MDQyNjEyMjE1MloXDTE5MDQyNjEyMjE1Mlow
|
||||
FjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
|
||||
ggIKAoICAQDO7stcdJQwUtVeJriwpAswDAirO827peSlgdxsfW8X9M2hE9ihvESE
|
||||
ILb7TRMRiFlDyQYg1BxKMrJ0DZmixFi8RvUCZbH6TFOUMsk+w1FhpzjuqAqxNQ51
|
||||
s7u30sfruJg7XN3YJLEPelom62wvzhvLXJFLQZlQCDrMx+PCofWs4IA7jR8JaXZj
|
||||
IGdkEU0GgRPy8zKPUe3dUBHi2UR4eKT4cRCn4IwCrFx4BQjVAxNKNDGpe+fTVOzI
|
||||
I/UX+FppDdGZZ4g0y3E1mQUEKkff4dKCK7vhlGJR9D+5v/V0/stwP72aXczijuVt
|
||||
nXXyli+oj24NaijoqQluNCD3MvV/INovLL2Tyk54H3/GvpU1+sJbpE2+UPh+Rh8D
|
||||
NkT6RPRguymJO8MSsdLt/qvVD8BlZ7I9V3XZlDKosCRTUyxfjjFpa+VzB3nt7uFt
|
||||
IXZ9HNGhQIpOULvkFGizWV+tS8PpGdTFVzDjyWg0HUKWn8g8IiWR9S40h6mHjVuT
|
||||
uxA9tlO69PuTjGK7MlAvFTDaPC8seau1LUiqtQ+prnSLI0h16GfI9W2G7BKKVPlo
|
||||
ErODhLcsOcwRcmaJVW+yBda3te8+cMBIvtQYKAYSCtg8qXwsxyfPLo4GChbGGRbR
|
||||
CuM3mB1lG1qHEivJ0vynsgolp0t8jaXSFVBVgYj+C6Vd9/hlieUcOwIDAQABoyYw
|
||||
JDAiBgNVHREEGzAZhwR/AAABgRF2YWxpZEBleGFtcGxlLmNvbTANBgkqhkiG9w0B
|
||||
AQUFAAOCAQEAp2T99t93hxPyCDaqfTF0lsdzIgxZ5GkSzYTYQ2pekLfMDUUy4WFQ
|
||||
AppdnSJSpm6b+xWO2DkO8UAgOdSEORf/Qpfm+UpHaEYZlQiWQ0zNmIQgBoh6indU
|
||||
bEZKeL6aAOfIshPNfmqjFt+DpEClrQvCHJggG/rB77Ujj6hPY2+8h4JjbjeX7Pe9
|
||||
oUEx9LpZ5Qpo6PK5vB537PP7Q2qp2PIr29DLz1VeLCbqUnV+j7qT0T3hhqurnpTA
|
||||
QUiRZI0etgeP/B5lw/S4AWijq+R6RasdPAS4UNHsYK+PSGiqdhW/bJvSx5UBXQbk
|
||||
DuY2A4kdv60H5Aw45/F6enH2Fg1kg7PlQA==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDO7stcdJQwUtVe
|
||||
JriwpAswDAirO827peSlgdxsfW8X9M2hE9ihvESEILb7TRMRiFlDyQYg1BxKMrJ0
|
||||
DZmixFi8RvUCZbH6TFOUMsk+w1FhpzjuqAqxNQ51s7u30sfruJg7XN3YJLEPelom
|
||||
62wvzhvLXJFLQZlQCDrMx+PCofWs4IA7jR8JaXZjIGdkEU0GgRPy8zKPUe3dUBHi
|
||||
2UR4eKT4cRCn4IwCrFx4BQjVAxNKNDGpe+fTVOzII/UX+FppDdGZZ4g0y3E1mQUE
|
||||
Kkff4dKCK7vhlGJR9D+5v/V0/stwP72aXczijuVtnXXyli+oj24NaijoqQluNCD3
|
||||
MvV/INovLL2Tyk54H3/GvpU1+sJbpE2+UPh+Rh8DNkT6RPRguymJO8MSsdLt/qvV
|
||||
D8BlZ7I9V3XZlDKosCRTUyxfjjFpa+VzB3nt7uFtIXZ9HNGhQIpOULvkFGizWV+t
|
||||
S8PpGdTFVzDjyWg0HUKWn8g8IiWR9S40h6mHjVuTuxA9tlO69PuTjGK7MlAvFTDa
|
||||
PC8seau1LUiqtQ+prnSLI0h16GfI9W2G7BKKVPloErODhLcsOcwRcmaJVW+yBda3
|
||||
te8+cMBIvtQYKAYSCtg8qXwsxyfPLo4GChbGGRbRCuM3mB1lG1qHEivJ0vynsgol
|
||||
p0t8jaXSFVBVgYj+C6Vd9/hlieUcOwIDAQABAoICAQDFitqh6TxqITlFBwv6vK9d
|
||||
b696371XrFdo1F57RwcdxHnkklCUnWh/BcgIgJx6eUJV3nq2LibPgjQva6hF5NCc
|
||||
89QDNNfBjMmgyRaqjsSKx5sm4U5Lus2R+UFzi4mEcpUI3m99XhGVKAUV8Fo4DLcl
|
||||
3LlrMTVNXH3dbdj0va4NGcfwkZiWYJI+sPliYs24LtK/dADJJro/MqfQef7OTsWV
|
||||
0kHHMSoXhzlC7fNvfd8VUFw0Ym99pC3iJclc155feWyk2FwDok7xjqFmR4KTrD1M
|
||||
PLm/7+ooOFX5WdHVnULSZlb3HSJxCV7l1JJ7QXo/nKS/s59X875n8OWjdoc7lD4T
|
||||
Xw/K9CzJCyhJ/HDhTAea1+MNTig4a6wjdSim6vasig/Gkot6jjS2lhnZae8ZhYxP
|
||||
GUx4JcPthHppgHt8s6Jb2PHuqNVRmVB0x41c5mmXOnJcSqOX0XhbSbeS1TUV8BiC
|
||||
HMaa+agt7RpQOb5uxpb+Hath/88tsjDXI0ZHNAG43ndkHxSQQ9P/q/m5uaLwuJyo
|
||||
Yb06yUy/g7ceXpJFjGsjO+33DmamvligqOswgg+oazMFo8S9ZUJw6sSXhM/XiHla
|
||||
JOj+Vatfj0ViVcaGlO2kWughuCT5thn92bgC9V2VnJhbaSzSaQlRphlbuSYJEYj0
|
||||
S1uIbwPzTrcBQuekwY50YQKCAQEA/vve5K/nAnw4KLSrKwwCp9trYSm8C5czv2jV
|
||||
tn6vQtckQMrw/hubX7TcTTgTuGboGdHMwZMFJBKpx6AlRHCR5IBw8fR1z+58c+2V
|
||||
VJgllc23eKwCcBMKoe6LmsiUXOWmc7MuHc+qQS+9OemO93nNafsSwFCkucBFQs/3
|
||||
Yx7J3zNvMOuy+dq3jrxO0xl2jBF0pcmJF/czrvbMCD7tvDntgqvpAnybgrwm2cu3
|
||||
Q5F6i+E5w6VDhCprQL/aK95iT7cPmfdGxsUCdfNzDGIJFHZp2Hrar1TsOP6ESsDl
|
||||
Q/Oz9oO1vMy7MymJjWFoVELBlCBxDEgubyM1f8cE1tQ6UAqFSwKCAQEAz8HnKWPe
|
||||
NWZtqdAzSmY+3ZxSe1BbukOo4XtCV8LfRHGazKpXMTqsO9l7ynK7ifXv3b3GHTr+
|
||||
ck2Af/vyiVx6f7Ty2dmBotFQDzg0HfKD2skAPyH8cHpA8TUeL3yMOR3XQU5/pOnG
|
||||
tn84n7KWpAyZXh8gzMnmzWjMlb9pUlkKcATUj0gb8iSa9PV0zBwMKYKY0ngznJT2
|
||||
CgE1vhy59rpuUVMrQ8i5iW9jbqYVrqID+ta2DWgcLsEXft7jKfupnRHF0Dvc650p
|
||||
+Lkxv0YgKjUg5sYc2QJbIiBxXaW0cTRrw/KfOe4kvdG5RMF60Six+W1DIW2l+qi3
|
||||
irnDRvRm1N6e0QKCAQEA86d5MaxJIl3TSEqEeikK7J3GuV0pHSZKQ7EI70+VaFiv
|
||||
gt6qdReqXEU2cu+QIJjtV6bcc2lq8zKGXITSt9ieAO0fgIWqgpyQ/jJcjS6qU8D1
|
||||
fnFYDwKTGXQaoTjkVPT6HvtsqP4E4i+dMZbWj/MrcAeEvpMRJZLuXE7gRi5ol0nO
|
||||
CcBhEVKILvQQmrZtSqFvhvDTeTw2fg3FoGeJw2DTbheaHE84RzBGK774C7Abm0kI
|
||||
asUkhEoInSH3eA4UgbobRXQ+hLhDhrSxDncr2ArjUALtr7eF11yWy9wR+OIK6Rio
|
||||
9JXqmJQrphcbm9ECq+poPGVJQdgySjzCigrZAh1biwKCAQBiBnFVXCOaOov/lZa9
|
||||
weRjl8BrIo1FI2tpiEjTM8U4fAm4C588QRzG2GTKLrxB6eKVU1dIr28i62J4AJ59
|
||||
JT8/RldXZoL+GZiWtcQRZT3FWxVctGJxh51gsdleOnvG70eDLtCXNR5nOTu0TgU5
|
||||
viAXAsTtG05lGM9+0GOXUR/VntHUEQfuhkr+zVmgfJNYeqA0njZr6PT134BGBTPR
|
||||
MEGg6Yb+YpT4PbBCouaUESmjju8zAC5b+Qtm9y9jvbRXwez9xWEFYpBNJMROJX5D
|
||||
q/GsMUmnMq9hOMGEmAy9ZSh7udxa7vwy++NYh5m1Wmgu8di8ywmHbVe8gs2aivKB
|
||||
+dAhAoIBAQC7nSuRSmRGeKJCAqikMFPVdFdLaTnEw4i/fcyYsPa+o2hTWXyLll8K
|
||||
lwSnBxe+BCvdeQ8cWzg3rPaIBVzUjDecZjdwjcHnhlHvgHjEvFX339rvpD7J2HIb
|
||||
DaVqvtPniCFdNK4Jyvd3JMtNq34SIHFAcmB9358JuKsOwCmk8CpMAqKPVsKj7m6H
|
||||
ETISh/K8aI2vZxVZ4WN4FsQTCqmtQDXFSGpZF5EZSpMJIB3ZZLt2jyyDW2DaZ+1T
|
||||
yuVl9jU56fTtacQROQY7cvrwznX0lFpmniwl0Aj0wln/svFAqKo1+RujqApw5iYn
|
||||
ssH1dH2tESx6RpMMyLYihjHVDC/ULUVu
|
||||
-----END PRIVATE KEY-----
|
|
@ -10,12 +10,7 @@ distinguished_name = dn
|
|||
CN = example.com
|
||||
|
||||
[ req_v3 ]
|
||||
subjectAltName = @alt_names
|
||||
2.1.1.1=ASN1:UTF8String:A UTF8String Extension
|
||||
2.1.1.2=ASN1:UTF8:A UTF8 Extension
|
||||
2.1.1.3=ASN1:IA5:An IA5 Extension
|
||||
2.1.1.4=ASN1:VISIBLE:A Visible Extension
|
||||
|
||||
[ alt_names ]
|
||||
DNS.1 = example.com
|
||||
IP.1 = 127.0.0.1
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
[ req ]
|
||||
default_bits = 2048
|
||||
encrypt_key = no
|
||||
prompt = no
|
||||
default_md = sha256
|
||||
req_extensions = req_v3
|
||||
distinguished_name = dn
|
||||
|
||||
[ dn ]
|
||||
CN = example.com
|
||||
|
||||
[ req_v3 ]
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[ alt_names ]
|
||||
DNS.1 = example.com
|
||||
IP.1 = 127.0.0.1
|
||||
URI.1 = spiffe://example.com/host
|
|
@ -0,0 +1,17 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICpTCCAY0CAQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDEtoz6THzA8RFNJ+wu40Pa30Inyprv3xRGYA71
|
||||
0T3yLrWUA0xaS8i7HHXDaEVmtHi7I+dFRqGwCgtDLY3sXN1C1t/U6V6xhhQ1hRW7
|
||||
PJhbGfsfi8uBx83amWiSMlmEBYPryQzPS+8mmRErBi6EdmgbdGWV5IcovMddDxE1
|
||||
Npc1vwmTxDUOe6mRSa8UkaR9nwFl8LTz9clIkGlOJLHWD2oX15PVr7SKYco+MrIh
|
||||
HLKkYMgATFJ05EKLyRxO/lQWD6ibUYJuGhFeNyjk34swl3uoWQBGndxcs2BQP4OL
|
||||
EfnsoXVDrHwjZ1FWSu/Bf6TfKvwo5It1IZLnm+cCTqxCnaLRAgMBAAGgSjBIBgkq
|
||||
hkiG9w0BCQ4xOzA5MDcGA1UdEQQwMC6CC2V4YW1wbGUuY29thwR/AAABhhlzcGlm
|
||||
ZmU6Ly9leGFtcGxlLmNvbS9ob3N0MA0GCSqGSIb3DQEBCwUAA4IBAQBw2y7bPrLk
|
||||
B7DrZRvO/s8yj/Mi2iS/q3KEACEUxHTXH9GrqnQJ1n00WjaEu5JgXW8F08738nj/
|
||||
QhO5IM9ZMBtFyt9/GguZzGWnGUGUvtfM/ps/qzF6lAnjxYnFfqJeDWhg4SQsW6ZW
|
||||
eFZ3S1kx0iQjy+Y7oWZNObbgDhszdJa6swN1WJBB8BZuiDJYXMBzfWdR6aZStJ0Z
|
||||
lUHyaQbILXRc+meuDY7KeILJhldlE8oU/NENO1w1WXcsseXg8790pPYg+uR/uXg0
|
||||
0iWPtqgjO+55eAvkZ5nY0N/kABV1oaCB8bVs6/2HPqquPX6c+xkcUI/HY8SJgWzk
|
||||
AHCG7VIB4W94
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -0,0 +1,18 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIC6jCCAdKgAwIBAgIJAJIiPq+77hekMA0GCSqGSIb3DQEBBQUAMBYxFDASBgNV
|
||||
BAMTC2V4YW1wbGUuY29tMB4XDTE4MDMzMTE2MTE0NVoXDTE5MDMzMTE2MTE0NVow
|
||||
FjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQDEtoz6THzA8RFNJ+wu40Pa30Inyprv3xRGYA710T3yLrWUA0xaS8i7
|
||||
HHXDaEVmtHi7I+dFRqGwCgtDLY3sXN1C1t/U6V6xhhQ1hRW7PJhbGfsfi8uBx83a
|
||||
mWiSMlmEBYPryQzPS+8mmRErBi6EdmgbdGWV5IcovMddDxE1Npc1vwmTxDUOe6mR
|
||||
Sa8UkaR9nwFl8LTz9clIkGlOJLHWD2oX15PVr7SKYco+MrIhHLKkYMgATFJ05EKL
|
||||
yRxO/lQWD6ibUYJuGhFeNyjk34swl3uoWQBGndxcs2BQP4OLEfnsoXVDrHwjZ1FW
|
||||
Su/Bf6TfKvwo5It1IZLnm+cCTqxCnaLRAgMBAAGjOzA5MDcGA1UdEQQwMC6CC2V4
|
||||
YW1wbGUuY29thwR/AAABhhlzcGlmZmU6Ly9leGFtcGxlLmNvbS9ob3N0MA0GCSqG
|
||||
SIb3DQEBBQUAA4IBAQDhR59hSpL4k4wbK3bA17YoNwFBsDpDcoU2iB9NDUTj+j+T
|
||||
Rgumt+VHtgxuGRDFPQ+0D2hmJJHNCHKulgeDKVLtY/c5dCEsk8epLQwoCd/pQsNR
|
||||
Lj102g83rCrU0pfTFjAUoecmHBFt7GDxVyWDsJgGItMatPQuWyZXTzO8JdhCfpMP
|
||||
m7z65VYZjIPgevpSR5NVJDU8u2jRCkRQBFqOXotJS6EObu4P8aly4YhwiMf1B0L8
|
||||
60XHbBksOQSZOky37uFhaab78bAu5nd2kN1K4qSObTJshCZAwRYk0XdCjDrMcZRJ
|
||||
Fp+yygib+K8e7o71Co0zUdSU0yxOKGsWvjz1BUVl
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDEtoz6THzA8RFN
|
||||
J+wu40Pa30Inyprv3xRGYA710T3yLrWUA0xaS8i7HHXDaEVmtHi7I+dFRqGwCgtD
|
||||
LY3sXN1C1t/U6V6xhhQ1hRW7PJhbGfsfi8uBx83amWiSMlmEBYPryQzPS+8mmREr
|
||||
Bi6EdmgbdGWV5IcovMddDxE1Npc1vwmTxDUOe6mRSa8UkaR9nwFl8LTz9clIkGlO
|
||||
JLHWD2oX15PVr7SKYco+MrIhHLKkYMgATFJ05EKLyRxO/lQWD6ibUYJuGhFeNyjk
|
||||
34swl3uoWQBGndxcs2BQP4OLEfnsoXVDrHwjZ1FWSu/Bf6TfKvwo5It1IZLnm+cC
|
||||
TqxCnaLRAgMBAAECggEAYLdYbR/6HmroFMVSLGN000H9ps7IirNlpoxIDrhH+rDY
|
||||
eeN9QNAN62E8zUyRAsQsr+YhKUBm8sSdcPQO2W13JAu9lVMAScwgV4gNfTd3uSL3
|
||||
AzWaYz63iYjvjyHOPUjw6Za6A5nUBWgwtrSdXmdRHF6IK8Bma7MVWj20OjOS+MsM
|
||||
ScXk+yMTzpQYZ+AhP6rgcccn6djtk+Mqrpa7yW5cTDkQ0+/MF0KR7tYUbakRSimI
|
||||
Ph6e+zFt4infOWP5fDr0oSpMXA2chh0INTtxbltnJzvaaPF8LSzyihWTZszABc84
|
||||
Ckgrvmt5DViYbmfKHk0csS/xF/wdygfkkJHML8l/IQKBgQD9CMaDgfpM78uH8Kgm
|
||||
Ja/ANu4Te5zO/n5E96PHdvCN+m7pCMgYuXuKgXDADgD1O6MItzDnEpkubluffARf
|
||||
1eJyw9ner0tTAs8bZgtKdLQvaghq5Afk1+m8XDTskJsVLVGrozvJLuabPqnZrkRH
|
||||
AxLdZjiAh6z2csFVYTQnMQSfhQKBgQDHBMjapcDx9y/jUq/yoFvwkdT3THQO9JgK
|
||||
XC5NOHGVhyT3695wpqi/ANA4b8P9MmAzcUkT8a3jcqV87OIQmK3Y1oGvjHQCKS60
|
||||
OYE9TadpxwW2uzxS5T7YegXf5L3uHinoWHlLklN+Q9pvJStw4QrDzhd8rtcZA+FN
|
||||
KBmjzYdJ3QKBgQDYutl97qi7mXEVgPYlpoYA94u4OFq5mZYB8LLhuGiW03iINbNe
|
||||
KhE9M12lwtjjNC+S2YYThgSaln/3/LuqcoLBlitY54B3G6LVbvQg1BE5w3JuS97P
|
||||
Dnjvk3LpZXrQCr83altdGMUBGA1XnEJzKJjR9ipTPOLTPLuIK/gF0aCKGQKBgQCm
|
||||
ZFitfZGge4M9Mt/KIcpciwCcNf5+ln8bglBv3XYRhykgYsLaOmyxLLPpy3/4DAsk
|
||||
V1263//7PtofZUnoiE4pEcbhh7NiLx5OLhngsDD9Hhmn2kkoIWR2xyZsN6mYEP4G
|
||||
tRnMVi2aTo6tCE2WlYBTjtZSNze9QWI4CQPO0MKAvQKBgQCzpJAJXl04zQv9S5uW
|
||||
pH3xShmd0Zjv9tNyOVNqWUeg47IFzNC2w/6FqYkhd9C4DCAibzPx7WkVjYAR+ivY
|
||||
NQv1usVhV3maJX5rw+C4Zck8kAmiqMbLacUVdy/5E2Mbk7xqjAvu+qrMFdSk/2GR
|
||||
raR1xOEvE0cKWIwr8c8wIva4wA==
|
||||
-----END PRIVATE KEY-----
|
|
@ -2,16 +2,9 @@ package ldap
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/vault/helper/consts"
|
||||
"github.com/hashicorp/vault/helper/ldaputil"
|
||||
"github.com/hashicorp/vault/helper/tlsutil"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
@ -19,105 +12,7 @@ import (
|
|||
func pathConfig(b *backend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: `config`,
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"url": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Default: "ldap://127.0.0.1",
|
||||
Description: "LDAP URL to connect to (default: ldap://127.0.0.1). Multiple URLs can be specified by concatenating them with commas; they will be tried in-order.",
|
||||
},
|
||||
|
||||
"userdn": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "LDAP domain to use for users (eg: ou=People,dc=example,dc=org)",
|
||||
},
|
||||
|
||||
"binddn": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "LDAP DN for searching for the user DN (optional)",
|
||||
},
|
||||
|
||||
"bindpass": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "LDAP password for searching for the user DN (optional)",
|
||||
},
|
||||
|
||||
"groupdn": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "LDAP search base to use for group membership search (eg: ou=Groups,dc=example,dc=org)",
|
||||
},
|
||||
|
||||
"groupfilter": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Default: "(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))",
|
||||
Description: `Go template for querying group membership of user (optional)
|
||||
The template can access the following context variables: UserDN, Username
|
||||
Example: (&(objectClass=group)(member:1.2.840.113556.1.4.1941:={{.UserDN}}))
|
||||
Default: (|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))`,
|
||||
},
|
||||
|
||||
"groupattr": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Default: "cn",
|
||||
Description: `LDAP attribute to follow on objects returned by <groupfilter>
|
||||
in order to enumerate user group membership.
|
||||
Examples: "cn" or "memberOf", etc.
|
||||
Default: cn`,
|
||||
},
|
||||
|
||||
"upndomain": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Enables userPrincipalDomain login with [username]@UPNDomain (optional)",
|
||||
},
|
||||
|
||||
"userattr": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Default: "cn",
|
||||
Description: "Attribute used for users (default: cn)",
|
||||
},
|
||||
|
||||
"certificate": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "CA certificate to use when verifying LDAP server certificate, must be x509 PEM encoded (optional)",
|
||||
},
|
||||
|
||||
"discoverdn": &framework.FieldSchema{
|
||||
Type: framework.TypeBool,
|
||||
Description: "Use anonymous bind to discover the bind DN of a user (optional)",
|
||||
},
|
||||
|
||||
"insecure_tls": &framework.FieldSchema{
|
||||
Type: framework.TypeBool,
|
||||
Description: "Skip LDAP server SSL Certificate verification - VERY insecure (optional)",
|
||||
},
|
||||
|
||||
"starttls": &framework.FieldSchema{
|
||||
Type: framework.TypeBool,
|
||||
Description: "Issue a StartTLS command after establishing unencrypted connection (optional)",
|
||||
},
|
||||
|
||||
"tls_min_version": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Default: "tls12",
|
||||
Description: "Minimum TLS version to use. Accepted values are 'tls10', 'tls11' or 'tls12'. Defaults to 'tls12'",
|
||||
},
|
||||
|
||||
"tls_max_version": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Default: "tls12",
|
||||
Description: "Maximum TLS version to use. Accepted values are 'tls10', 'tls11' or 'tls12'. Defaults to 'tls12'",
|
||||
},
|
||||
|
||||
"deny_null_bind": &framework.FieldSchema{
|
||||
Type: framework.TypeBool,
|
||||
Default: true,
|
||||
Description: "Denies an unauthenticated LDAP bind request if the user's password is empty; defaults to true",
|
||||
},
|
||||
|
||||
"case_sensitive_names": &framework.FieldSchema{
|
||||
Type: framework.TypeBool,
|
||||
Description: "If true, case sensitivity will be used when comparing usernames and groups for matching policies.",
|
||||
},
|
||||
},
|
||||
Fields: ldaputil.ConfigFields(),
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.ReadOperation: b.pathConfigRead,
|
||||
|
@ -140,7 +35,7 @@ func (b *backend) Config(ctx context.Context, req *logical.Request) (*ldaputil.C
|
|||
}
|
||||
|
||||
// Create a new ConfigEntry, filling in defaults where appropriate
|
||||
result, err := b.newConfigEntry(fd)
|
||||
result, err := ldaputil.NewConfigEntry(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -195,147 +90,14 @@ func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, d *f
|
|||
}
|
||||
|
||||
resp := &logical.Response{
|
||||
Data: map[string]interface{}{
|
||||
"url": cfg.Url,
|
||||
"userdn": cfg.UserDN,
|
||||
"groupdn": cfg.GroupDN,
|
||||
"groupfilter": cfg.GroupFilter,
|
||||
"groupattr": cfg.GroupAttr,
|
||||
"upndomain": cfg.UPNDomain,
|
||||
"userattr": cfg.UserAttr,
|
||||
"certificate": cfg.Certificate,
|
||||
"insecure_tls": cfg.InsecureTLS,
|
||||
"starttls": cfg.StartTLS,
|
||||
"binddn": cfg.BindDN,
|
||||
"deny_null_bind": cfg.DenyNullBind,
|
||||
"discoverdn": cfg.DiscoverDN,
|
||||
"tls_min_version": cfg.TLSMinVersion,
|
||||
"tls_max_version": cfg.TLSMaxVersion,
|
||||
"case_sensitive_names": *cfg.CaseSensitiveNames,
|
||||
},
|
||||
Data: cfg.PasswordlessMap(),
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates and initializes a ConfigEntry object with its default values,
|
||||
* as specified by the passed schema.
|
||||
*/
|
||||
func (b *backend) newConfigEntry(d *framework.FieldData) (*ldaputil.ConfigEntry, error) {
|
||||
cfg := new(ldaputil.ConfigEntry)
|
||||
|
||||
url := d.Get("url").(string)
|
||||
if url != "" {
|
||||
cfg.Url = strings.ToLower(url)
|
||||
}
|
||||
userattr := d.Get("userattr").(string)
|
||||
if userattr != "" {
|
||||
cfg.UserAttr = strings.ToLower(userattr)
|
||||
}
|
||||
userdn := d.Get("userdn").(string)
|
||||
if userdn != "" {
|
||||
cfg.UserDN = userdn
|
||||
}
|
||||
groupdn := d.Get("groupdn").(string)
|
||||
if groupdn != "" {
|
||||
cfg.GroupDN = groupdn
|
||||
}
|
||||
groupfilter := d.Get("groupfilter").(string)
|
||||
if groupfilter != "" {
|
||||
// Validate the template before proceeding
|
||||
_, err := template.New("queryTemplate").Parse(groupfilter)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("invalid groupfilter: {{err}}", err)
|
||||
}
|
||||
|
||||
cfg.GroupFilter = groupfilter
|
||||
}
|
||||
groupattr := d.Get("groupattr").(string)
|
||||
if groupattr != "" {
|
||||
cfg.GroupAttr = groupattr
|
||||
}
|
||||
upndomain := d.Get("upndomain").(string)
|
||||
if upndomain != "" {
|
||||
cfg.UPNDomain = upndomain
|
||||
}
|
||||
certificate := d.Get("certificate").(string)
|
||||
if certificate != "" {
|
||||
block, _ := pem.Decode([]byte(certificate))
|
||||
|
||||
if block == nil || block.Type != "CERTIFICATE" {
|
||||
return nil, fmt.Errorf("failed to decode PEM block in the certificate")
|
||||
}
|
||||
_, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("failed to parse certificate: {{err}}", err)
|
||||
}
|
||||
cfg.Certificate = certificate
|
||||
}
|
||||
insecureTLS := d.Get("insecure_tls").(bool)
|
||||
if insecureTLS {
|
||||
cfg.InsecureTLS = insecureTLS
|
||||
}
|
||||
cfg.TLSMinVersion = d.Get("tls_min_version").(string)
|
||||
if cfg.TLSMinVersion == "" {
|
||||
return nil, fmt.Errorf("failed to get 'tls_min_version' value")
|
||||
}
|
||||
|
||||
var ok bool
|
||||
_, ok = tlsutil.TLSLookup[cfg.TLSMinVersion]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid 'tls_min_version'")
|
||||
}
|
||||
|
||||
cfg.TLSMaxVersion = d.Get("tls_max_version").(string)
|
||||
if cfg.TLSMaxVersion == "" {
|
||||
return nil, fmt.Errorf("failed to get 'tls_max_version' value")
|
||||
}
|
||||
|
||||
_, ok = tlsutil.TLSLookup[cfg.TLSMaxVersion]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid 'tls_max_version'")
|
||||
}
|
||||
if cfg.TLSMaxVersion < cfg.TLSMinVersion {
|
||||
return nil, fmt.Errorf("'tls_max_version' must be greater than or equal to 'tls_min_version'")
|
||||
}
|
||||
|
||||
startTLS := d.Get("starttls").(bool)
|
||||
if startTLS {
|
||||
cfg.StartTLS = startTLS
|
||||
}
|
||||
|
||||
bindDN := d.Get("binddn").(string)
|
||||
if bindDN != "" {
|
||||
cfg.BindDN = bindDN
|
||||
}
|
||||
|
||||
bindPass := d.Get("bindpass").(string)
|
||||
if bindPass != "" {
|
||||
cfg.BindPassword = bindPass
|
||||
}
|
||||
|
||||
denyNullBind := d.Get("deny_null_bind").(bool)
|
||||
if denyNullBind {
|
||||
cfg.DenyNullBind = denyNullBind
|
||||
}
|
||||
|
||||
discoverDN := d.Get("discoverdn").(bool)
|
||||
if discoverDN {
|
||||
cfg.DiscoverDN = discoverDN
|
||||
}
|
||||
|
||||
caseSensitiveNames, ok := d.GetOk("case_sensitive_names")
|
||||
if ok {
|
||||
cfg.CaseSensitiveNames = new(bool)
|
||||
*cfg.CaseSensitiveNames = caseSensitiveNames.(bool)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
// Build a ConfigEntry struct out of the supplied FieldData
|
||||
cfg, err := b.newConfigEntry(d)
|
||||
cfg, err := ldaputil.NewConfigEntry(d)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(err.Error()), nil
|
||||
}
|
||||
|
|
|
@ -66,10 +66,12 @@ func (c *AuthCommand) Run(args []string) int {
|
|||
for _, arg := range args {
|
||||
switch {
|
||||
case strings.HasPrefix(arg, "-methods"):
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn(wrapAtLength(
|
||||
"WARNING! The -methods flag is deprecated. Please use "+
|
||||
"\"vault auth list\" instead. This flag will be removed in "+
|
||||
"Vault 0.11 (or later).") + "\n")
|
||||
}
|
||||
return (&AuthListCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: c.UI,
|
||||
|
@ -77,10 +79,12 @@ func (c *AuthCommand) Run(args []string) int {
|
|||
},
|
||||
}).Run(nil)
|
||||
case strings.HasPrefix(arg, "-method-help"):
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn(wrapAtLength(
|
||||
"WARNING! The -method-help flag is deprecated. Please use "+
|
||||
"\"vault auth help\" instead. This flag will be removed in "+
|
||||
"Vault 0.11 (or later).") + "\n")
|
||||
}
|
||||
// Parse the args to pull out the method, suppressing any errors because
|
||||
// there could be other flags that we don't care about.
|
||||
f := flag.NewFlagSet("", flag.ContinueOnError)
|
||||
|
@ -101,11 +105,13 @@ func (c *AuthCommand) Run(args []string) int {
|
|||
|
||||
// If we got this far, we have an arg or a series of args that should be
|
||||
// passed directly to the new "vault login" command.
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn(wrapAtLength(
|
||||
"WARNING! The \"vault auth ARG\" command is deprecated and is now a "+
|
||||
"subcommand for interacting with auth methods. To authenticate "+
|
||||
"locally to Vault, use \"vault login\" instead. This backwards "+
|
||||
"compatibility will be removed in Vault 0.11 (or later).") + "\n")
|
||||
}
|
||||
return (&LoginCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: c.UI,
|
||||
|
|
|
@ -284,7 +284,7 @@ func (c *BaseCommand) flagSet(bit FlagSetBit) *FlagSets {
|
|||
Usage: "Print only the field with the given name. Specifying " +
|
||||
"this option will take precedence over other formatting " +
|
||||
"directives. The result will not have a trailing newline " +
|
||||
"making it idea for piping to other processes.",
|
||||
"making it ideal for piping to other processes.",
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
ad "github.com/hashicorp/vault-plugin-secrets-ad/plugin"
|
||||
gcp "github.com/hashicorp/vault-plugin-secrets-gcp/plugin"
|
||||
kv "github.com/hashicorp/vault-plugin-secrets-kv"
|
||||
"github.com/hashicorp/vault/audit"
|
||||
|
@ -110,6 +111,7 @@ var (
|
|||
}
|
||||
|
||||
logicalBackends = map[string]logical.Factory{
|
||||
"ad": ad.Factory,
|
||||
"aws": aws.Factory,
|
||||
"cassandra": cassandra.Factory,
|
||||
"consul": consul.Factory,
|
||||
|
@ -211,359 +213,212 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
},
|
||||
}
|
||||
|
||||
Commands = map[string]cli.CommandFactory{
|
||||
"audit": func() (cli.Command, error) {
|
||||
return &AuditCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
getBaseCommand := func() *BaseCommand {
|
||||
return &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
client: runOpts.Client,
|
||||
}
|
||||
}
|
||||
|
||||
Commands = map[string]cli.CommandFactory{
|
||||
"audit": func() (cli.Command, error) {
|
||||
return &AuditCommand{
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"audit disable": func() (cli.Command, error) {
|
||||
return &AuditDisableCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"audit enable": func() (cli.Command, error) {
|
||||
return &AuditEnableCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"audit list": func() (cli.Command, error) {
|
||||
return &AuditListCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"auth tune": func() (cli.Command, error) {
|
||||
return &AuthTuneCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"auth": func() (cli.Command, error) {
|
||||
return &AuthCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
Handlers: loginHandlers,
|
||||
}, nil
|
||||
},
|
||||
"auth disable": func() (cli.Command, error) {
|
||||
return &AuthDisableCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"auth enable": func() (cli.Command, error) {
|
||||
return &AuthEnableCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"auth help": func() (cli.Command, error) {
|
||||
return &AuthHelpCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
Handlers: loginHandlers,
|
||||
}, nil
|
||||
},
|
||||
"auth list": func() (cli.Command, error) {
|
||||
return &AuthListCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"delete": func() (cli.Command, error) {
|
||||
return &DeleteCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"lease": func() (cli.Command, error) {
|
||||
return &LeaseCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"lease renew": func() (cli.Command, error) {
|
||||
return &LeaseRenewCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"lease revoke": func() (cli.Command, error) {
|
||||
return &LeaseRevokeCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"list": func() (cli.Command, error) {
|
||||
return &ListCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"login": func() (cli.Command, error) {
|
||||
return &LoginCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
Handlers: loginHandlers,
|
||||
}, nil
|
||||
},
|
||||
"operator": func() (cli.Command, error) {
|
||||
return &OperatorCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"operator generate-root": func() (cli.Command, error) {
|
||||
return &OperatorGenerateRootCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"operator init": func() (cli.Command, error) {
|
||||
return &OperatorInitCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"operator key-status": func() (cli.Command, error) {
|
||||
return &OperatorKeyStatusCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"operator rekey": func() (cli.Command, error) {
|
||||
return &OperatorRekeyCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"operator rotate": func() (cli.Command, error) {
|
||||
return &OperatorRotateCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"operator seal": func() (cli.Command, error) {
|
||||
return &OperatorSealCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"operator step-down": func() (cli.Command, error) {
|
||||
return &OperatorStepDownCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"operator unseal": func() (cli.Command, error) {
|
||||
return &OperatorUnsealCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"path-help": func() (cli.Command, error) {
|
||||
return &PathHelpCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"policy": func() (cli.Command, error) {
|
||||
return &PolicyCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"policy delete": func() (cli.Command, error) {
|
||||
return &PolicyDeleteCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"policy fmt": func() (cli.Command, error) {
|
||||
return &PolicyFmtCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"policy list": func() (cli.Command, error) {
|
||||
return &PolicyListCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"policy read": func() (cli.Command, error) {
|
||||
return &PolicyReadCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"policy write": func() (cli.Command, error) {
|
||||
return &PolicyWriteCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"read": func() (cli.Command, error) {
|
||||
return &ReadCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"secrets": func() (cli.Command, error) {
|
||||
return &SecretsCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"secrets disable": func() (cli.Command, error) {
|
||||
return &SecretsDisableCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"secrets enable": func() (cli.Command, error) {
|
||||
return &SecretsEnableCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"secrets list": func() (cli.Command, error) {
|
||||
return &SecretsListCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"secrets move": func() (cli.Command, error) {
|
||||
return &SecretsMoveCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"secrets tune": func() (cli.Command, error) {
|
||||
return &SecretsTuneCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"server": func() (cli.Command, error) {
|
||||
|
@ -583,193 +438,123 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
},
|
||||
"ssh": func() (cli.Command, error) {
|
||||
return &SSHCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"status": func() (cli.Command, error) {
|
||||
return &StatusCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"token": func() (cli.Command, error) {
|
||||
return &TokenCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"token create": func() (cli.Command, error) {
|
||||
return &TokenCreateCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"token capabilities": func() (cli.Command, error) {
|
||||
return &TokenCapabilitiesCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"token lookup": func() (cli.Command, error) {
|
||||
return &TokenLookupCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"token renew": func() (cli.Command, error) {
|
||||
return &TokenRenewCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"token revoke": func() (cli.Command, error) {
|
||||
return &TokenRevokeCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"unwrap": func() (cli.Command, error) {
|
||||
return &UnwrapCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"version": func() (cli.Command, error) {
|
||||
return &VersionCommand{
|
||||
VersionInfo: version.GetVersion(),
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"write": func() (cli.Command, error) {
|
||||
return &WriteCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"kv": func() (cli.Command, error) {
|
||||
return &KVCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"kv put": func() (cli.Command, error) {
|
||||
return &KVPutCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"kv patch": func() (cli.Command, error) {
|
||||
return &KVPatchCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"kv get": func() (cli.Command, error) {
|
||||
return &KVGetCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"kv delete": func() (cli.Command, error) {
|
||||
return &KVDeleteCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"kv list": func() (cli.Command, error) {
|
||||
return &KVListCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"kv destroy": func() (cli.Command, error) {
|
||||
return &KVDestroyCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"kv undelete": func() (cli.Command, error) {
|
||||
return &KVUndeleteCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"kv enable-versioning": func() (cli.Command, error) {
|
||||
return &KVEnableVersioningCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"kv metadata": func() (cli.Command, error) {
|
||||
return &KVMetadataCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"kv metadata put": func() (cli.Command, error) {
|
||||
return &KVMetadataPutCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"kv metadata get": func() (cli.Command, error) {
|
||||
return &KVMetadataGetCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
"kv metadata delete": func() (cli.Command, error) {
|
||||
return &KVMetadataDeleteCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
@ -782,12 +567,9 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
return &DeprecatedCommand{
|
||||
Old: "audit-disable",
|
||||
New: "audit disable",
|
||||
Command: &AuditDisableCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
Command: &AuditDisableCommand{
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -798,11 +580,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "audit enable",
|
||||
UI: ui,
|
||||
Command: &AuditEnableCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -813,11 +591,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "audit list",
|
||||
UI: ui,
|
||||
Command: &AuditListCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -828,11 +602,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "auth disable",
|
||||
UI: ui,
|
||||
Command: &AuthDisableCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -843,11 +613,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "auth enable",
|
||||
UI: ui,
|
||||
Command: &AuthEnableCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -858,11 +624,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "token capabilities",
|
||||
UI: ui,
|
||||
Command: &TokenCapabilitiesCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -873,11 +635,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "operator generate-root",
|
||||
UI: ui,
|
||||
Command: &OperatorGenerateRootCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -888,11 +646,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "operator init",
|
||||
UI: ui,
|
||||
Command: &OperatorInitCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -903,11 +657,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "operator key-status",
|
||||
UI: ui,
|
||||
Command: &OperatorKeyStatusCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -918,11 +668,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "lease renew",
|
||||
UI: ui,
|
||||
Command: &LeaseRenewCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -933,11 +679,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "lease revoke",
|
||||
UI: ui,
|
||||
Command: &LeaseRevokeCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -948,11 +690,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "secrets enable",
|
||||
UI: ui,
|
||||
Command: &SecretsEnableCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -963,11 +701,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "secrets tune",
|
||||
UI: ui,
|
||||
Command: &SecretsTuneCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -978,11 +712,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "secrets list",
|
||||
UI: ui,
|
||||
Command: &SecretsListCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -993,11 +723,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "policy read\" or \"vault policy list", // lol
|
||||
UI: ui,
|
||||
Command: &PoliciesDeprecatedCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -1008,11 +734,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "policy delete",
|
||||
UI: ui,
|
||||
Command: &PolicyDeleteCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -1023,11 +745,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "policy write",
|
||||
UI: ui,
|
||||
Command: &PolicyWriteCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -1038,11 +756,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "operator rekey",
|
||||
UI: ui,
|
||||
Command: &OperatorRekeyCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -1053,11 +767,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "secrets move",
|
||||
UI: ui,
|
||||
Command: &SecretsMoveCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -1068,11 +778,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "operator rotate",
|
||||
UI: ui,
|
||||
Command: &OperatorRotateCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -1083,11 +789,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "operator seal",
|
||||
UI: ui,
|
||||
Command: &OperatorSealCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -1098,11 +800,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "operator step-down",
|
||||
UI: ui,
|
||||
Command: &OperatorStepDownCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -1113,11 +811,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "token create",
|
||||
UI: ui,
|
||||
Command: &TokenCreateCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -1128,11 +822,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "token lookup",
|
||||
UI: ui,
|
||||
Command: &TokenLookupCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -1143,11 +833,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "token renew",
|
||||
UI: ui,
|
||||
Command: &TokenRenewCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -1158,11 +844,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "token revoke",
|
||||
UI: ui,
|
||||
Command: &TokenRevokeCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -1173,11 +855,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "secrets disable",
|
||||
UI: ui,
|
||||
Command: &SecretsDisableCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
@ -1188,11 +866,7 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
|||
New: "operator unseal",
|
||||
UI: ui,
|
||||
Command: &OperatorUnsealCommand{
|
||||
BaseCommand: &BaseCommand{
|
||||
UI: ui,
|
||||
tokenHelper: runOpts.TokenHelper,
|
||||
flagAddress: runOpts.Address,
|
||||
},
|
||||
BaseCommand: getBaseCommand(),
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
|
|
|
@ -69,20 +69,18 @@ var Formatters = map[string]Formatter{
|
|||
"yml": YamlFormatter{},
|
||||
}
|
||||
|
||||
func format() string {
|
||||
format := os.Getenv(EnvVaultFormat)
|
||||
if format == "" {
|
||||
format = "table"
|
||||
}
|
||||
return format
|
||||
}
|
||||
|
||||
func Format(ui cli.Ui) string {
|
||||
switch ui.(type) {
|
||||
case *VaultUI:
|
||||
return ui.(*VaultUI).format
|
||||
}
|
||||
return format()
|
||||
|
||||
format := os.Getenv(EnvVaultFormat)
|
||||
if format == "" {
|
||||
format = "table"
|
||||
}
|
||||
|
||||
return format
|
||||
}
|
||||
|
||||
// An output formatter for json output of an object
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -91,13 +92,13 @@ func Test_Format_Parsing(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
"format",
|
||||
[]string{"-format", "json"},
|
||||
[]string{"token", "renew", "-format", "json"},
|
||||
"{",
|
||||
0,
|
||||
},
|
||||
{
|
||||
"format_bad",
|
||||
[]string{"-format", "nope-not-real"},
|
||||
[]string{"token", "renew", "-format", "nope-not-real"},
|
||||
"Invalid output format",
|
||||
1,
|
||||
},
|
||||
|
@ -110,21 +111,24 @@ func Test_Format_Parsing(t *testing.T) {
|
|||
client, closer := testVaultServer(t)
|
||||
defer closer()
|
||||
|
||||
stdout := bytes.NewBuffer(nil)
|
||||
stderr := bytes.NewBuffer(nil)
|
||||
runOpts := &RunOptions{
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
Client: client,
|
||||
}
|
||||
|
||||
// Login with the token so we can renew-self.
|
||||
token, _ := testTokenAndAccessor(t, client)
|
||||
client.SetToken(token)
|
||||
|
||||
ui, cmd := testTokenRenewCommand(t)
|
||||
cmd.client = client
|
||||
|
||||
tc.args = setupEnv(tc.args)
|
||||
|
||||
code := cmd.Run(tc.args)
|
||||
code := RunCustom(tc.args, runOpts)
|
||||
if code != tc.code {
|
||||
t.Errorf("expected %d to be %d", code, tc.code)
|
||||
}
|
||||
|
||||
combined := ui.OutputWriter.String() + ui.ErrorWriter.String()
|
||||
combined := stdout.String() + stderr.String()
|
||||
if !strings.Contains(combined, tc.out) {
|
||||
t.Errorf("expected %q to contain %q", combined, tc.out)
|
||||
}
|
||||
|
|
|
@ -94,11 +94,12 @@ func (c *LeaseRenewCommand) Run(args []string) int {
|
|||
case 2:
|
||||
// Deprecation
|
||||
// TODO: remove in 0.9.0
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn(wrapAtLength(
|
||||
"WARNING! Specifying INCREMENT as a second argument is deprecated. " +
|
||||
"Please use -increment instead. This will be removed in the next " +
|
||||
"major release of Vault."))
|
||||
|
||||
}
|
||||
leaseID = strings.TrimSpace(args[0])
|
||||
parsed, err := time.ParseDuration(appendDurationSuffix(args[1]))
|
||||
if err != nil {
|
||||
|
|
|
@ -167,6 +167,7 @@ func (c *LoginCommand) Run(args []string) int {
|
|||
// TODO: remove in 0.10.0
|
||||
switch {
|
||||
case c.flagNoVerify:
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn(wrapAtLength(
|
||||
"WARNING! The -no-verify flag is deprecated. In the past, Vault " +
|
||||
"performed a lookup on a token after authentication. This is no " +
|
||||
|
@ -187,6 +188,7 @@ func (c *LoginCommand) Run(args []string) int {
|
|||
"If you are not using token authentication, you can safely omit this " +
|
||||
"flag. Vault will not perform a lookup after authentication."))
|
||||
c.UI.Warn("")
|
||||
}
|
||||
|
||||
// There's no point in passing this to other auth handlers...
|
||||
if c.flagMethod == "token" {
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"text/tabwriter"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/hashicorp/vault/api"
|
||||
"github.com/hashicorp/vault/command/token"
|
||||
colorable "github.com/mattn/go-colorable"
|
||||
"github.com/mitchellh/cli"
|
||||
|
@ -22,8 +23,7 @@ type VaultUI struct {
|
|||
|
||||
// setupEnv parses args and may replace them and sets some env vars to known
|
||||
// values based on format options
|
||||
func setupEnv(args []string) []string {
|
||||
var format string
|
||||
func setupEnv(args []string) (retArgs []string, format string) {
|
||||
var nextArgFormat bool
|
||||
|
||||
for _, arg := range args {
|
||||
|
@ -65,10 +65,8 @@ func setupEnv(args []string) []string {
|
|||
if format == "" {
|
||||
format = "table"
|
||||
}
|
||||
// Put back into the env for later
|
||||
os.Setenv(EnvVaultFormat, format)
|
||||
|
||||
return args
|
||||
return args, format
|
||||
}
|
||||
|
||||
type RunOptions struct {
|
||||
|
@ -76,6 +74,7 @@ type RunOptions struct {
|
|||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
Address string
|
||||
Client *api.Client
|
||||
}
|
||||
|
||||
func Run(args []string) int {
|
||||
|
@ -89,7 +88,8 @@ func RunCustom(args []string, runOpts *RunOptions) int {
|
|||
runOpts = &RunOptions{}
|
||||
}
|
||||
|
||||
args = setupEnv(args)
|
||||
var format string
|
||||
args, format = setupEnv(args)
|
||||
|
||||
// Don't use color if disabled
|
||||
useColor := true
|
||||
|
@ -104,8 +104,6 @@ func RunCustom(args []string, runOpts *RunOptions) int {
|
|||
runOpts.Stderr = os.Stderr
|
||||
}
|
||||
|
||||
format := format()
|
||||
|
||||
// Only use colored UI if stdout is a tty, and not disabled
|
||||
if useColor && format == "table" {
|
||||
if f, ok := runOpts.Stdout.(*os.File); ok {
|
||||
|
|
|
@ -217,9 +217,11 @@ func (c *OperatorGenerateRootCommand) Run(args []string) int {
|
|||
// TODO: remove in 0.9.0
|
||||
switch {
|
||||
case c.flagGenOTP:
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn(wrapAtLength(
|
||||
"The -gen-otp flag is deprecated. Please use the -generate-otp flag " +
|
||||
"WARNING! The -gen-otp flag is deprecated. Please use the -generate-otp flag " +
|
||||
"instead."))
|
||||
}
|
||||
c.flagGenerateOTP = c.flagGenOTP
|
||||
}
|
||||
|
||||
|
|
|
@ -244,14 +244,18 @@ func (c *OperatorInitCommand) Run(args []string) int {
|
|||
// Deprecations
|
||||
// TODO: remove in 0.9.0
|
||||
if c.flagAuto {
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn(wrapAtLength("WARNING! -auto is deprecated. Please use " +
|
||||
"-consul-auto instead. This will be removed in Vault 0.11 " +
|
||||
"(or later)."))
|
||||
}
|
||||
c.flagConsulAuto = true
|
||||
}
|
||||
if c.flagCheck {
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn(wrapAtLength("WARNING! -check is deprecated. Please use " +
|
||||
"-status instead. This will be removed in Vault 0.11 (or later)."))
|
||||
}
|
||||
c.flagStatus = true
|
||||
}
|
||||
|
||||
|
|
|
@ -269,21 +269,27 @@ func (c *OperatorRekeyCommand) Run(args []string) int {
|
|||
// Deprecations
|
||||
// TODO: remove in 0.9.0
|
||||
if c.flagDelete {
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn(wrapAtLength(
|
||||
"WARNING! The -delete flag is deprecated. Please use -backup-delete " +
|
||||
"instead. This flag will be removed in Vault 0.11 (or later)."))
|
||||
}
|
||||
c.flagBackupDelete = true
|
||||
}
|
||||
if c.flagRetrieve {
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn(wrapAtLength(
|
||||
"WARNING! The -retrieve flag is deprecated. Please use -backup-retrieve " +
|
||||
"instead. This flag will be removed in Vault 0.11 (or later)."))
|
||||
}
|
||||
c.flagBackupRetrieve = true
|
||||
}
|
||||
if c.flagRecoveryKey {
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn(wrapAtLength(
|
||||
"WARNING! The -recovery-key flag is deprecated. Please use -target=recovery " +
|
||||
"instead. This flag will be removed in Vault 0.11 (or later)."))
|
||||
}
|
||||
c.flagTarget = "recovery"
|
||||
}
|
||||
|
||||
|
@ -344,6 +350,7 @@ func (c *OperatorRekeyCommand) init(client *api.Client) int {
|
|||
|
||||
// Print warnings about recovery, etc.
|
||||
if len(c.flagPGPKeys) == 0 {
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn(wrapAtLength(
|
||||
"WARNING! If you lose the keys after they are returned, there is no " +
|
||||
"recovery. Consider canceling this operation and re-initializing " +
|
||||
|
@ -353,7 +360,9 @@ func (c *OperatorRekeyCommand) init(client *api.Client) int {
|
|||
"flag."))
|
||||
c.UI.Output("")
|
||||
}
|
||||
}
|
||||
if len(c.flagPGPKeys) > 0 && !c.flagBackup {
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn(wrapAtLength(
|
||||
"WARNING! You are using PGP keys for encrypted the resulting unseal " +
|
||||
"keys, but you did not enable the option to backup the keys to " +
|
||||
|
@ -364,6 +373,7 @@ func (c *OperatorRekeyCommand) init(client *api.Client) int {
|
|||
"stored keys later using the -delete flag."))
|
||||
c.UI.Output("")
|
||||
}
|
||||
}
|
||||
|
||||
// Provide the current status
|
||||
return c.printStatus(status)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
@ -147,7 +148,6 @@ func TestOperatorUnsealCommand_Run(t *testing.T) {
|
|||
|
||||
func TestOperatorUnsealCommand_Format(t *testing.T) {
|
||||
defer func() {
|
||||
os.Setenv(EnvVaultFormat, "")
|
||||
os.Setenv(EnvVaultCLINoColor, "")
|
||||
}()
|
||||
|
||||
|
@ -159,21 +159,28 @@ func TestOperatorUnsealCommand_Format(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ui, cmd := testOperatorUnsealCommand(t)
|
||||
cmd.client = client
|
||||
cmd.testOutput = ioutil.Discard
|
||||
|
||||
args := setupEnv([]string{"-format", "json"})
|
||||
|
||||
// Unseal with one key
|
||||
code := cmd.Run(append(args, []string{
|
||||
keys[0],
|
||||
}...))
|
||||
if exp := 0; code != exp {
|
||||
t.Errorf("expected %d to be %d: %s", code, exp, ui.ErrorWriter.String())
|
||||
stdout := bytes.NewBuffer(nil)
|
||||
stderr := bytes.NewBuffer(nil)
|
||||
runOpts := &RunOptions{
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
Client: client,
|
||||
}
|
||||
|
||||
if !json.Valid(ui.OutputWriter.Bytes()) {
|
||||
args, format := setupEnv([]string{"unseal", "-format", "json"})
|
||||
if format != "json" {
|
||||
t.Fatalf("expected %q, got %q", "json", format)
|
||||
}
|
||||
|
||||
// Unseal with one key
|
||||
code := RunCustom(append(args, []string{
|
||||
keys[0],
|
||||
}...), runOpts)
|
||||
if exp := 0; code != exp {
|
||||
t.Errorf("expected %d to be %d: %s", code, exp, stderr.String())
|
||||
}
|
||||
|
||||
if !json.Valid(stdout.Bytes()) {
|
||||
t.Error("expected output to be valid JSON")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -207,9 +207,11 @@ func (c *TokenCreateCommand) Run(args []string) int {
|
|||
|
||||
// TODO: remove in 0.9.0
|
||||
if c.flagLease != 0 {
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn("The -lease flag is deprecated. Please use -ttl instead.")
|
||||
c.flagTTL = c.flagLease
|
||||
}
|
||||
}
|
||||
|
||||
client, err := c.Client()
|
||||
if err != nil {
|
||||
|
|
|
@ -99,9 +99,10 @@ func (c *TokenRenewCommand) Run(args []string) int {
|
|||
token = strings.TrimSpace(args[0])
|
||||
case 2:
|
||||
// TODO: remove in 0.9.0 - backwards compat
|
||||
if Format(c.UI) == "table" {
|
||||
c.UI.Warn("Specifying increment as a second argument is deprecated. " +
|
||||
"Please use -increment instead.")
|
||||
|
||||
}
|
||||
token = strings.TrimSpace(args[0])
|
||||
parsed, err := time.ParseDuration(appendDurationSuffix(args[1]))
|
||||
if err != nil {
|
||||
|
|
|
@ -5,10 +5,235 @@ import (
|
|||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/hashicorp/vault/helper/tlsutil"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
)
|
||||
|
||||
// ConfigFields returns all the config fields that can potentially be used by the LDAP client.
|
||||
// Not all fields will be used by every integration.
|
||||
func ConfigFields() map[string]*framework.FieldSchema {
|
||||
return map[string]*framework.FieldSchema{
|
||||
"url": {
|
||||
Type: framework.TypeString,
|
||||
Default: "ldap://127.0.0.1",
|
||||
Description: "LDAP URL to connect to (default: ldap://127.0.0.1). Multiple URLs can be specified by concatenating them with commas; they will be tried in-order.",
|
||||
},
|
||||
|
||||
"userdn": {
|
||||
Type: framework.TypeString,
|
||||
Description: "LDAP domain to use for users (eg: ou=People,dc=example,dc=org)",
|
||||
},
|
||||
|
||||
"binddn": {
|
||||
Type: framework.TypeString,
|
||||
Description: "LDAP DN for searching for the user DN (optional)",
|
||||
},
|
||||
|
||||
"bindpass": {
|
||||
Type: framework.TypeString,
|
||||
Description: "LDAP password for searching for the user DN (optional)",
|
||||
},
|
||||
|
||||
"groupdn": {
|
||||
Type: framework.TypeString,
|
||||
Description: "LDAP search base to use for group membership search (eg: ou=Groups,dc=example,dc=org)",
|
||||
},
|
||||
|
||||
"groupfilter": {
|
||||
Type: framework.TypeString,
|
||||
Default: "(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))",
|
||||
Description: `Go template for querying group membership of user (optional)
|
||||
The template can access the following context variables: UserDN, Username
|
||||
Example: (&(objectClass=group)(member:1.2.840.113556.1.4.1941:={{.UserDN}}))
|
||||
Default: (|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))`,
|
||||
},
|
||||
|
||||
"groupattr": {
|
||||
Type: framework.TypeString,
|
||||
Default: "cn",
|
||||
Description: `LDAP attribute to follow on objects returned by <groupfilter>
|
||||
in order to enumerate user group membership.
|
||||
Examples: "cn" or "memberOf", etc.
|
||||
Default: cn`,
|
||||
},
|
||||
|
||||
"upndomain": {
|
||||
Type: framework.TypeString,
|
||||
Description: "Enables userPrincipalDomain login with [username]@UPNDomain (optional)",
|
||||
},
|
||||
|
||||
"userattr": {
|
||||
Type: framework.TypeString,
|
||||
Default: "cn",
|
||||
Description: "Attribute used for users (default: cn)",
|
||||
},
|
||||
|
||||
"certificate": {
|
||||
Type: framework.TypeString,
|
||||
Description: "CA certificate to use when verifying LDAP server certificate, must be x509 PEM encoded (optional)",
|
||||
},
|
||||
|
||||
"discoverdn": {
|
||||
Type: framework.TypeBool,
|
||||
Description: "Use anonymous bind to discover the bind DN of a user (optional)",
|
||||
},
|
||||
|
||||
"insecure_tls": {
|
||||
Type: framework.TypeBool,
|
||||
Description: "Skip LDAP server SSL Certificate verification - VERY insecure (optional)",
|
||||
},
|
||||
|
||||
"starttls": {
|
||||
Type: framework.TypeBool,
|
||||
Description: "Issue a StartTLS command after establishing unencrypted connection (optional)",
|
||||
},
|
||||
|
||||
"tls_min_version": {
|
||||
Type: framework.TypeString,
|
||||
Default: "tls12",
|
||||
Description: "Minimum TLS version to use. Accepted values are 'tls10', 'tls11' or 'tls12'. Defaults to 'tls12'",
|
||||
},
|
||||
|
||||
"tls_max_version": {
|
||||
Type: framework.TypeString,
|
||||
Default: "tls12",
|
||||
Description: "Maximum TLS version to use. Accepted values are 'tls10', 'tls11' or 'tls12'. Defaults to 'tls12'",
|
||||
},
|
||||
|
||||
"deny_null_bind": {
|
||||
Type: framework.TypeBool,
|
||||
Default: true,
|
||||
Description: "Denies an unauthenticated LDAP bind request if the user's password is empty; defaults to true",
|
||||
},
|
||||
|
||||
"case_sensitive_names": {
|
||||
Type: framework.TypeBool,
|
||||
Description: "If true, case sensitivity will be used when comparing usernames and groups for matching policies.",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates and initializes a ConfigEntry object with its default values,
|
||||
* as specified by the passed schema.
|
||||
*/
|
||||
func NewConfigEntry(d *framework.FieldData) (*ConfigEntry, error) {
|
||||
cfg := new(ConfigEntry)
|
||||
|
||||
url := d.Get("url").(string)
|
||||
if url != "" {
|
||||
cfg.Url = strings.ToLower(url)
|
||||
}
|
||||
userattr := d.Get("userattr").(string)
|
||||
if userattr != "" {
|
||||
cfg.UserAttr = strings.ToLower(userattr)
|
||||
}
|
||||
userdn := d.Get("userdn").(string)
|
||||
if userdn != "" {
|
||||
cfg.UserDN = userdn
|
||||
}
|
||||
groupdn := d.Get("groupdn").(string)
|
||||
if groupdn != "" {
|
||||
cfg.GroupDN = groupdn
|
||||
}
|
||||
groupfilter := d.Get("groupfilter").(string)
|
||||
if groupfilter != "" {
|
||||
// Validate the template before proceeding
|
||||
_, err := template.New("queryTemplate").Parse(groupfilter)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("invalid groupfilter: {{err}}", err)
|
||||
}
|
||||
|
||||
cfg.GroupFilter = groupfilter
|
||||
}
|
||||
groupattr := d.Get("groupattr").(string)
|
||||
if groupattr != "" {
|
||||
cfg.GroupAttr = groupattr
|
||||
}
|
||||
upndomain := d.Get("upndomain").(string)
|
||||
if upndomain != "" {
|
||||
cfg.UPNDomain = upndomain
|
||||
}
|
||||
certificate := d.Get("certificate").(string)
|
||||
if certificate != "" {
|
||||
block, _ := pem.Decode([]byte(certificate))
|
||||
|
||||
if block == nil || block.Type != "CERTIFICATE" {
|
||||
return nil, fmt.Errorf("failed to decode PEM block in the certificate")
|
||||
}
|
||||
_, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, errwrap.Wrapf("failed to parse certificate: {{err}}", err)
|
||||
}
|
||||
cfg.Certificate = certificate
|
||||
}
|
||||
insecureTLS := d.Get("insecure_tls").(bool)
|
||||
if insecureTLS {
|
||||
cfg.InsecureTLS = insecureTLS
|
||||
}
|
||||
cfg.TLSMinVersion = d.Get("tls_min_version").(string)
|
||||
if cfg.TLSMinVersion == "" {
|
||||
return nil, fmt.Errorf("failed to get 'tls_min_version' value")
|
||||
}
|
||||
|
||||
var ok bool
|
||||
_, ok = tlsutil.TLSLookup[cfg.TLSMinVersion]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid 'tls_min_version'")
|
||||
}
|
||||
|
||||
cfg.TLSMaxVersion = d.Get("tls_max_version").(string)
|
||||
if cfg.TLSMaxVersion == "" {
|
||||
return nil, fmt.Errorf("failed to get 'tls_max_version' value")
|
||||
}
|
||||
|
||||
_, ok = tlsutil.TLSLookup[cfg.TLSMaxVersion]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid 'tls_max_version'")
|
||||
}
|
||||
if cfg.TLSMaxVersion < cfg.TLSMinVersion {
|
||||
return nil, fmt.Errorf("'tls_max_version' must be greater than or equal to 'tls_min_version'")
|
||||
}
|
||||
|
||||
startTLS := d.Get("starttls").(bool)
|
||||
if startTLS {
|
||||
cfg.StartTLS = startTLS
|
||||
}
|
||||
|
||||
bindDN := d.Get("binddn").(string)
|
||||
if bindDN != "" {
|
||||
cfg.BindDN = bindDN
|
||||
}
|
||||
|
||||
bindPass := d.Get("bindpass").(string)
|
||||
if bindPass != "" {
|
||||
cfg.BindPassword = bindPass
|
||||
}
|
||||
|
||||
denyNullBind := d.Get("deny_null_bind").(bool)
|
||||
if denyNullBind {
|
||||
cfg.DenyNullBind = denyNullBind
|
||||
}
|
||||
|
||||
discoverDN := d.Get("discoverdn").(bool)
|
||||
if discoverDN {
|
||||
cfg.DiscoverDN = discoverDN
|
||||
}
|
||||
|
||||
caseSensitiveNames, ok := d.GetOk("case_sensitive_names")
|
||||
if ok {
|
||||
cfg.CaseSensitiveNames = new(bool)
|
||||
*cfg.CaseSensitiveNames = caseSensitiveNames.(bool)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
type ConfigEntry struct {
|
||||
Url string `json:"url"`
|
||||
UserDN string `json:"userdn"`
|
||||
|
@ -34,6 +259,36 @@ type ConfigEntry struct {
|
|||
CaseSensitiveNames *bool `json:"CaseSensitiveNames,omitempty"`
|
||||
}
|
||||
|
||||
func (c *ConfigEntry) Map() map[string]interface{} {
|
||||
m := c.PasswordlessMap()
|
||||
m["bindpass"] = c.BindPassword
|
||||
return m
|
||||
}
|
||||
|
||||
func (c *ConfigEntry) PasswordlessMap() map[string]interface{} {
|
||||
m := map[string]interface{}{
|
||||
"url": c.Url,
|
||||
"userdn": c.UserDN,
|
||||
"groupdn": c.GroupDN,
|
||||
"groupfilter": c.GroupFilter,
|
||||
"groupattr": c.GroupAttr,
|
||||
"upndomain": c.UPNDomain,
|
||||
"userattr": c.UserAttr,
|
||||
"certificate": c.Certificate,
|
||||
"insecure_tls": c.InsecureTLS,
|
||||
"starttls": c.StartTLS,
|
||||
"binddn": c.BindDN,
|
||||
"deny_null_bind": c.DenyNullBind,
|
||||
"discoverdn": c.DiscoverDN,
|
||||
"tls_min_version": c.TLSMinVersion,
|
||||
"tls_max_version": c.TLSMaxVersion,
|
||||
}
|
||||
if c.CaseSensitiveNames != nil {
|
||||
m["case_sensitive_names"] = *c.CaseSensitiveNames
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (c *ConfigEntry) Validate() error {
|
||||
if len(c.Url) == 0 {
|
||||
return errors.New("at least one url must be provided")
|
||||
|
|
|
@ -138,7 +138,7 @@ func newEtcdV2Client(conf map[string]string) (client.Client, error) {
|
|||
if (hasCert && hasKey) || hasCa {
|
||||
var transportErr error
|
||||
tls := transport.TLSInfo{
|
||||
CAFile: ca,
|
||||
TrustedCAFile: ca,
|
||||
CertFile: cert,
|
||||
KeyFile: key,
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ func newEtcd3Backend(conf map[string]string, logger log.Logger) (physical.Backen
|
|||
ca, hasCa := conf["tls_ca_file"]
|
||||
if (hasCert && hasKey) || hasCa {
|
||||
tls := transport.TLSInfo{
|
||||
CAFile: ca,
|
||||
TrustedCAFile: ca,
|
||||
CertFile: cert,
|
||||
KeyFile: key,
|
||||
}
|
||||
|
|
|
@ -32,8 +32,12 @@ govendor init
|
|||
echo "Fetching deps, will take some time..."
|
||||
govendor fetch +missing
|
||||
|
||||
# Clean up after the logrus mess
|
||||
govendor remove github.com/Sirupsen/logrus
|
||||
cd vendor
|
||||
find -type f | grep '.go' | xargs sed -i -e 's/Sirupsen/sirupsen/'
|
||||
|
||||
# Need the v2 branch for Azure
|
||||
govendor fetch github.com/coreos/go-oidc@v2
|
||||
|
||||
echo "Done; to commit run \n\ncd ${GOPATH}/src/github.com/hashicorp/${TOOL}\n"
|
||||
|
|
|
@ -16,6 +16,7 @@ export default Ember.Component.extend({
|
|||
class={{buttonClasses}}
|
||||
type="button"
|
||||
disabled={{disabled}}
|
||||
data-test-confirm-action-trigger=true
|
||||
{{action 'toggleConfirm'}}
|
||||
>
|
||||
{{yield}}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
import Ember from 'ember';
|
||||
const { assert, inject, Component } = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
flashMessages: inject.service(),
|
||||
params: null,
|
||||
successMessage() {
|
||||
return 'Save was successful';
|
||||
},
|
||||
errorMessage() {
|
||||
return 'There was an error saving';
|
||||
},
|
||||
onError(model) {
|
||||
if (model && model.rollbackAttributes) {
|
||||
model.rollbackAttributes();
|
||||
}
|
||||
},
|
||||
onSuccess(){},
|
||||
// override and return a promise
|
||||
transaction() {
|
||||
assert('override transaction call in an extension of popup-base', false);
|
||||
},
|
||||
|
||||
actions: {
|
||||
performTransaction() {
|
||||
let args = [...arguments];
|
||||
let messageArgs = this.messageArgs(...args);
|
||||
return this.transaction(...args)
|
||||
.then(() => {
|
||||
this.get('onSuccess')();
|
||||
this.get('flashMessages').success(this.successMessage(...messageArgs));
|
||||
})
|
||||
.catch(e => {
|
||||
this.onError(...messageArgs);
|
||||
this.get('flashMessages').success(this.errorMessage(e, ...messageArgs));
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
|
@ -2,20 +2,23 @@ import Ember from 'ember';
|
|||
import { task } from 'ember-concurrency';
|
||||
import { humanize } from 'vault/helpers/humanize';
|
||||
|
||||
const { computed } = Ember;
|
||||
const { computed, inject } = Ember;
|
||||
export default Ember.Component.extend({
|
||||
flashMessages: inject.service(),
|
||||
'data-test-component': 'identity-edit-form',
|
||||
model: null,
|
||||
|
||||
// 'create', 'edit', 'merge'
|
||||
mode: 'create',
|
||||
/*
|
||||
* @param Function
|
||||
* @public
|
||||
*
|
||||
* Optional param to call a function upon successfully mounting a backend
|
||||
*
|
||||
* Optional param to call a function upon successfully saving an entity
|
||||
*/
|
||||
onSave: () => {},
|
||||
|
||||
cancelLink: computed('mode', 'model', function() {
|
||||
cancelLink: computed('mode', 'model.identityType', function() {
|
||||
let { model, mode } = this.getProperties('model', 'mode');
|
||||
let key = `${mode}-${model.get('identityType')}`;
|
||||
let routes = {
|
||||
|
@ -33,16 +36,17 @@ export default Ember.Component.extend({
|
|||
return routes[key];
|
||||
}),
|
||||
|
||||
getMessage(model) {
|
||||
getMessage(model, isDelete = false) {
|
||||
let mode = this.get('mode');
|
||||
let typeDisplay = humanize([model.get('identityType')]);
|
||||
let action = isDelete ? 'deleted' : 'saved';
|
||||
if (mode === 'merge') {
|
||||
return 'Successfully merged entities';
|
||||
}
|
||||
if (model.get('id')) {
|
||||
return `Successfully saved ${typeDisplay} ${model.id}.`;
|
||||
return `Successfully ${action} ${typeDisplay} ${model.id}.`;
|
||||
}
|
||||
return `Successfully saved ${typeDisplay}.`;
|
||||
return `Successfully ${action} ${typeDisplay}.`;
|
||||
},
|
||||
|
||||
save: task(function*() {
|
||||
|
@ -56,13 +60,26 @@ export default Ember.Component.extend({
|
|||
return;
|
||||
}
|
||||
this.get('flashMessages').success(message);
|
||||
yield this.get('onSave')(model);
|
||||
yield this.get('onSave')({saveType: 'save', model});
|
||||
}).drop(),
|
||||
|
||||
willDestroy() {
|
||||
let model = this.get('model');
|
||||
if (!model.isDestroyed || !model.isDestroying) {
|
||||
if ((model.get('isDirty') && !model.isDestroyed) || !model.isDestroying) {
|
||||
model.rollbackAttributes();
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
deleteItem(model) {
|
||||
let message = this.getMessage(model, true);
|
||||
let flash = this.get('flashMessages');
|
||||
model
|
||||
.destroyRecord()
|
||||
.then(() => {
|
||||
flash.success(message);
|
||||
return this.get('onSave')({saveType: 'delete', model});
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const { inject } = Ember;
|
||||
|
||||
export default Ember.Component.extend({
|
||||
flashMessages: inject.service(),
|
||||
|
||||
actions: {
|
||||
enable(model) {
|
||||
model.set('disabled', false);
|
||||
|
||||
model.save().
|
||||
then(() => {
|
||||
this.get('flashMessages').success(`Successfully enabled entity: ${model.id}`);
|
||||
})
|
||||
.catch(e => {
|
||||
this.get('flashMessages').success(
|
||||
`There was a problem enabling the entity: ${model.id} - ${e.error.join(' ') || e.message}`
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
import Base from './_popup-base';
|
||||
|
||||
export default Base.extend({
|
||||
messageArgs(model) {
|
||||
let type = model.get('identityType');
|
||||
let id = model.id;
|
||||
return [type, id];
|
||||
},
|
||||
|
||||
successMessage(type, id) {
|
||||
return `Successfully deleted ${type}: ${id}`;
|
||||
},
|
||||
|
||||
errorMessage(e, type, id) {
|
||||
let error = e.errors ? e.errors.join(' ') : e.message;
|
||||
return `There was a problem deleting ${type}: ${id} - ${error}`;
|
||||
},
|
||||
|
||||
transaction(model) {
|
||||
return model.destroyRecord();
|
||||
},
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
import Base from './_popup-base';
|
||||
import Ember from 'ember';
|
||||
const { computed } = Ember;
|
||||
|
||||
export default Base.extend({
|
||||
model: computed.alias('params.firstObject'),
|
||||
|
||||
groupArray: computed('params', function() {
|
||||
return this.get('params').objectAt(1);
|
||||
}),
|
||||
|
||||
memberId: computed('params', function() {
|
||||
return this.get('params').objectAt(2);
|
||||
}),
|
||||
|
||||
messageArgs(/*model, groupArray, memberId*/) {
|
||||
return [...arguments];
|
||||
},
|
||||
|
||||
successMessage(model, groupArray, memberId) {
|
||||
return `Successfully removed '${memberId}' from the group`;
|
||||
},
|
||||
|
||||
errorMessage(e, model, groupArray, memberId) {
|
||||
let error = e.errors ? e.errors.join(' ') : e.message;
|
||||
return `There was a problem removing '${memberId}' from the group - ${error}`;
|
||||
},
|
||||
|
||||
transaction(model, groupArray, memberId) {
|
||||
let members = model.get(groupArray);
|
||||
model.set(groupArray, members.without(memberId));
|
||||
return model.save();
|
||||
},
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
import Base from './_popup-base';
|
||||
import Ember from 'ember';
|
||||
const { computed } = Ember;
|
||||
|
||||
export default Base.extend({
|
||||
model: computed.alias('params.firstObject'),
|
||||
key: computed('params', function() {
|
||||
return this.get('params').objectAt(1);
|
||||
}),
|
||||
|
||||
messageArgs(model, key) {
|
||||
return [model, key];
|
||||
},
|
||||
|
||||
successMessage(model, key) {
|
||||
return `Successfully removed '${key}' from metadata`;
|
||||
},
|
||||
errorMessage(e, model, key) {
|
||||
let error = e.errors ? e.errors.join(' ') : e.message;
|
||||
return `There was a problem removing '${key}' from the metadata - ${error}`;
|
||||
},
|
||||
|
||||
transaction(model, key) {
|
||||
let metadata = model.get('metadata');
|
||||
delete metadata[key];
|
||||
model.set('metadata', { ...metadata });
|
||||
return model.save();
|
||||
},
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
import Base from './_popup-base';
|
||||
import Ember from 'ember';
|
||||
const { computed } = Ember;
|
||||
|
||||
export default Base.extend({
|
||||
model: computed.alias('params.firstObject'),
|
||||
policyName: computed('params', function() {
|
||||
return this.get('params').objectAt(1);
|
||||
}),
|
||||
|
||||
messageArgs(model, policyName) {
|
||||
return [model, policyName];
|
||||
},
|
||||
|
||||
successMessage(model, policyName) {
|
||||
return `Successfully removed '${policyName}' policy from ${model.id} `;
|
||||
},
|
||||
|
||||
errorMessage(e, model, policyName) {
|
||||
let error = e.errors ? e.errors.join(' ') : e.message;
|
||||
return `There was a problem removing '${policyName}' policy - ${error}`;
|
||||
},
|
||||
|
||||
transaction(model, policyName) {
|
||||
let policies = model.get('policies');
|
||||
model.set('policies', policies.without(policyName));
|
||||
return model.save();
|
||||
},
|
||||
});
|
|
@ -1,6 +1,7 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
'data-test-component': 'info-table-row',
|
||||
classNames: ['info-table-row'],
|
||||
isVisible: Ember.computed.or('alwaysRender', 'value'),
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ const { computed } = Ember;
|
|||
export default Ember.Component.extend({
|
||||
type: null,
|
||||
|
||||
yieldWithoutColumn: false,
|
||||
|
||||
classNameBindings: ['containerClass'],
|
||||
|
||||
containerClass: computed('type', function() {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: '',
|
||||
hasLevel: true,
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: '',
|
||||
|
||||
// api
|
||||
isCertTab: false,
|
||||
isConfigure: false,
|
||||
baseKey: null,
|
||||
backendCrumb: null,
|
||||
model: null,
|
||||
});
|
|
@ -71,10 +71,11 @@ export default Ember.Component.extend(DEFAULTS, {
|
|||
|
||||
handleSuccess(resp, action) {
|
||||
let props = {};
|
||||
if (resp && resp.data && action === 'unwrap') {
|
||||
props = Ember.assign({}, props, { unwrap_data: resp.data });
|
||||
let secret = (resp && resp.data) || resp.auth;
|
||||
if (secret && action === 'unwrap') {
|
||||
props = Ember.assign({}, props, { unwrap_data: secret });
|
||||
}
|
||||
props = Ember.assign({}, props, resp.data);
|
||||
props = Ember.assign({}, props, secret);
|
||||
|
||||
if (resp && resp.wrap_info) {
|
||||
const keyName = action === 'rewrap' ? 'rewrap_token' : 'token';
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import Ember from 'ember';
|
||||
import ListController from 'vault/mixins/list-controller';
|
||||
|
||||
export default Ember.Controller.extend(ListController);
|
||||
export default Ember.Controller.extend(ListController, {
|
||||
actions: {
|
||||
onDelete() {
|
||||
this.send('reload');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,7 +4,26 @@ import { task } from 'ember-concurrency';
|
|||
export default Ember.Controller.extend({
|
||||
showRoute: 'vault.cluster.access.identity.show',
|
||||
showTab: 'details',
|
||||
navToShow: task(function*(model) {
|
||||
yield this.transitionToRoute(this.get('showRoute'), model.id, this.get('showTab'));
|
||||
navAfterSave: task(function*({saveType, model}) {
|
||||
let isDelete = saveType === 'delete';
|
||||
let type = model.get('identityType');
|
||||
let listRoutes= {
|
||||
'entity-alias': 'vault.cluster.access.identity.aliases.index',
|
||||
'group-alias': 'vault.cluster.access.identity.aliases.index',
|
||||
'group': 'vault.cluster.access.identity.index',
|
||||
'entity': 'vault.cluster.access.identity.index',
|
||||
};
|
||||
let routeName = listRoutes[type]
|
||||
if (!isDelete) {
|
||||
yield this.transitionToRoute(
|
||||
this.get('showRoute'),
|
||||
model.id,
|
||||
this.get('showTab')
|
||||
);
|
||||
return;
|
||||
}
|
||||
yield this.transitionToRoute(
|
||||
routeName
|
||||
);
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -1,4 +1,46 @@
|
|||
import Ember from 'ember';
|
||||
import ListController from 'vault/mixins/list-controller';
|
||||
|
||||
export default Ember.Controller.extend(ListController);
|
||||
const { inject } = Ember;
|
||||
|
||||
export default Ember.Controller.extend(ListController, {
|
||||
flashMessages: inject.service(),
|
||||
|
||||
actions: {
|
||||
delete(model) {
|
||||
let type = model.get('identityType');
|
||||
let id = model.id;
|
||||
return model
|
||||
.destroyRecord()
|
||||
.then(() => {
|
||||
this.send('reload');
|
||||
this.get('flashMessages').success(`Successfully deleted ${type}: ${id}`);
|
||||
})
|
||||
.catch(e => {
|
||||
this.get('flashMessages').success(
|
||||
`There was a problem deleting ${type}: ${id} - ${e.error.join(' ') || e.message}`
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
toggleDisabled(model) {
|
||||
let action = model.get('disabled') ? ['enabled', 'enabling'] : ['disabled', 'disabling'];
|
||||
let type = model.get('identityType');
|
||||
let id = model.id;
|
||||
model.toggleProperty('disabled');
|
||||
|
||||
model.save().
|
||||
then(() => {
|
||||
this.get('flashMessages').success(`Successfully ${action[0]} ${type}: ${id}`);
|
||||
})
|
||||
.catch(e => {
|
||||
this.get('flashMessages').success(
|
||||
`There was a problem ${action[1]} ${type}: ${id} - ${e.error.join(' ') || e.message}`
|
||||
);
|
||||
});
|
||||
},
|
||||
reloadRecord(model) {
|
||||
model.reload();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import Ember from 'ember';
|
||||
import utils from 'vault/lib/key-utils';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
flashMessages: Ember.inject.service(),
|
||||
clusterController: Ember.inject.controller('vault.cluster'),
|
||||
const { inject, computed, Controller } = Ember;
|
||||
export default Controller.extend({
|
||||
flashMessages: inject.service(),
|
||||
store: inject.service(),
|
||||
clusterController: inject.controller('vault.cluster'),
|
||||
queryParams: {
|
||||
page: 'page',
|
||||
pageFilter: 'pageFilter',
|
||||
|
@ -13,7 +15,7 @@ export default Ember.Controller.extend({
|
|||
pageFilter: null,
|
||||
filter: null,
|
||||
|
||||
backendCrumb: Ember.computed(function() {
|
||||
backendCrumb: computed(function() {
|
||||
return {
|
||||
label: 'leases',
|
||||
text: 'leases',
|
||||
|
@ -24,13 +26,13 @@ export default Ember.Controller.extend({
|
|||
|
||||
isLoading: false,
|
||||
|
||||
filterMatchesKey: Ember.computed('filter', 'model', 'model.[]', function() {
|
||||
filterMatchesKey: computed('filter', 'model', 'model.[]', function() {
|
||||
var filter = this.get('filter');
|
||||
var content = this.get('model');
|
||||
return !!(content.length && content.findBy('id', filter));
|
||||
}),
|
||||
|
||||
firstPartialMatch: Ember.computed('filter', 'model', 'model.[]', 'filterMatchesKey', function() {
|
||||
firstPartialMatch: computed('filter', 'model', 'model.[]', 'filterMatchesKey', function() {
|
||||
var filter = this.get('filter');
|
||||
var content = this.get('model');
|
||||
var filterMatchesKey = this.get('filterMatchesKey');
|
||||
|
@ -42,7 +44,7 @@ export default Ember.Controller.extend({
|
|||
});
|
||||
}),
|
||||
|
||||
filterIsFolder: Ember.computed('filter', function() {
|
||||
filterIsFolder: computed('filter', function() {
|
||||
return !!utils.keyIsFolder(this.get('filter'));
|
||||
}),
|
||||
|
||||
|
@ -56,7 +58,7 @@ export default Ember.Controller.extend({
|
|||
},
|
||||
|
||||
revokePrefix(prefix, isForce) {
|
||||
const adapter = this.model.store.adapterFor('lease');
|
||||
const adapter = this.get('store').adapterFor('lease');
|
||||
const method = isForce ? 'forceRevokePrefix' : 'revokePrefix';
|
||||
const fn = adapter[method];
|
||||
fn
|
||||
|
|
|
@ -66,6 +66,7 @@ export default Ember.Controller.extend(BackendCrumbMixin, {
|
|||
delete(item) {
|
||||
const name = item.id;
|
||||
item.destroyRecord().then(() => {
|
||||
this.send('reload');
|
||||
this.get('flashMessages').success(`${name} was successfully deleted.`);
|
||||
});
|
||||
},
|
||||
|
|
|
@ -30,7 +30,6 @@ export default Ember.Controller.extend({
|
|||
description: null,
|
||||
default_lease_ttl: null,
|
||||
max_lease_ttl: null,
|
||||
force_no_cache: null,
|
||||
showConfig: false,
|
||||
local: false,
|
||||
sealWrap: false,
|
||||
|
@ -50,7 +49,6 @@ export default Ember.Controller.extend({
|
|||
description: null,
|
||||
default_lease_ttl: null,
|
||||
max_lease_ttl: null,
|
||||
force_no_cache: null,
|
||||
local: false,
|
||||
showConfig: false,
|
||||
sealWrap: false,
|
||||
|
@ -82,7 +80,6 @@ export default Ember.Controller.extend({
|
|||
selectedType: type,
|
||||
description,
|
||||
default_lease_ttl,
|
||||
force_no_cache,
|
||||
local,
|
||||
max_lease_ttl,
|
||||
sealWrap,
|
||||
|
@ -92,7 +89,6 @@ export default Ember.Controller.extend({
|
|||
'selectedType',
|
||||
'description',
|
||||
'default_lease_ttl',
|
||||
'force_no_cache',
|
||||
'local',
|
||||
'max_lease_ttl',
|
||||
'sealWrap',
|
||||
|
@ -112,9 +108,8 @@ export default Ember.Controller.extend({
|
|||
|
||||
if (this.get('showConfig')) {
|
||||
attrs.config = {
|
||||
default_lease_ttl,
|
||||
max_lease_ttl,
|
||||
force_no_cache,
|
||||
defaultLeaseTtl: default_lease_ttl,
|
||||
maxLeaseTtl: max_lease_ttl,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
|
||||
|
||||
export default function() {
|
||||
return lazyCapabilities(apiPath`identity/${'identityType'}/id/${'id'}`, 'id', 'identityType');
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import { queryRecord } from 'ember-computed-query';
|
||||
|
||||
export function apiPath(strings, ...keys) {
|
||||
return function(data) {
|
||||
let dict = data || {};
|
||||
let result = [strings[0]];
|
||||
keys.forEach((key, i) => {
|
||||
result.push(dict[key], strings[i + 1]);
|
||||
});
|
||||
return result.join('');
|
||||
};
|
||||
}
|
||||
|
||||
export default function() {
|
||||
let [templateFn, ...keys] = arguments;
|
||||
return queryRecord(
|
||||
'capabilities',
|
||||
context => {
|
||||
return {
|
||||
id: templateFn(context.getProperties(...keys)),
|
||||
};
|
||||
},
|
||||
...keys
|
||||
);
|
||||
}
|
|
@ -1,8 +1,12 @@
|
|||
import IdentityModel from './_base';
|
||||
import DS from 'ember-data';
|
||||
import Ember from 'ember';
|
||||
import identityCapabilities from 'vault/macros/identity-capabilities';
|
||||
const { attr, belongsTo } = DS;
|
||||
const { computed } = Ember;
|
||||
|
||||
export default IdentityModel.extend({
|
||||
parentType: 'entity',
|
||||
formFields: ['name', 'mountAccessor', 'metadata'],
|
||||
entity: belongsTo('identity/entity', { readOnly: true, async: false }),
|
||||
|
||||
|
@ -12,7 +16,7 @@ export default IdentityModel.extend({
|
|||
label: 'Auth Backend',
|
||||
editType: 'mountAccessor',
|
||||
}),
|
||||
metadata: attr('object', {
|
||||
metadata: attr({
|
||||
editType: 'kv',
|
||||
}),
|
||||
mountPath: attr('string', {
|
||||
|
@ -28,4 +32,8 @@ export default IdentityModel.extend({
|
|||
readOnly: true,
|
||||
}),
|
||||
mergedFromCanonicalIds: attr(),
|
||||
|
||||
updatePath: identityCapabilities(),
|
||||
canDelete: computed.alias('updatePath.canDelete'),
|
||||
canEdit: computed.alias('updatePath.canUpdate'),
|
||||
});
|
||||
|
|
|
@ -1,12 +1,23 @@
|
|||
import Ember from 'ember';
|
||||
import IdentityModel from './_base';
|
||||
import DS from 'ember-data';
|
||||
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
|
||||
import identityCapabilities from 'vault/macros/identity-capabilities';
|
||||
|
||||
const { computed } = Ember;
|
||||
|
||||
const { attr, hasMany } = DS;
|
||||
|
||||
export default IdentityModel.extend({
|
||||
formFields: ['name', 'policies', 'metadata'],
|
||||
formFields: ['name', 'disabled', 'policies', 'metadata'],
|
||||
name: attr('string'),
|
||||
disabled: attr('boolean', {
|
||||
defaultValue: false,
|
||||
label: 'Disable entity',
|
||||
helpText: 'All associated tokens cannot be used, but are not revoked.',
|
||||
}),
|
||||
mergedEntityIds: attr(),
|
||||
metadata: attr('object', {
|
||||
metadata: attr({
|
||||
editType: 'kv',
|
||||
}),
|
||||
policies: attr({
|
||||
|
@ -28,4 +39,11 @@ export default IdentityModel.extend({
|
|||
inheritedGroupIds: attr({
|
||||
readOnly: true,
|
||||
}),
|
||||
|
||||
updatePath: identityCapabilities(),
|
||||
canDelete: computed.alias('updatePath.canDelete'),
|
||||
canEdit: computed.alias('updatePath.canUpdate'),
|
||||
|
||||
aliasPath: lazyCapabilities(apiPath`identity/entity-alias`),
|
||||
canAddAlias: computed.alias('aliasPath.canCreate'),
|
||||
});
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
import IdentityModel from './_base';
|
||||
import DS from 'ember-data';
|
||||
import Ember from 'ember';
|
||||
import identityCapabilities from 'vault/macros/identity-capabilities';
|
||||
|
||||
const { attr, belongsTo } = DS;
|
||||
const { computed } = Ember;
|
||||
|
||||
export default IdentityModel.extend({
|
||||
parentType: 'group',
|
||||
formFields: ['name', 'mountAccessor'],
|
||||
group: belongsTo('identity/group', { readOnly: true, async: false }),
|
||||
|
||||
|
@ -26,4 +31,9 @@ export default IdentityModel.extend({
|
|||
lastUpdateTime: attr('string', {
|
||||
readOnly: true,
|
||||
}),
|
||||
|
||||
updatePath: identityCapabilities(),
|
||||
canDelete: computed.alias('updatePath.canDelete'),
|
||||
canEdit: computed.alias('updatePath.canUpdate'),
|
||||
|
||||
});
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import Ember from 'ember';
|
||||
import IdentityModel from './_base';
|
||||
import DS from 'ember-data';
|
||||
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
|
||||
import identityCapabilities from 'vault/macros/identity-capabilities';
|
||||
|
||||
const { computed } = Ember;
|
||||
const { attr, belongsTo } = DS;
|
||||
|
@ -52,4 +54,18 @@ export default IdentityModel.extend({
|
|||
),
|
||||
|
||||
alias: belongsTo('identity/group-alias', { async: false, readOnly: true }),
|
||||
updatePath: identityCapabilities(),
|
||||
canDelete: computed.alias('updatePath.canDelete'),
|
||||
canEdit: computed.alias('updatePath.canUpdate'),
|
||||
|
||||
aliasPath: lazyCapabilities(apiPath`identity/group-alias`),
|
||||
canAddAlias: computed('aliasPath.canCreate', 'type', 'alias', function() {
|
||||
let type = this.get('type');
|
||||
let alias = this.get('alias');
|
||||
// internal groups can't have aliases, and external groups can only have one
|
||||
if (type === 'internal' || alias) {
|
||||
return false;
|
||||
}
|
||||
return this.get('aliasPath.canCreate');
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -2,5 +2,7 @@ import attr from 'ember-data/attr';
|
|||
import Fragment from 'ember-data-model-fragments/fragment';
|
||||
|
||||
export default Fragment.extend({
|
||||
version: attr('number'),
|
||||
version: attr('number', {
|
||||
label: 'Version',
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -3,6 +3,8 @@ import DS from 'ember-data';
|
|||
import { queryRecord } from 'ember-computed-query';
|
||||
import { fragment } from 'ember-data-model-fragments/attributes';
|
||||
|
||||
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
||||
|
||||
const { attr } = DS;
|
||||
const { computed } = Ember;
|
||||
|
||||
|
@ -16,11 +18,26 @@ export default DS.Model.extend({
|
|||
name: attr('string'),
|
||||
type: attr('string'),
|
||||
description: attr('string'),
|
||||
config: attr('object'),
|
||||
options: fragment('mount-options'),
|
||||
config: fragment('mount-config', { defaultValue: {} }),
|
||||
options: fragment('mount-options', { defaultValue: {} }),
|
||||
local: attr('boolean'),
|
||||
sealWrap: attr('boolean'),
|
||||
|
||||
formFields: [
|
||||
'type',
|
||||
'path',
|
||||
'description',
|
||||
'accessor',
|
||||
'local',
|
||||
'sealWrap',
|
||||
'config.{defaultLeaseTtl,maxLeaseTtl}',
|
||||
'options.{version}',
|
||||
],
|
||||
|
||||
attrs: computed('formFields', function() {
|
||||
return expandAttributeMeta(this, this.get('formFields'));
|
||||
}),
|
||||
|
||||
shouldIncludeInList: computed('type', function() {
|
||||
return !LIST_EXCLUDED_BACKENDS.includes(this.get('type'));
|
||||
}),
|
||||
|
|
|
@ -68,6 +68,7 @@ Router.map(function() {
|
|||
this.route('backends', { path: '/' });
|
||||
this.route('backend', { path: '/:backend' }, function() {
|
||||
this.route('index', { path: '/' });
|
||||
this.route('configuration');
|
||||
// because globs / params can't be empty,
|
||||
// we have to special-case ids of '' with thier own routes
|
||||
this.route('list-root', { path: '/list/' });
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import Ember from 'ember';
|
||||
import UnloadModelRoute from 'vault/mixins/unload-model-route';
|
||||
import UnsavedModelRoute from 'vault/mixins/unsaved-model-route';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
export default Ember.Route.extend(UnloadModelRoute, UnsavedModelRoute, {
|
||||
model(params) {
|
||||
let itemType = this.modelFor('vault.cluster.access.identity');
|
||||
let modelType = `identity/${itemType}-alias`;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import Ember from 'ember';
|
||||
import UnloadModelRoute from 'vault/mixins/unload-model-route';
|
||||
import UnsavedModelRoute from 'vault/mixins/unsaved-model-route';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
export default Ember.Route.extend(UnloadModelRoute, UnsavedModelRoute, {
|
||||
model(params) {
|
||||
let itemType = this.modelFor('vault.cluster.access.identity');
|
||||
let modelType = `identity/${itemType}-alias`;
|
||||
|
|
|
@ -27,10 +27,14 @@ export default Ember.Route.extend(ListRoute, {
|
|||
actions: {
|
||||
willTransition(transition) {
|
||||
window.scrollTo(0, 0);
|
||||
if (transition.targetName !== this.routeName) {
|
||||
if (!transition || transition.targetName !== this.routeName) {
|
||||
this.store.clearAllDatasets();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
reload() {
|
||||
this.store.clearAllDatasets();
|
||||
this.refresh();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import Ember from 'ember';
|
||||
import UnloadModelRoute from 'vault/mixins/unload-model-route';
|
||||
import UnsavedModelRoute from 'vault/mixins/unsaved-model-route';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
export default Ember.Route.extend(UnloadModelRoute, UnsavedModelRoute, {
|
||||
model() {
|
||||
let itemType = this.modelFor('vault.cluster.access.identity');
|
||||
let modelType = `identity/${itemType}`;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import Ember from 'ember';
|
||||
import UnloadModelRoute from 'vault/mixins/unload-model-route';
|
||||
import UnsavedModelRoute from 'vault/mixins/unsaved-model-route';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
export default Ember.Route.extend(UnloadModelRoute, UnsavedModelRoute, {
|
||||
model(params) {
|
||||
let itemType = this.modelFor('vault.cluster.access.identity');
|
||||
let modelType = `identity/${itemType}`;
|
||||
|
|
|
@ -34,5 +34,9 @@ export default Ember.Route.extend(ListRoute, {
|
|||
}
|
||||
return true;
|
||||
},
|
||||
reload() {
|
||||
this.store.clearAllDatasets();
|
||||
this.refresh();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -13,13 +13,36 @@ export default Ember.Route.extend({
|
|||
Ember.set(error, 'httpStatus', 404);
|
||||
throw error;
|
||||
}
|
||||
// TODO peekRecord here to see if we have the record already
|
||||
|
||||
// if the record is in the store use that
|
||||
let model = this.store.peekRecord(modelType, params.item_id);
|
||||
|
||||
// if we don't have creationTime, we only have a partial model so reload
|
||||
if (model && !model.get('creationTime')) {
|
||||
model = model.reload();
|
||||
}
|
||||
|
||||
// if there's no model, we need to fetch it
|
||||
if (!model) {
|
||||
model = this.store.findRecord(modelType, params.item_id);
|
||||
}
|
||||
|
||||
return Ember.RSVP.hash({
|
||||
model: this.store.findRecord(modelType, params.item_id),
|
||||
model,
|
||||
section,
|
||||
});
|
||||
},
|
||||
|
||||
activate() {
|
||||
// if we're just entering the route, and it's not a hard reload
|
||||
// reload to make sure we have the newest info
|
||||
if (this.currentModel) {
|
||||
Ember.run.next(() => {
|
||||
this.controller.get('model').reload();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
afterModel(resolvedModel) {
|
||||
let { section, model } = resolvedModel;
|
||||
if (model.get('identityType') === 'group' && model.get('type') === 'internal' && section === 'aliases') {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
model() {
|
||||
return this.modelFor('vault.cluster.secrets.backend');
|
||||
},
|
||||
});
|
|
@ -159,5 +159,9 @@ export default Ember.Route.extend({
|
|||
}
|
||||
return true;
|
||||
},
|
||||
reload() {
|
||||
this.refresh();
|
||||
this.store.clearAllDatasets();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -131,13 +131,25 @@ export default DS.Store.extend({
|
|||
// pushes records into the store and returns the result
|
||||
fetchPage(modelName, query) {
|
||||
const response = this.constructResponse(modelName, query);
|
||||
this.unloadAll(modelName);
|
||||
this.peekAll(modelName).forEach(record => {
|
||||
record.unloadRecord();
|
||||
});
|
||||
return new Ember.RSVP.Promise(resolve => {
|
||||
Ember.run.schedule('destroy', () => {
|
||||
this.push(
|
||||
this.serializerFor(modelName).normalizeResponse(this, this.modelFor(modelName), response, null, 'query')
|
||||
this.serializerFor(modelName).normalizeResponse(
|
||||
this,
|
||||
this.modelFor(modelName),
|
||||
response,
|
||||
null,
|
||||
'query'
|
||||
)
|
||||
);
|
||||
const model = this.peekAll(modelName);
|
||||
let model = this.peekAll(modelName).toArray();
|
||||
model.set('meta', response.meta);
|
||||
return model;
|
||||
resolve(model);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// get cached data
|
||||
|
|
|
@ -20,8 +20,7 @@
|
|||
}
|
||||
}
|
||||
.popup-menu-trigger {
|
||||
width: 3rem;
|
||||
height: 2rem;
|
||||
min-width: auto;
|
||||
}
|
||||
.popup-menu-trigger.is-active {
|
||||
&,
|
||||
|
@ -50,6 +49,7 @@
|
|||
height: auto;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
background-color: $menu-item-hover-background-color;
|
||||
|
|
|
@ -133,6 +133,17 @@ $button-box-shadow-standard: 0 3px 1px 0 rgba($black, 0.12);
|
|||
}
|
||||
}
|
||||
|
||||
&.is-orange {
|
||||
background-color: $orange;
|
||||
border-color: $orange;
|
||||
color: $white;
|
||||
|
||||
&:hover,
|
||||
&.is-hovered {
|
||||
background-color: darken($orange, 5%);
|
||||
border-color: darken($orange, 5%);
|
||||
}
|
||||
}
|
||||
&.is-compact {
|
||||
height: 2rem;
|
||||
padding: $size-11 $size-8;
|
||||
|
@ -146,7 +157,6 @@ $button-box-shadow-standard: 0 3px 1px 0 rgba($black, 0.12);
|
|||
}
|
||||
}
|
||||
|
||||
&.is-more-icon,
|
||||
&.tool-tip-trigger {
|
||||
color: $black;
|
||||
min-width: auto;
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
{{/if}}
|
||||
</li>
|
||||
<li class="{{if (is-active-route (array 'vault.cluster.policies' 'vault.cluster.policy')) 'is-active'}}">
|
||||
<a href="{{href-to "vault.cluster.policies" activeClusterName current-when='vault.cluster.policies vault.cluster.policy'}}">
|
||||
<a href="{{href-to "vault.cluster.policies" "acl" current-when='vault.cluster.policies vault.cluster.policy'}}">
|
||||
Policies
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
{{form-field data-test-field attr=attr model=model}}
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
|
||||
<div class="field is-grouped is-grouped-split is-fullwidth box is-bottomless">
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button type="submit" data-test-identity-submit=true class="button is-primary {{if save.isRunning 'loading'}}" disabled={{save.isRunning}}>
|
||||
{{#if (eq mode "create")}}
|
||||
|
@ -20,14 +22,27 @@
|
|||
{{/if}}
|
||||
</button>
|
||||
{{#if (or (eq mode "merge") (eq mode "create" ))}}
|
||||
<a href={{href-to cancelLink}} class="button">
|
||||
<a href={{href-to cancelLink}} class="button" data-test-cancel-link>
|
||||
Cancel
|
||||
</a>
|
||||
{{else}}
|
||||
<a href={{href-to cancelLink model.id "details"}} class="button">
|
||||
<a href={{href-to cancelLink model.id "details"}} class="button" data-test-cancel-link>
|
||||
Cancel
|
||||
</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if (and (eq mode "edit") model.canDelete)}}
|
||||
{{#confirm-action
|
||||
buttonClasses="button is-ghost"
|
||||
onConfirmAction=(action "deleteItem" model)
|
||||
confirmMessage=(concat "Are you sure you want to delete " model.id "?")
|
||||
data-test-entity-item-delete=true
|
||||
}}
|
||||
Delete
|
||||
{{/confirm-action}}
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
</div>
|
||||
<div class="level-right">
|
||||
{{#if (eq identityType "entity")}}
|
||||
<a href="{{href-to 'vault.cluster.access.identity.merge'}}" class="button has-icon-right is-ghost is-compact" data-test-entity-merge-link=true>
|
||||
<a href="{{href-to 'vault.cluster.access.identity.merge' (pluralize identityType)}}" class="button has-icon-right is-ghost is-compact" data-test-entity-merge-link=true>
|
||||
Merge {{pluralize identityType}}
|
||||
{{i-con glyph="chevron-right" size=11}}
|
||||
</a>
|
||||
{{/if}}
|
||||
<a href="{{href-to 'vault.cluster.access.identity.create'}}" class="button has-icon-right is-ghost is-compact" data-test-entity-create-link=true>
|
||||
<a href="{{href-to 'vault.cluster.access.identity.create' (pluralize identityType)}}" class="button has-icon-right is-ghost is-compact" data-test-entity-create-link=true>
|
||||
Create {{identityType}}
|
||||
{{i-con glyph="chevron-right" size=11}}
|
||||
</a>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{{info-table-row label="Name" value=model.name }}
|
||||
{{info-table-row label="Name" value=model.name data-test-alias-name=true}}
|
||||
{{info-table-row label="ID" value=model.id }}
|
||||
{{#info-table-row label=(if (eq model.identityType "entity-alias") "Entity ID" "Group ID") value=model.canonicalId}}
|
||||
<a href={{href-to 'vault.cluster.access.identity.show' (if (eq model.identityType "entity-alias") "entities" "groups") model.canonicalId "details"}}
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
{{value}}
|
||||
</div>
|
||||
<div class="column has-text-right">
|
||||
{{#if model.canEdit}}
|
||||
{{identity/popup-metadata params=(array model key)}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<code class="has-text-grey is-size-8">{{item.mountAccessor}}</code>
|
||||
</div>
|
||||
<div class="column has-text-right">
|
||||
{{identity/popup-alias params=(array item)}}
|
||||
</div>
|
||||
</div>
|
||||
{{/linked-block}}
|
||||
|
|
|
@ -1,4 +1,20 @@
|
|||
{{info-table-row label="Name" value=model.name }}
|
||||
{{#if model.disabled}}
|
||||
<div class="box is-shadowless is-marginless">
|
||||
{{#message-in-page type="warning" yieldWithoutColumn=true messageClass="message-body is-marginless" data-test-disabled-warning=true}}
|
||||
<div class="column">
|
||||
<strong>Attention</strong> This {{model.identityType}} is disabled. All associated tokens cannot be used, but are not revoked.
|
||||
</div>
|
||||
{{#if model.canEdit}}
|
||||
<div class="column is-flex-v-centered is-narrow">
|
||||
<button type="button" class="button is-orange box" {{action "enable" model}} data-test-enable=true>
|
||||
Enable
|
||||
</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/message-in-page}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{info-table-row label="Name" value=model.name data-test-identity-item-name=true}}
|
||||
{{info-table-row label="Type" value=model.type }}
|
||||
{{info-table-row label="ID" value=model.id }}
|
||||
{{#info-table-row label="Merged Ids" value=model.mergedEntityIds }}
|
||||
|
|
|
@ -1,21 +1,56 @@
|
|||
{{#if model.hasMembers}}
|
||||
{{#each model.memberGroupIds as |gid|}}
|
||||
<a href={{href-to "vault.cluster.access.identity.show" "groups" gid "details" }}
|
||||
|
||||
{{#linked-block
|
||||
"vault.cluster.access.identity.show"
|
||||
"groups"
|
||||
gid
|
||||
details
|
||||
class="box is-sideless is-marginless"
|
||||
}}
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-10">
|
||||
<a href={{href-to "vault.cluster.access.identity.show" "groups" gid "details" }}
|
||||
class="is-block has-text-black has-text-weight-semibold"
|
||||
>{{i-con
|
||||
glyph='folder'
|
||||
size=14
|
||||
class="has-text-grey-light"
|
||||
}}{{gid}}</a>
|
||||
</div>
|
||||
<div class="column has-text-right">
|
||||
{{#if model.canEdit}}
|
||||
{{identity/popup-members params=(array model "memberGroupIds" gid)}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/linked-block}}
|
||||
{{/each}}
|
||||
{{#each model.memberEntityIds as |gid|}}
|
||||
<a href={{href-to "vault.cluster.access.identity.show" "entities" gid "details" }}
|
||||
{{#linked-block
|
||||
"vault.cluster.access.identity.show"
|
||||
"groups"
|
||||
gid
|
||||
details
|
||||
class="box is-sideless is-marginless"
|
||||
}}
|
||||
<div class="columns">
|
||||
<div class="column is-10">
|
||||
<a href={{href-to "vault.cluster.access.identity.show" "entities" gid "details" }}
|
||||
class="is-block has-text-black has-text-weight-semibold"
|
||||
>{{i-con
|
||||
glyph='role'
|
||||
size=14
|
||||
class="has-text-grey-light"
|
||||
}}{{gid}}</a>
|
||||
</div>
|
||||
<div class="column has-text-right">
|
||||
{{#if model.canEdit}}
|
||||
{{identity/popup-members params=(array model "memberEntityIds" gid)}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/linked-block}}
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<div class="box is-bottomless has-background-white-bis">
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
{{value}}
|
||||
</div>
|
||||
<div class="column has-text-right">
|
||||
{{#if model.canEdit}}
|
||||
{{identity/popup-metadata params=(array model key)}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{{#each model.policies as |item|}}
|
||||
{{#each model.policies as |policyName|}}
|
||||
{{#linked-block
|
||||
"vault.cluster.policy.show"
|
||||
"acl"
|
||||
|
@ -7,12 +7,15 @@
|
|||
}}
|
||||
<div class="columns is-mobile">
|
||||
<div class="column is-10">
|
||||
<a href={{href-to "vault.cluster.policy.show" "acl" item}}
|
||||
class="has-text-black has-text-weight-semibold"
|
||||
><span class="is-underline">{{item}}</span>
|
||||
<a href={{href-to "vault.cluster.policy.show" "acl" policyName}}
|
||||
class="is-block has-text-black has-text-weight-semibold"
|
||||
><span class="is-underline">{{policyName}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="column has-text-right">
|
||||
{{#if model.canEdit}}
|
||||
{{identity/popup-policy params=(array model policyName)}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/linked-block}}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
{{#popup-menu name="alias-menu"}}
|
||||
{{#with params.firstObject as |item|}}
|
||||
<nav class="menu">
|
||||
<ul class="menu-list">
|
||||
<li class="action">
|
||||
<a href={{href-to "vault.cluster.access.identity.aliases.show" (pluralize item.parentType) item.id "details" }}>
|
||||
Details
|
||||
</a>
|
||||
</li>
|
||||
{{#if item.updatePath.isPending}}
|
||||
<li class="action">
|
||||
<button disabled=true type="button" class="link button is-loading is-transparent">
|
||||
loading
|
||||
</button>
|
||||
</li>
|
||||
{{else}}
|
||||
{{#if item.canEdit}}
|
||||
<li class="action">
|
||||
<a href={{href-to "vault.cluster.access.identity.aliases.edit" (pluralize item.parentType) item.id}}>
|
||||
Edit
|
||||
</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
{{#if item.canDelete}}
|
||||
<li class="action">
|
||||
{{#confirm-action
|
||||
data-test-item-delete=true
|
||||
confirmButtonClasses="button is-primary"
|
||||
buttonClasses="link"
|
||||
onConfirmAction=(action "performTransaction" item)
|
||||
confirmMessage=(concat "Are you sure you want to delete " item.id "?")
|
||||
showConfirm=(get this (concat "shouldDelete-" item.id))
|
||||
class=(if (get this (concat "shouldDelete-" item.id)) "message is-block is-warning is-outline")
|
||||
containerClasses="message-body is-block"
|
||||
messageClasses="is-block"
|
||||
}}
|
||||
Delete
|
||||
{{/confirm-action}}
|
||||
</li>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</ul>
|
||||
</nav>
|
||||
{{/with}}
|
||||
{{/popup-menu}}
|
|
@ -0,0 +1,21 @@
|
|||
{{#popup-menu name="member-edit-menu"}}
|
||||
<nav class="menu">
|
||||
<ul class="menu-list">
|
||||
<li class="action">
|
||||
{{#confirm-action
|
||||
confirmButtonClasses="button is-primary"
|
||||
confirmButtonText="Remove"
|
||||
buttonClasses="link"
|
||||
onConfirmAction=(action "performTransaction" model groupArray memberId)
|
||||
confirmMessage=(concat "Are you sure you want to remove " memberId "?")
|
||||
showConfirm=(get this (concat "shouldDelete-" memberId))
|
||||
class=(if (get this (concat "shouldDelete-" memberId)) "message is-block is-warning is-outline")
|
||||
containerClasses="message-body is-block"
|
||||
messageClasses="is-block"
|
||||
}}
|
||||
Remove
|
||||
{{/confirm-action}}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{{/popup-menu}}
|
|
@ -0,0 +1,21 @@
|
|||
{{#popup-menu name="metadata-edit-menu"}}
|
||||
<nav class="menu">
|
||||
<ul class="menu-list">
|
||||
<li class="action">
|
||||
{{#confirm-action
|
||||
confirmButtonClasses="button is-primary"
|
||||
confirmButtonText="Remove"
|
||||
buttonClasses="link"
|
||||
onConfirmAction=(action "performTransaction" model key)
|
||||
confirmMessage=(concat "Are you sure you want to remove " key "?")
|
||||
showConfirm=(get this (concat "shouldDelete-" key))
|
||||
class=(if (get this (concat "shouldDelete-" key)) "message is-block is-warning is-outline")
|
||||
containerClasses="message-body is-block"
|
||||
messageClasses="is-block"
|
||||
}}
|
||||
Remove
|
||||
{{/confirm-action}}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{{/popup-menu}}
|
|
@ -0,0 +1,31 @@
|
|||
{{#popup-menu name="policy-menu"}}
|
||||
<nav class="menu">
|
||||
<ul class="menu-list">
|
||||
<li class="action">
|
||||
<a href={{href-to "vault.cluster.policy.show" "acl" policyName }}>
|
||||
View Policy
|
||||
</a>
|
||||
</li>
|
||||
<li class="action">
|
||||
<a href={{href-to "vault.cluster.policy.edit" "acl" policyName }}>
|
||||
Edit Policy
|
||||
</a>
|
||||
</li>
|
||||
<li class="action">
|
||||
{{#confirm-action
|
||||
confirmButtonClasses="button is-primary"
|
||||
confirmButtonText="Remove"
|
||||
buttonClasses="link"
|
||||
onConfirmAction=(action "performTransaction" model policyName)
|
||||
confirmMessage=(concat "Are you sure you want to remove " policyName "?")
|
||||
showConfirm=(get this (concat "shouldDelete-" policyName))
|
||||
class=(if (get this (concat "shouldDelete-" policyName)) "message is-block is-warning is-outline")
|
||||
containerClasses="message-body is-block"
|
||||
messageClasses="is-block"
|
||||
}}
|
||||
Remove from {{model.identityType}}
|
||||
{{/confirm-action}}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{{/popup-menu}}
|
|
@ -1,4 +1,5 @@
|
|||
<ul>
|
||||
{{yield}}
|
||||
{{#each secretPath as |path index|}}
|
||||
<li class="{{if (is-active-route path.path path.model isExact=true) 'is-active'}}">
|
||||
<span class="sep">/</span>
|
||||
|
|
|
@ -8,11 +8,15 @@
|
|||
excludeIconClass=true
|
||||
}}
|
||||
</div>
|
||||
{{#if yieldWithoutColumn}}
|
||||
{{yield}}
|
||||
{{else}}
|
||||
<div class="column">
|
||||
<p>
|
||||
<strong>{{alertType.text}}</strong>
|
||||
{{yield}}
|
||||
</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{{yield}}
|
|
@ -0,0 +1 @@
|
|||
{{yield}}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue