1package ldap
2
3import (
4	"strings"
5
6	"github.com/hashicorp/vault/logical"
7	"github.com/hashicorp/vault/logical/framework"
8)
9
10func pathUsersList(b *backend) *framework.Path {
11	return &framework.Path{
12		Pattern: "users/?$",
13
14		Callbacks: map[logical.Operation]framework.OperationFunc{
15			logical.ListOperation: b.pathUserList,
16		},
17
18		HelpSynopsis:    pathUserHelpSyn,
19		HelpDescription: pathUserHelpDesc,
20	}
21}
22
23func pathUsers(b *backend) *framework.Path {
24	return &framework.Path{
25		Pattern: `users/(?P<name>.+)`,
26		Fields: map[string]*framework.FieldSchema{
27			"name": &framework.FieldSchema{
28				Type:        framework.TypeString,
29				Description: "Name of the LDAP user.",
30			},
31
32			"groups": &framework.FieldSchema{
33				Type:        framework.TypeString,
34				Description: "Comma-separated list of additional groups associated with the user.",
35			},
36		},
37
38		Callbacks: map[logical.Operation]framework.OperationFunc{
39			logical.DeleteOperation: b.pathUserDelete,
40			logical.ReadOperation:   b.pathUserRead,
41			logical.UpdateOperation: b.pathUserWrite,
42		},
43
44		HelpSynopsis:    pathUserHelpSyn,
45		HelpDescription: pathUserHelpDesc,
46	}
47}
48
49func (b *backend) User(s logical.Storage, n string) (*UserEntry, error) {
50	entry, err := s.Get("user/" + n)
51	if err != nil {
52		return nil, err
53	}
54	if entry == nil {
55		return nil, nil
56	}
57
58	var result UserEntry
59	if err := entry.DecodeJSON(&result); err != nil {
60		return nil, err
61	}
62
63	return &result, nil
64}
65
66func (b *backend) pathUserDelete(
67	req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
68	err := req.Storage.Delete("user/" + d.Get("name").(string))
69	if err != nil {
70		return nil, err
71	}
72
73	return nil, nil
74}
75
76func (b *backend) pathUserRead(
77	req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
78	user, err := b.User(req.Storage, d.Get("name").(string))
79	if err != nil {
80		return nil, err
81	}
82	if user == nil {
83		return nil, nil
84	}
85
86	return &logical.Response{
87		Data: map[string]interface{}{
88			"groups": strings.Join(user.Groups, ","),
89		},
90	}, nil
91}
92
93func (b *backend) pathUserWrite(
94	req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
95	name := d.Get("name").(string)
96	groups := strings.Split(d.Get("groups").(string), ",")
97	for i, g := range groups {
98		groups[i] = strings.TrimSpace(g)
99	}
100
101	// Store it
102	entry, err := logical.StorageEntryJSON("user/"+name, &UserEntry{
103		Groups: groups,
104	})
105	if err != nil {
106		return nil, err
107	}
108	if err := req.Storage.Put(entry); err != nil {
109		return nil, err
110	}
111
112	return nil, nil
113}
114
115func (b *backend) pathUserList(
116	req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
117	users, err := req.Storage.List("user/")
118	if err != nil {
119		return nil, err
120	}
121	return logical.ListResponse(users), nil
122}
123
124type UserEntry struct {
125	Groups []string
126}
127
128const pathUserHelpSyn = `
129Manage additional groups for users allowed to authenticate.
130`
131
132const pathUserHelpDesc = `
133This endpoint allows you to create, read, update, and delete configuration
134for LDAP users that are allowed to authenticate, in particular associating
135additional groups to them.
136
137Deleting a user will not revoke their auth. To do this, do a revoke on "login/<username>" for
138the usernames you want revoked.
139`
140