blob: b6ba7e150dc6142ad3813f284cb62d860f719d89 [file] [log] [blame]
khenaidoo59ce9dd2019-11-11 13:05:32 -05001// Copyright 2015 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package client
16
17import (
18 "bytes"
19 "context"
20 "encoding/json"
21 "net/http"
22 "net/url"
23)
24
25type Role struct {
26 Role string `json:"role"`
27 Permissions Permissions `json:"permissions"`
28 Grant *Permissions `json:"grant,omitempty"`
29 Revoke *Permissions `json:"revoke,omitempty"`
30}
31
32type Permissions struct {
33 KV rwPermission `json:"kv"`
34}
35
36type rwPermission struct {
37 Read []string `json:"read"`
38 Write []string `json:"write"`
39}
40
41type PermissionType int
42
43const (
44 ReadPermission PermissionType = iota
45 WritePermission
46 ReadWritePermission
47)
48
49// NewAuthRoleAPI constructs a new AuthRoleAPI that uses HTTP to
50// interact with etcd's role creation and modification features.
51func NewAuthRoleAPI(c Client) AuthRoleAPI {
52 return &httpAuthRoleAPI{
53 client: c,
54 }
55}
56
57type AuthRoleAPI interface {
58 // AddRole adds a role.
59 AddRole(ctx context.Context, role string) error
60
61 // RemoveRole removes a role.
62 RemoveRole(ctx context.Context, role string) error
63
64 // GetRole retrieves role details.
65 GetRole(ctx context.Context, role string) (*Role, error)
66
67 // GrantRoleKV grants a role some permission prefixes for the KV store.
68 GrantRoleKV(ctx context.Context, role string, prefixes []string, permType PermissionType) (*Role, error)
69
70 // RevokeRoleKV revokes some permission prefixes for a role on the KV store.
71 RevokeRoleKV(ctx context.Context, role string, prefixes []string, permType PermissionType) (*Role, error)
72
73 // ListRoles lists roles.
74 ListRoles(ctx context.Context) ([]string, error)
75}
76
77type httpAuthRoleAPI struct {
78 client httpClient
79}
80
81type authRoleAPIAction struct {
82 verb string
83 name string
84 role *Role
85}
86
87type authRoleAPIList struct{}
88
89func (list *authRoleAPIList) HTTPRequest(ep url.URL) *http.Request {
90 u := v2AuthURL(ep, "roles", "")
91 req, _ := http.NewRequest("GET", u.String(), nil)
92 req.Header.Set("Content-Type", "application/json")
93 return req
94}
95
96func (l *authRoleAPIAction) HTTPRequest(ep url.URL) *http.Request {
97 u := v2AuthURL(ep, "roles", l.name)
98 if l.role == nil {
99 req, _ := http.NewRequest(l.verb, u.String(), nil)
100 return req
101 }
102 b, err := json.Marshal(l.role)
103 if err != nil {
104 panic(err)
105 }
106 body := bytes.NewReader(b)
107 req, _ := http.NewRequest(l.verb, u.String(), body)
108 req.Header.Set("Content-Type", "application/json")
109 return req
110}
111
112func (r *httpAuthRoleAPI) ListRoles(ctx context.Context) ([]string, error) {
113 resp, body, err := r.client.Do(ctx, &authRoleAPIList{})
114 if err != nil {
115 return nil, err
116 }
117 if err = assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
118 return nil, err
119 }
120 var roleList struct {
121 Roles []Role `json:"roles"`
122 }
123 if err = json.Unmarshal(body, &roleList); err != nil {
124 return nil, err
125 }
126 ret := make([]string, 0, len(roleList.Roles))
127 for _, r := range roleList.Roles {
128 ret = append(ret, r.Role)
129 }
130 return ret, nil
131}
132
133func (r *httpAuthRoleAPI) AddRole(ctx context.Context, rolename string) error {
134 role := &Role{
135 Role: rolename,
136 }
137 return r.addRemoveRole(ctx, &authRoleAPIAction{
138 verb: "PUT",
139 name: rolename,
140 role: role,
141 })
142}
143
144func (r *httpAuthRoleAPI) RemoveRole(ctx context.Context, rolename string) error {
145 return r.addRemoveRole(ctx, &authRoleAPIAction{
146 verb: "DELETE",
147 name: rolename,
148 })
149}
150
151func (r *httpAuthRoleAPI) addRemoveRole(ctx context.Context, req *authRoleAPIAction) error {
152 resp, body, err := r.client.Do(ctx, req)
153 if err != nil {
154 return err
155 }
156 if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
157 var sec authError
158 err := json.Unmarshal(body, &sec)
159 if err != nil {
160 return err
161 }
162 return sec
163 }
164 return nil
165}
166
167func (r *httpAuthRoleAPI) GetRole(ctx context.Context, rolename string) (*Role, error) {
168 return r.modRole(ctx, &authRoleAPIAction{
169 verb: "GET",
170 name: rolename,
171 })
172}
173
174func buildRWPermission(prefixes []string, permType PermissionType) rwPermission {
175 var out rwPermission
176 switch permType {
177 case ReadPermission:
178 out.Read = prefixes
179 case WritePermission:
180 out.Write = prefixes
181 case ReadWritePermission:
182 out.Read = prefixes
183 out.Write = prefixes
184 }
185 return out
186}
187
188func (r *httpAuthRoleAPI) GrantRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) {
189 rwp := buildRWPermission(prefixes, permType)
190 role := &Role{
191 Role: rolename,
192 Grant: &Permissions{
193 KV: rwp,
194 },
195 }
196 return r.modRole(ctx, &authRoleAPIAction{
197 verb: "PUT",
198 name: rolename,
199 role: role,
200 })
201}
202
203func (r *httpAuthRoleAPI) RevokeRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) {
204 rwp := buildRWPermission(prefixes, permType)
205 role := &Role{
206 Role: rolename,
207 Revoke: &Permissions{
208 KV: rwp,
209 },
210 }
211 return r.modRole(ctx, &authRoleAPIAction{
212 verb: "PUT",
213 name: rolename,
214 role: role,
215 })
216}
217
218func (r *httpAuthRoleAPI) modRole(ctx context.Context, req *authRoleAPIAction) (*Role, error) {
219 resp, body, err := r.client.Do(ctx, req)
220 if err != nil {
221 return nil, err
222 }
223 if err = assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
224 var sec authError
225 err = json.Unmarshal(body, &sec)
226 if err != nil {
227 return nil, err
228 }
229 return nil, sec
230 }
231 var role Role
232 if err = json.Unmarshal(body, &role); err != nil {
233 return nil, err
234 }
235 return &role, nil
236}