| // Copyright 2015 The etcd Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package client |
| |
| import ( |
| "bytes" |
| "context" |
| "encoding/json" |
| "net/http" |
| "net/url" |
| ) |
| |
| type Role struct { |
| Role string `json:"role"` |
| Permissions Permissions `json:"permissions"` |
| Grant *Permissions `json:"grant,omitempty"` |
| Revoke *Permissions `json:"revoke,omitempty"` |
| } |
| |
| type Permissions struct { |
| KV rwPermission `json:"kv"` |
| } |
| |
| type rwPermission struct { |
| Read []string `json:"read"` |
| Write []string `json:"write"` |
| } |
| |
| type PermissionType int |
| |
| const ( |
| ReadPermission PermissionType = iota |
| WritePermission |
| ReadWritePermission |
| ) |
| |
| // NewAuthRoleAPI constructs a new AuthRoleAPI that uses HTTP to |
| // interact with etcd's role creation and modification features. |
| func NewAuthRoleAPI(c Client) AuthRoleAPI { |
| return &httpAuthRoleAPI{ |
| client: c, |
| } |
| } |
| |
| type AuthRoleAPI interface { |
| // AddRole adds a role. |
| AddRole(ctx context.Context, role string) error |
| |
| // RemoveRole removes a role. |
| RemoveRole(ctx context.Context, role string) error |
| |
| // GetRole retrieves role details. |
| GetRole(ctx context.Context, role string) (*Role, error) |
| |
| // GrantRoleKV grants a role some permission prefixes for the KV store. |
| GrantRoleKV(ctx context.Context, role string, prefixes []string, permType PermissionType) (*Role, error) |
| |
| // RevokeRoleKV revokes some permission prefixes for a role on the KV store. |
| RevokeRoleKV(ctx context.Context, role string, prefixes []string, permType PermissionType) (*Role, error) |
| |
| // ListRoles lists roles. |
| ListRoles(ctx context.Context) ([]string, error) |
| } |
| |
| type httpAuthRoleAPI struct { |
| client httpClient |
| } |
| |
| type authRoleAPIAction struct { |
| verb string |
| name string |
| role *Role |
| } |
| |
| type authRoleAPIList struct{} |
| |
| func (list *authRoleAPIList) HTTPRequest(ep url.URL) *http.Request { |
| u := v2AuthURL(ep, "roles", "") |
| req, _ := http.NewRequest("GET", u.String(), nil) |
| req.Header.Set("Content-Type", "application/json") |
| return req |
| } |
| |
| func (l *authRoleAPIAction) HTTPRequest(ep url.URL) *http.Request { |
| u := v2AuthURL(ep, "roles", l.name) |
| if l.role == nil { |
| req, _ := http.NewRequest(l.verb, u.String(), nil) |
| return req |
| } |
| b, err := json.Marshal(l.role) |
| if err != nil { |
| panic(err) |
| } |
| body := bytes.NewReader(b) |
| req, _ := http.NewRequest(l.verb, u.String(), body) |
| req.Header.Set("Content-Type", "application/json") |
| return req |
| } |
| |
| func (r *httpAuthRoleAPI) ListRoles(ctx context.Context) ([]string, error) { |
| resp, body, err := r.client.Do(ctx, &authRoleAPIList{}) |
| if err != nil { |
| return nil, err |
| } |
| if err = assertStatusCode(resp.StatusCode, http.StatusOK); err != nil { |
| return nil, err |
| } |
| var roleList struct { |
| Roles []Role `json:"roles"` |
| } |
| if err = json.Unmarshal(body, &roleList); err != nil { |
| return nil, err |
| } |
| ret := make([]string, 0, len(roleList.Roles)) |
| for _, r := range roleList.Roles { |
| ret = append(ret, r.Role) |
| } |
| return ret, nil |
| } |
| |
| func (r *httpAuthRoleAPI) AddRole(ctx context.Context, rolename string) error { |
| role := &Role{ |
| Role: rolename, |
| } |
| return r.addRemoveRole(ctx, &authRoleAPIAction{ |
| verb: "PUT", |
| name: rolename, |
| role: role, |
| }) |
| } |
| |
| func (r *httpAuthRoleAPI) RemoveRole(ctx context.Context, rolename string) error { |
| return r.addRemoveRole(ctx, &authRoleAPIAction{ |
| verb: "DELETE", |
| name: rolename, |
| }) |
| } |
| |
| func (r *httpAuthRoleAPI) addRemoveRole(ctx context.Context, req *authRoleAPIAction) error { |
| resp, body, err := r.client.Do(ctx, req) |
| if err != nil { |
| return err |
| } |
| if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil { |
| var sec authError |
| err := json.Unmarshal(body, &sec) |
| if err != nil { |
| return err |
| } |
| return sec |
| } |
| return nil |
| } |
| |
| func (r *httpAuthRoleAPI) GetRole(ctx context.Context, rolename string) (*Role, error) { |
| return r.modRole(ctx, &authRoleAPIAction{ |
| verb: "GET", |
| name: rolename, |
| }) |
| } |
| |
| func buildRWPermission(prefixes []string, permType PermissionType) rwPermission { |
| var out rwPermission |
| switch permType { |
| case ReadPermission: |
| out.Read = prefixes |
| case WritePermission: |
| out.Write = prefixes |
| case ReadWritePermission: |
| out.Read = prefixes |
| out.Write = prefixes |
| } |
| return out |
| } |
| |
| func (r *httpAuthRoleAPI) GrantRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) { |
| rwp := buildRWPermission(prefixes, permType) |
| role := &Role{ |
| Role: rolename, |
| Grant: &Permissions{ |
| KV: rwp, |
| }, |
| } |
| return r.modRole(ctx, &authRoleAPIAction{ |
| verb: "PUT", |
| name: rolename, |
| role: role, |
| }) |
| } |
| |
| func (r *httpAuthRoleAPI) RevokeRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) { |
| rwp := buildRWPermission(prefixes, permType) |
| role := &Role{ |
| Role: rolename, |
| Revoke: &Permissions{ |
| KV: rwp, |
| }, |
| } |
| return r.modRole(ctx, &authRoleAPIAction{ |
| verb: "PUT", |
| name: rolename, |
| role: role, |
| }) |
| } |
| |
| func (r *httpAuthRoleAPI) modRole(ctx context.Context, req *authRoleAPIAction) (*Role, error) { |
| resp, body, err := r.client.Do(ctx, req) |
| if err != nil { |
| return nil, err |
| } |
| if err = assertStatusCode(resp.StatusCode, http.StatusOK); err != nil { |
| var sec authError |
| err = json.Unmarshal(body, &sec) |
| if err != nil { |
| return nil, err |
| } |
| return nil, sec |
| } |
| var role Role |
| if err = json.Unmarshal(body, &role); err != nil { |
| return nil, err |
| } |
| return &role, nil |
| } |