...

Source file src/go.etcd.io/etcd/client/v2/auth_user.go

Documentation: go.etcd.io/etcd/client/v2

     1  // 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  
    15  package client
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"encoding/json"
    21  	"net/http"
    22  	"net/url"
    23  	"path"
    24  )
    25  
    26  var (
    27  	defaultV2AuthPrefix = "/v2/auth"
    28  )
    29  
    30  type User struct {
    31  	User     string   `json:"user"`
    32  	Password string   `json:"password,omitempty"`
    33  	Roles    []string `json:"roles"`
    34  	Grant    []string `json:"grant,omitempty"`
    35  	Revoke   []string `json:"revoke,omitempty"`
    36  }
    37  
    38  // userListEntry is the user representation given by the server for ListUsers
    39  type userListEntry struct {
    40  	User  string `json:"user"`
    41  	Roles []Role `json:"roles"`
    42  }
    43  
    44  type UserRoles struct {
    45  	User  string `json:"user"`
    46  	Roles []Role `json:"roles"`
    47  }
    48  
    49  func v2AuthURL(ep url.URL, action string, name string) *url.URL {
    50  	if name != "" {
    51  		ep.Path = path.Join(ep.Path, defaultV2AuthPrefix, action, name)
    52  		return &ep
    53  	}
    54  	ep.Path = path.Join(ep.Path, defaultV2AuthPrefix, action)
    55  	return &ep
    56  }
    57  
    58  // NewAuthAPI constructs a new AuthAPI that uses HTTP to
    59  // interact with etcd's general auth features.
    60  func NewAuthAPI(c Client) AuthAPI {
    61  	return &httpAuthAPI{
    62  		client: c,
    63  	}
    64  }
    65  
    66  type AuthAPI interface {
    67  	// Enable auth.
    68  	Enable(ctx context.Context) error
    69  
    70  	// Disable auth.
    71  	Disable(ctx context.Context) error
    72  }
    73  
    74  type httpAuthAPI struct {
    75  	client httpClient
    76  }
    77  
    78  func (s *httpAuthAPI) Enable(ctx context.Context) error {
    79  	return s.enableDisable(ctx, &authAPIAction{"PUT"})
    80  }
    81  
    82  func (s *httpAuthAPI) Disable(ctx context.Context) error {
    83  	return s.enableDisable(ctx, &authAPIAction{"DELETE"})
    84  }
    85  
    86  func (s *httpAuthAPI) enableDisable(ctx context.Context, req httpAction) error {
    87  	resp, body, err := s.client.Do(ctx, req)
    88  	if err != nil {
    89  		return err
    90  	}
    91  	if err = assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
    92  		var sec authError
    93  		err = json.Unmarshal(body, &sec)
    94  		if err != nil {
    95  			return err
    96  		}
    97  		return sec
    98  	}
    99  	return nil
   100  }
   101  
   102  type authAPIAction struct {
   103  	verb string
   104  }
   105  
   106  func (l *authAPIAction) HTTPRequest(ep url.URL) *http.Request {
   107  	u := v2AuthURL(ep, "enable", "")
   108  	req, _ := http.NewRequest(l.verb, u.String(), nil)
   109  	return req
   110  }
   111  
   112  type authError struct {
   113  	Message string `json:"message"`
   114  	Code    int    `json:"-"`
   115  }
   116  
   117  func (e authError) Error() string {
   118  	return e.Message
   119  }
   120  
   121  // NewAuthUserAPI constructs a new AuthUserAPI that uses HTTP to
   122  // interact with etcd's user creation and modification features.
   123  func NewAuthUserAPI(c Client) AuthUserAPI {
   124  	return &httpAuthUserAPI{
   125  		client: c,
   126  	}
   127  }
   128  
   129  type AuthUserAPI interface {
   130  	// AddUser adds a user.
   131  	AddUser(ctx context.Context, username string, password string) error
   132  
   133  	// RemoveUser removes a user.
   134  	RemoveUser(ctx context.Context, username string) error
   135  
   136  	// GetUser retrieves user details.
   137  	GetUser(ctx context.Context, username string) (*User, error)
   138  
   139  	// GrantUser grants a user some permission roles.
   140  	GrantUser(ctx context.Context, username string, roles []string) (*User, error)
   141  
   142  	// RevokeUser revokes some permission roles from a user.
   143  	RevokeUser(ctx context.Context, username string, roles []string) (*User, error)
   144  
   145  	// ChangePassword changes the user's password.
   146  	ChangePassword(ctx context.Context, username string, password string) (*User, error)
   147  
   148  	// ListUsers lists the users.
   149  	ListUsers(ctx context.Context) ([]string, error)
   150  }
   151  
   152  type httpAuthUserAPI struct {
   153  	client httpClient
   154  }
   155  
   156  type authUserAPIAction struct {
   157  	verb     string
   158  	username string
   159  	user     *User
   160  }
   161  
   162  type authUserAPIList struct{}
   163  
   164  func (list *authUserAPIList) HTTPRequest(ep url.URL) *http.Request {
   165  	u := v2AuthURL(ep, "users", "")
   166  	req, _ := http.NewRequest("GET", u.String(), nil)
   167  	req.Header.Set("Content-Type", "application/json")
   168  	return req
   169  }
   170  
   171  func (l *authUserAPIAction) HTTPRequest(ep url.URL) *http.Request {
   172  	u := v2AuthURL(ep, "users", l.username)
   173  	if l.user == nil {
   174  		req, _ := http.NewRequest(l.verb, u.String(), nil)
   175  		return req
   176  	}
   177  	b, err := json.Marshal(l.user)
   178  	if err != nil {
   179  		panic(err)
   180  	}
   181  	body := bytes.NewReader(b)
   182  	req, _ := http.NewRequest(l.verb, u.String(), body)
   183  	req.Header.Set("Content-Type", "application/json")
   184  	return req
   185  }
   186  
   187  func (u *httpAuthUserAPI) ListUsers(ctx context.Context) ([]string, error) {
   188  	resp, body, err := u.client.Do(ctx, &authUserAPIList{})
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  	if err = assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
   193  		var sec authError
   194  		err = json.Unmarshal(body, &sec)
   195  		if err != nil {
   196  			return nil, err
   197  		}
   198  		return nil, sec
   199  	}
   200  
   201  	var userList struct {
   202  		Users []userListEntry `json:"users"`
   203  	}
   204  
   205  	if err = json.Unmarshal(body, &userList); err != nil {
   206  		return nil, err
   207  	}
   208  
   209  	ret := make([]string, 0, len(userList.Users))
   210  	for _, u := range userList.Users {
   211  		ret = append(ret, u.User)
   212  	}
   213  	return ret, nil
   214  }
   215  
   216  func (u *httpAuthUserAPI) AddUser(ctx context.Context, username string, password string) error {
   217  	user := &User{
   218  		User:     username,
   219  		Password: password,
   220  	}
   221  	return u.addRemoveUser(ctx, &authUserAPIAction{
   222  		verb:     "PUT",
   223  		username: username,
   224  		user:     user,
   225  	})
   226  }
   227  
   228  func (u *httpAuthUserAPI) RemoveUser(ctx context.Context, username string) error {
   229  	return u.addRemoveUser(ctx, &authUserAPIAction{
   230  		verb:     "DELETE",
   231  		username: username,
   232  	})
   233  }
   234  
   235  func (u *httpAuthUserAPI) addRemoveUser(ctx context.Context, req *authUserAPIAction) error {
   236  	resp, body, err := u.client.Do(ctx, req)
   237  	if err != nil {
   238  		return err
   239  	}
   240  	if err = assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
   241  		var sec authError
   242  		err = json.Unmarshal(body, &sec)
   243  		if err != nil {
   244  			return err
   245  		}
   246  		return sec
   247  	}
   248  	return nil
   249  }
   250  
   251  func (u *httpAuthUserAPI) GetUser(ctx context.Context, username string) (*User, error) {
   252  	return u.modUser(ctx, &authUserAPIAction{
   253  		verb:     "GET",
   254  		username: username,
   255  	})
   256  }
   257  
   258  func (u *httpAuthUserAPI) GrantUser(ctx context.Context, username string, roles []string) (*User, error) {
   259  	user := &User{
   260  		User:  username,
   261  		Grant: roles,
   262  	}
   263  	return u.modUser(ctx, &authUserAPIAction{
   264  		verb:     "PUT",
   265  		username: username,
   266  		user:     user,
   267  	})
   268  }
   269  
   270  func (u *httpAuthUserAPI) RevokeUser(ctx context.Context, username string, roles []string) (*User, error) {
   271  	user := &User{
   272  		User:   username,
   273  		Revoke: roles,
   274  	}
   275  	return u.modUser(ctx, &authUserAPIAction{
   276  		verb:     "PUT",
   277  		username: username,
   278  		user:     user,
   279  	})
   280  }
   281  
   282  func (u *httpAuthUserAPI) ChangePassword(ctx context.Context, username string, password string) (*User, error) {
   283  	user := &User{
   284  		User:     username,
   285  		Password: password,
   286  	}
   287  	return u.modUser(ctx, &authUserAPIAction{
   288  		verb:     "PUT",
   289  		username: username,
   290  		user:     user,
   291  	})
   292  }
   293  
   294  func (u *httpAuthUserAPI) modUser(ctx context.Context, req *authUserAPIAction) (*User, error) {
   295  	resp, body, err := u.client.Do(ctx, req)
   296  	if err != nil {
   297  		return nil, err
   298  	}
   299  	if err = assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
   300  		var sec authError
   301  		err = json.Unmarshal(body, &sec)
   302  		if err != nil {
   303  			return nil, err
   304  		}
   305  		return nil, sec
   306  	}
   307  	var user User
   308  	if err = json.Unmarshal(body, &user); err != nil {
   309  		var userR UserRoles
   310  		if urerr := json.Unmarshal(body, &userR); urerr != nil {
   311  			return nil, err
   312  		}
   313  		user.User = userR.User
   314  		for _, r := range userR.Roles {
   315  			user.Roles = append(user.Roles, r.Role)
   316  		}
   317  	}
   318  	return &user, nil
   319  }
   320  

View as plain text