1package rabbitmq
2
3import (
4	"context"
5	"fmt"
6
7	"github.com/fatih/structs"
8	"github.com/hashicorp/vault/helper/jsonutil"
9	"github.com/hashicorp/vault/logical"
10	"github.com/hashicorp/vault/logical/framework"
11)
12
13func pathListRoles(b *backend) *framework.Path {
14	return &framework.Path{
15		Pattern: "roles/?$",
16		Callbacks: map[logical.Operation]framework.OperationFunc{
17			logical.ListOperation: b.pathRoleList,
18		},
19		HelpSynopsis:    pathRoleHelpSyn,
20		HelpDescription: pathRoleHelpDesc,
21	}
22}
23
24func pathRoles(b *backend) *framework.Path {
25	return &framework.Path{
26		Pattern: "roles/" + framework.GenericNameRegex("name"),
27		Fields: map[string]*framework.FieldSchema{
28			"name": &framework.FieldSchema{
29				Type:        framework.TypeString,
30				Description: "Name of the role.",
31			},
32			"tags": &framework.FieldSchema{
33				Type:        framework.TypeString,
34				Description: "Comma-separated list of tags for this role.",
35			},
36			"vhosts": &framework.FieldSchema{
37				Type:        framework.TypeString,
38				Description: "A map of virtual hosts to permissions.",
39			},
40		},
41		Callbacks: map[logical.Operation]framework.OperationFunc{
42			logical.ReadOperation:   b.pathRoleRead,
43			logical.UpdateOperation: b.pathRoleUpdate,
44			logical.DeleteOperation: b.pathRoleDelete,
45		},
46		HelpSynopsis:    pathRoleHelpSyn,
47		HelpDescription: pathRoleHelpDesc,
48	}
49}
50
51// Reads the role configuration from the storage
52func (b *backend) Role(ctx context.Context, s logical.Storage, n string) (*roleEntry, error) {
53	entry, err := s.Get(ctx, "role/"+n)
54	if err != nil {
55		return nil, err
56	}
57	if entry == nil {
58		return nil, nil
59	}
60
61	var result roleEntry
62	if err := entry.DecodeJSON(&result); err != nil {
63		return nil, err
64	}
65
66	return &result, nil
67}
68
69// Deletes an existing role
70func (b *backend) pathRoleDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
71	name := d.Get("name").(string)
72	if name == "" {
73		return logical.ErrorResponse("missing name"), nil
74	}
75
76	return nil, req.Storage.Delete(ctx, "role/"+name)
77}
78
79// Reads an existing role
80func (b *backend) pathRoleRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
81	name := d.Get("name").(string)
82	if name == "" {
83		return logical.ErrorResponse("missing name"), nil
84	}
85
86	role, err := b.Role(ctx, req.Storage, name)
87	if err != nil {
88		return nil, err
89	}
90	if role == nil {
91		return nil, nil
92	}
93
94	return &logical.Response{
95		Data: structs.New(role).Map(),
96	}, nil
97}
98
99// Lists all the roles registered with the backend
100func (b *backend) pathRoleList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
101	roles, err := req.Storage.List(ctx, "role/")
102	if err != nil {
103		return nil, err
104	}
105
106	return logical.ListResponse(roles), nil
107}
108
109// Registers a new role with the backend
110func (b *backend) pathRoleUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
111	name := d.Get("name").(string)
112	if name == "" {
113		return logical.ErrorResponse("missing name"), nil
114	}
115
116	tags := d.Get("tags").(string)
117	rawVHosts := d.Get("vhosts").(string)
118
119	if tags == "" && rawVHosts == "" {
120		return logical.ErrorResponse("both tags and vhosts not specified"), nil
121	}
122
123	var vhosts map[string]vhostPermission
124	if len(rawVHosts) > 0 {
125		if err := jsonutil.DecodeJSON([]byte(rawVHosts), &vhosts); err != nil {
126			return logical.ErrorResponse(fmt.Sprintf("failed to unmarshal vhosts: %s", err)), nil
127		}
128	}
129
130	// Store it
131	entry, err := logical.StorageEntryJSON("role/"+name, &roleEntry{
132		Tags:   tags,
133		VHosts: vhosts,
134	})
135	if err != nil {
136		return nil, err
137	}
138	if err := req.Storage.Put(ctx, entry); err != nil {
139		return nil, err
140	}
141
142	return nil, nil
143}
144
145// Role that defines the capabilities of the credentials issued against it
146type roleEntry struct {
147	Tags   string                     `json:"tags" structs:"tags" mapstructure:"tags"`
148	VHosts map[string]vhostPermission `json:"vhosts" structs:"vhosts" mapstructure:"vhosts"`
149}
150
151// Structure representing the permissions of a vhost
152type vhostPermission struct {
153	Configure string `json:"configure" structs:"configure" mapstructure:"configure"`
154	Write     string `json:"write" structs:"write" mapstructure:"write"`
155	Read      string `json:"read" structs:"read" mapstructure:"read"`
156}
157
158const pathRoleHelpSyn = `
159Manage the roles that can be created with this backend.
160`
161
162const pathRoleHelpDesc = `
163This path lets you manage the roles that can be created with this backend.
164
165The "tags" parameter customizes the tags used to create the role.
166This is a comma separated list of strings. The "vhosts" parameter customizes
167the virtual hosts that this user will be associated with. This is a JSON object
168passed as a string in the form:
169{
170	"vhostOne": {
171		"configure": ".*",
172		"write": ".*",
173		"read": ".*"
174	},
175	"vhostTwo": {
176		"configure": ".*",
177		"write": ".*",
178		"read": ".*"
179	}
180}
181`
182