Merge pull request #1216 from hashicorp/userpass-update
Userpass: Update the password and policies associated to user
This commit is contained in:
commit
2c0c901eac
|
@ -29,6 +29,8 @@ func Backend() *framework.Backend {
|
|||
|
||||
Paths: append([]*framework.Path{
|
||||
pathUsers(&b),
|
||||
pathUserPolicies(&b),
|
||||
pathUserPassword(&b),
|
||||
},
|
||||
mfa.MFAPaths(b.Backend, pathLogin(&b))...,
|
||||
),
|
||||
|
|
|
@ -81,7 +81,7 @@ func TestBackend_basic(t *testing.T) {
|
|||
Backend: b,
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepUser(t, "web", "password", "foo"),
|
||||
testAccStepLogin(t, "web", "password"),
|
||||
testAccStepLogin(t, "web", "password", []string{"default", "foo"}),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -109,6 +109,98 @@ func TestBackend_userCrud(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestBackend_userCreateOperation(t *testing.T) {
|
||||
b, err := Factory(&logical.BackendConfig{
|
||||
Logger: nil,
|
||||
System: &logical.StaticSystemView{
|
||||
DefaultLeaseTTLVal: testSysTTL,
|
||||
MaxLeaseTTLVal: testSysMaxTTL,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create backend: %s", err)
|
||||
}
|
||||
|
||||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
Backend: b,
|
||||
Steps: []logicaltest.TestStep{
|
||||
testUserCreateOperation(t, "web", "password", "foo"),
|
||||
testAccStepLogin(t, "web", "password", []string{"default", "foo"}),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestBackend_passwordUpdate(t *testing.T) {
|
||||
b, err := Factory(&logical.BackendConfig{
|
||||
Logger: nil,
|
||||
System: &logical.StaticSystemView{
|
||||
DefaultLeaseTTLVal: testSysTTL,
|
||||
MaxLeaseTTLVal: testSysMaxTTL,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create backend: %s", err)
|
||||
}
|
||||
|
||||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
Backend: b,
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepUser(t, "web", "password", "foo"),
|
||||
testAccStepReadUser(t, "web", "foo"),
|
||||
testAccStepLogin(t, "web", "password", []string{"default", "foo"}),
|
||||
testUpdatePassword(t, "web", "newpassword"),
|
||||
testAccStepLogin(t, "web", "newpassword", []string{"default", "foo"}),
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestBackend_policiesUpdate(t *testing.T) {
|
||||
b, err := Factory(&logical.BackendConfig{
|
||||
Logger: nil,
|
||||
System: &logical.StaticSystemView{
|
||||
DefaultLeaseTTLVal: testSysTTL,
|
||||
MaxLeaseTTLVal: testSysMaxTTL,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create backend: %s", err)
|
||||
}
|
||||
|
||||
logicaltest.Test(t, logicaltest.TestCase{
|
||||
Backend: b,
|
||||
Steps: []logicaltest.TestStep{
|
||||
testAccStepUser(t, "web", "password", "foo"),
|
||||
testAccStepReadUser(t, "web", "foo"),
|
||||
testAccStepLogin(t, "web", "password", []string{"default", "foo"}),
|
||||
testUpdatePolicies(t, "web", "foo,bar"),
|
||||
testAccStepReadUser(t, "web", "foo,bar"),
|
||||
testAccStepLogin(t, "web", "password", []string{"bar", "default", "foo"}),
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func testUpdatePassword(t *testing.T, user, password string) logicaltest.TestStep {
|
||||
return logicaltest.TestStep{
|
||||
Operation: logical.UpdateOperation,
|
||||
Path: "users/" + user + "/password",
|
||||
Data: map[string]interface{}{
|
||||
"password": password,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testUpdatePolicies(t *testing.T, user, policies string) logicaltest.TestStep {
|
||||
return logicaltest.TestStep{
|
||||
Operation: logical.UpdateOperation,
|
||||
Path: "users/" + user + "/policies",
|
||||
Data: map[string]interface{}{
|
||||
"policies": policies,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testUsersWrite(t *testing.T, user string, data map[string]interface{}, expectError bool) logicaltest.TestStep {
|
||||
return logicaltest.TestStep{
|
||||
Operation: logical.UpdateOperation,
|
||||
|
@ -139,7 +231,7 @@ func testLoginWrite(t *testing.T, user string, data map[string]interface{}, expe
|
|||
}
|
||||
}
|
||||
|
||||
func testAccStepLogin(t *testing.T, user string, pass string) logicaltest.TestStep {
|
||||
func testAccStepLogin(t *testing.T, user string, pass string, policies []string) logicaltest.TestStep {
|
||||
return logicaltest.TestStep{
|
||||
Operation: logical.UpdateOperation,
|
||||
Path: "login/" + user,
|
||||
|
@ -148,7 +240,19 @@ func testAccStepLogin(t *testing.T, user string, pass string) logicaltest.TestSt
|
|||
},
|
||||
Unauthenticated: true,
|
||||
|
||||
Check: logicaltest.TestCheckAuth([]string{"foo"}),
|
||||
Check: logicaltest.TestCheckAuth(policies),
|
||||
}
|
||||
}
|
||||
|
||||
func testUserCreateOperation(
|
||||
t *testing.T, name string, password string, policies string) logicaltest.TestStep {
|
||||
return logicaltest.TestStep{
|
||||
Operation: logical.CreateOperation,
|
||||
Path: "users/" + name,
|
||||
Data: map[string]interface{}{
|
||||
"password": password,
|
||||
"policies": policies,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package userpass
|
|||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
|
@ -11,9 +12,9 @@ import (
|
|||
|
||||
func pathLogin(b *backend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: "login/" + framework.GenericNameRegex("name"),
|
||||
Pattern: "login/" + framework.GenericNameRegex("username"),
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"name": &framework.FieldSchema{
|
||||
"username": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Username of the user.",
|
||||
},
|
||||
|
@ -35,16 +36,20 @@ func pathLogin(b *backend) *framework.Path {
|
|||
|
||||
func (b *backend) pathLogin(
|
||||
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
username := strings.ToLower(d.Get("name").(string))
|
||||
username := strings.ToLower(d.Get("username").(string))
|
||||
|
||||
password := d.Get("password").(string)
|
||||
if password == "" {
|
||||
return nil, fmt.Errorf("missing password")
|
||||
}
|
||||
|
||||
// Get the user and validate auth
|
||||
user, err := b.User(req.Storage, username)
|
||||
user, err := b.user(req.Storage, username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user == nil {
|
||||
return logical.ErrorResponse("unknown username or password"), nil
|
||||
return logical.ErrorResponse("username does not exist"), nil
|
||||
}
|
||||
|
||||
// Check for a password match. Check for a hash collision for Vault 0.2+,
|
||||
|
@ -78,7 +83,7 @@ func (b *backend) pathLogin(
|
|||
func (b *backend) pathLoginRenew(
|
||||
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
// Get the user
|
||||
user, err := b.User(req.Storage, req.Auth.Metadata["username"])
|
||||
user, err := b.user(req.Storage, req.Auth.Metadata["username"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package userpass
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
func pathUserPassword(b *backend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: "users/" + framework.GenericNameRegex("username") + "/password$",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"username": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Username for this user.",
|
||||
},
|
||||
|
||||
"password": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Password for this user.",
|
||||
},
|
||||
},
|
||||
|
||||
ExistenceCheck: b.userPasswordExistenceCheck,
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.UpdateOperation: b.pathUserPasswordUpdate,
|
||||
},
|
||||
|
||||
HelpSynopsis: pathUserPasswordHelpSyn,
|
||||
HelpDescription: pathUserPasswordHelpDesc,
|
||||
}
|
||||
}
|
||||
|
||||
// By always returning true, this endpoint will be enforced to be invoked only upon UpdateOperation.
|
||||
// The existence of user will be checked in the operation handler.
|
||||
func (b *backend) userPasswordExistenceCheck(req *logical.Request, data *framework.FieldData) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (b *backend) pathUserPasswordUpdate(
|
||||
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
|
||||
username := d.Get("username").(string)
|
||||
|
||||
userEntry, err := b.user(req.Storage, username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if userEntry == nil {
|
||||
return nil, fmt.Errorf("username does not exist")
|
||||
}
|
||||
|
||||
err = b.updateUserPassword(req, d, userEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, b.setUser(req.Storage, username, userEntry)
|
||||
}
|
||||
|
||||
func (b *backend) updateUserPassword(req *logical.Request, d *framework.FieldData, userEntry *UserEntry) error {
|
||||
password := d.Get("password").(string)
|
||||
if password == "" {
|
||||
return fmt.Errorf("missing password")
|
||||
}
|
||||
// Generate a hash of the password
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userEntry.PasswordHash = hash
|
||||
return nil
|
||||
}
|
||||
|
||||
const pathUserPasswordHelpSyn = `
|
||||
Reset user's password.
|
||||
`
|
||||
|
||||
const pathUserPasswordHelpDesc = `
|
||||
This endpoint allows resetting the user's password.
|
||||
`
|
|
@ -0,0 +1,78 @@
|
|||
package userpass
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
)
|
||||
|
||||
func pathUserPolicies(b *backend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: "users/" + framework.GenericNameRegex("username") + "/policies$",
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"username": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Username for this user.",
|
||||
},
|
||||
"policies": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Comma-separated list of policies",
|
||||
},
|
||||
},
|
||||
|
||||
ExistenceCheck: b.userPoliciesExistenceCheck,
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.UpdateOperation: b.pathUserPoliciesUpdate,
|
||||
},
|
||||
|
||||
HelpSynopsis: pathUserPoliciesHelpSyn,
|
||||
HelpDescription: pathUserPoliciesHelpDesc,
|
||||
}
|
||||
}
|
||||
|
||||
// By always returning true, this endpoint will be enforced to be invoked only upon UpdateOperation.
|
||||
// The existence of user will be checked in the operation handler.
|
||||
func (b *backend) userPoliciesExistenceCheck(req *logical.Request, data *framework.FieldData) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (b *backend) pathUserPoliciesUpdate(
|
||||
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
|
||||
username := d.Get("username").(string)
|
||||
|
||||
userEntry, err := b.user(req.Storage, username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if userEntry == nil {
|
||||
return nil, fmt.Errorf("username does not exist")
|
||||
}
|
||||
|
||||
err = b.updateUserPolicies(req, d, userEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, b.setUser(req.Storage, username, userEntry)
|
||||
}
|
||||
|
||||
func (b *backend) updateUserPolicies(req *logical.Request, d *framework.FieldData, userEntry *UserEntry) error {
|
||||
policies := strings.Split(d.Get("policies").(string), ",")
|
||||
for i, p := range policies {
|
||||
policies[i] = strings.TrimSpace(p)
|
||||
}
|
||||
userEntry.Policies = policies
|
||||
return nil
|
||||
}
|
||||
|
||||
const pathUserPoliciesHelpSyn = `
|
||||
Update the policies associated with the username.
|
||||
`
|
||||
|
||||
const pathUserPoliciesHelpDesc = `
|
||||
This endpoint allows updating the policies associated with the username.
|
||||
`
|
|
@ -7,14 +7,13 @@ import (
|
|||
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/logical/framework"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func pathUsers(b *backend) *framework.Path {
|
||||
return &framework.Path{
|
||||
Pattern: "users/" + framework.GenericNameRegex("name"),
|
||||
Pattern: "users/" + framework.GenericNameRegex("username"),
|
||||
Fields: map[string]*framework.FieldSchema{
|
||||
"name": &framework.FieldSchema{
|
||||
"username": &framework.FieldSchema{
|
||||
Type: framework.TypeString,
|
||||
Description: "Username for this user.",
|
||||
},
|
||||
|
@ -43,16 +42,32 @@ func pathUsers(b *backend) *framework.Path {
|
|||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
logical.DeleteOperation: b.pathUserDelete,
|
||||
logical.ReadOperation: b.pathUserRead,
|
||||
logical.UpdateOperation: b.pathUserWrite,
|
||||
logical.UpdateOperation: b.pathUserWrite,
|
||||
logical.CreateOperation: b.pathUserWrite,
|
||||
},
|
||||
|
||||
ExistenceCheck: b.userExistenceCheck,
|
||||
|
||||
HelpSynopsis: pathUserHelpSyn,
|
||||
HelpDescription: pathUserHelpDesc,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backend) User(s logical.Storage, n string) (*UserEntry, error) {
|
||||
entry, err := s.Get("user/" + strings.ToLower(n))
|
||||
func (b *backend) userExistenceCheck(req *logical.Request, data *framework.FieldData) (bool, error) {
|
||||
userEntry, err := b.user(req.Storage, data.Get("username").(string))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return userEntry != nil, nil
|
||||
}
|
||||
|
||||
func (b *backend) user(s logical.Storage, username string) (*UserEntry, error) {
|
||||
if username == "" {
|
||||
return nil, fmt.Errorf("missing username")
|
||||
}
|
||||
|
||||
entry, err := s.Get("user/" + strings.ToLower(username))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -68,9 +83,18 @@ func (b *backend) User(s logical.Storage, n string) (*UserEntry, error) {
|
|||
return &result, nil
|
||||
}
|
||||
|
||||
func (b *backend) setUser(s logical.Storage, username string, userEntry *UserEntry) error {
|
||||
entry, err := logical.StorageEntryJSON("user/"+username, userEntry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.Put(entry)
|
||||
}
|
||||
|
||||
func (b *backend) pathUserDelete(
|
||||
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
err := req.Storage.Delete("user/" + strings.ToLower(d.Get("name").(string)))
|
||||
err := req.Storage.Delete("user/" + strings.ToLower(d.Get("username").(string)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -80,7 +104,7 @@ func (b *backend) pathUserDelete(
|
|||
|
||||
func (b *backend) pathUserRead(
|
||||
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
user, err := b.User(req.Storage, strings.ToLower(d.Get("name").(string)))
|
||||
user, err := b.user(req.Storage, strings.ToLower(d.Get("username").(string)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -95,43 +119,56 @@ func (b *backend) pathUserRead(
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (b *backend) pathUserWrite(
|
||||
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
name := strings.ToLower(d.Get("name").(string))
|
||||
password := d.Get("password").(string)
|
||||
policies := strings.Split(d.Get("policies").(string), ",")
|
||||
for i, p := range policies {
|
||||
policies[i] = strings.TrimSpace(p)
|
||||
}
|
||||
|
||||
// Generate a hash of the password
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
func (b *backend) userCreateUpdate(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
username := strings.ToLower(d.Get("username").(string))
|
||||
userEntry, err := b.user(req.Storage, username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Due to existence check, user will only be nil if it's a create operation
|
||||
if userEntry == nil {
|
||||
userEntry = &UserEntry{}
|
||||
}
|
||||
|
||||
ttlStr := d.Get("ttl").(string)
|
||||
maxTTLStr := d.Get("max_ttl").(string)
|
||||
ttl, maxTTL, err := b.SanitizeTTL(ttlStr, maxTTLStr)
|
||||
if _, ok := d.GetOk("password"); ok {
|
||||
err = b.updateUserPassword(req, d, userEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := d.GetOk("policies"); ok {
|
||||
err = b.updateUserPolicies(req, d, userEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
ttlStr := userEntry.TTL.String()
|
||||
if ttlStrRaw, ok := d.GetOk("ttl"); ok {
|
||||
ttlStr = ttlStrRaw.(string)
|
||||
}
|
||||
|
||||
maxTTLStr := userEntry.MaxTTL.String()
|
||||
if maxTTLStrRaw, ok := d.GetOk("max_ttl"); ok {
|
||||
maxTTLStr = maxTTLStrRaw.(string)
|
||||
}
|
||||
|
||||
userEntry.TTL, userEntry.MaxTTL, err = b.SanitizeTTL(ttlStr, maxTTLStr)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse(fmt.Sprintf("err: %s", err)), nil
|
||||
}
|
||||
|
||||
// Store it
|
||||
entry, err := logical.StorageEntryJSON("user/"+name, &UserEntry{
|
||||
PasswordHash: hash,
|
||||
Policies: policies,
|
||||
TTL: ttl,
|
||||
MaxTTL: maxTTL,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := req.Storage.Put(entry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, b.setUser(req.Storage, username, userEntry)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
func (b *backend) pathUserWrite(
|
||||
req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||
password := d.Get("password").(string)
|
||||
if req.Operation == logical.CreateOperation && password == "" {
|
||||
return nil, fmt.Errorf("missing password")
|
||||
}
|
||||
return b.userCreateUpdate(req, d)
|
||||
}
|
||||
|
||||
type UserEntry struct {
|
||||
|
|
|
@ -249,7 +249,7 @@ func (b *Backend) SanitizeTTL(ttlStr, maxTTLStr string) (ttl, maxTTL time.Durati
|
|||
return 0, 0, fmt.Errorf("\"max_ttl\" value must be less than allowed max lease TTL value '%s'", sysMaxTTL.String())
|
||||
}
|
||||
}
|
||||
if ttl > maxTTL {
|
||||
if ttl > maxTTL && maxTTL != 0 {
|
||||
ttl = maxTTL
|
||||
}
|
||||
return
|
||||
|
|
|
@ -92,3 +92,215 @@ $ vault write auth/userpass/users/mitchellh \
|
|||
The above creates a new user "mitchellh" with the password "foo" that
|
||||
will be associated with the "root" policy. This is the only configuration
|
||||
necessary.
|
||||
|
||||
## API
|
||||
|
||||
### /auth/userpass/users/[username]
|
||||
#### POST
|
||||
|
||||
<dl class="api">
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
Create a new user or update an existing user.
|
||||
This path honors the distinction between the `create` and `update` capabilities inside ACL policies.
|
||||
</dd>
|
||||
|
||||
<dt>Method</dt>
|
||||
<dd>POST</dd>
|
||||
|
||||
<dt>URL</dt>
|
||||
<dd>`/auth/userpass/users/<username>`</dd>
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">username</span>
|
||||
<span class="param-flags">required</span>
|
||||
Username for this user.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">password</span>
|
||||
<span class="param-flags">required</span>
|
||||
Password for this user.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">policies</span>
|
||||
<span class="param-flags">optional</span>
|
||||
Comma-separated list of policies.
|
||||
If set to empty string, only the `default` policy will be applicable to the user.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">ttl</span>
|
||||
<span class="param-flags">optional</span>
|
||||
The lease duration which decides login expiration.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">max_ttl</span>
|
||||
<span class="param-flags">optional</span>
|
||||
Maximum duration after which login should expire.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt>Returns</dt>
|
||||
<dd>`204` response code.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
### /auth/userpass/users/[username]/password
|
||||
#### POST
|
||||
<dl class="api">
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
Update the password for an existing user.
|
||||
</dd>
|
||||
|
||||
<dt>Method</dt>
|
||||
<dd>POST</dd>
|
||||
|
||||
<dt>URL</dt>
|
||||
<dd>`/auth/userpass/users/<username>/password`</dd>
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">username</span>
|
||||
<span class="param-flags">required</span>
|
||||
Username for this user.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">password</span>
|
||||
<span class="param-flags">required</span>
|
||||
Password for this user.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt>Returns</dt>
|
||||
<dd>`204` response code.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
### /auth/userpass/users/[username]/policies
|
||||
#### POST
|
||||
<dl class="api">
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
Update the policies associated with an existing user.
|
||||
</dd>
|
||||
|
||||
<dt>Method</dt>
|
||||
<dd>POST</dd>
|
||||
|
||||
<dt>URL</dt>
|
||||
<dd>`/auth/userpass/users/<username>/policies`</dd>
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">username</span>
|
||||
<span class="param-flags">required</span>
|
||||
Username for this user.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">policies</span>
|
||||
<span class="param-flags">optional</span>
|
||||
Comma-separated list of policies.
|
||||
If this is field is not supplied, the policies will be unchanged.
|
||||
If set to empty string, only the `default` policy will be applicable to the user.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt>Returns</dt>
|
||||
<dd>`204` response code.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
### /auth/userpass/login/[username]
|
||||
#### POST
|
||||
<dl class="api">
|
||||
<dt>Description</dt>
|
||||
<dd>
|
||||
Update the policies associated with an existing user.
|
||||
</dd>
|
||||
|
||||
<dt>Method</dt>
|
||||
<dd>POST</dd>
|
||||
|
||||
<dt>URL</dt>
|
||||
<dd>`/auth/userpass/users/<username>/policies`</dd>
|
||||
|
||||
<dt>Parameters</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">username</span>
|
||||
<span class="param-flags">required</span>
|
||||
Username for this user.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="param">password</span>
|
||||
<span class="param-flags">required</span>
|
||||
Password for this user.
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt>Returns</dt>
|
||||
<dd>
|
||||
|
||||
```javascript
|
||||
{
|
||||
"lease_id": "",
|
||||
"renewable": false,
|
||||
"lease_duration": 0,
|
||||
"data": null,
|
||||
"warnings": null,
|
||||
"auth": {
|
||||
"client_token": "64d2a8f2-2a2f-5688-102b-e6088b76e344",
|
||||
"accessor": "18bb8f89-826a-56ee-c65b-1736dc5ea27d",
|
||||
"policies": ["default"],
|
||||
"metadata": {
|
||||
"username": "vishal"
|
||||
},
|
||||
"lease_duration": 7200,
|
||||
"renewable": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
|
Loading…
Reference in New Issue