github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/api/usermanager/client.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package usermanager
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	"gopkg.in/macaroon.v1"
    11  
    12  	"github.com/juju/errors"
    13  	"github.com/juju/loggo"
    14  	"github.com/juju/names"
    15  
    16  	"github.com/juju/juju/api/base"
    17  	"github.com/juju/juju/api/modelmanager"
    18  	"github.com/juju/juju/apiserver/params"
    19  )
    20  
    21  var logger = loggo.GetLogger("juju.api.usermanager")
    22  
    23  // Client provides methods that the Juju client command uses to interact
    24  // with users stored in the Juju Server.
    25  type Client struct {
    26  	base.ClientFacade
    27  	facade base.FacadeCaller
    28  }
    29  
    30  // NewClient creates a new `Client` based on an existing authenticated API
    31  // connection.
    32  func NewClient(st base.APICallCloser) *Client {
    33  	frontend, backend := base.NewClientFacade(st, "UserManager")
    34  	return &Client{ClientFacade: frontend, facade: backend}
    35  }
    36  
    37  // AddUser creates a new local user in the controller, sharing with that user any specified models.
    38  func (c *Client) AddUser(
    39  	username, displayName, password, access string, modelUUIDs ...string,
    40  ) (_ names.UserTag, secretKey []byte, _ error) {
    41  	if !names.IsValidUser(username) {
    42  		return names.UserTag{}, nil, fmt.Errorf("invalid user name %q", username)
    43  	}
    44  	modelTags := make([]string, len(modelUUIDs))
    45  	for i, uuid := range modelUUIDs {
    46  		modelTags[i] = names.NewModelTag(uuid).String()
    47  	}
    48  
    49  	var accessPermission params.ModelAccessPermission
    50  	var err error
    51  	if len(modelTags) > 0 {
    52  		accessPermission, err = modelmanager.ParseModelAccess(access)
    53  		if err != nil {
    54  			return names.UserTag{}, nil, errors.Trace(err)
    55  		}
    56  	}
    57  
    58  	userArgs := params.AddUsers{
    59  		Users: []params.AddUser{{
    60  			Username:        username,
    61  			DisplayName:     displayName,
    62  			Password:        password,
    63  			SharedModelTags: modelTags,
    64  			ModelAccess:     accessPermission}},
    65  	}
    66  	var results params.AddUserResults
    67  	err = c.facade.FacadeCall("AddUser", userArgs, &results)
    68  	if err != nil {
    69  		return names.UserTag{}, nil, errors.Trace(err)
    70  	}
    71  	if count := len(results.Results); count != 1 {
    72  		logger.Errorf("expected 1 result, got %#v", results)
    73  		return names.UserTag{}, nil, errors.Errorf("expected 1 result, got %d", count)
    74  	}
    75  	result := results.Results[0]
    76  	if result.Error != nil {
    77  		return names.UserTag{}, nil, errors.Trace(result.Error)
    78  	}
    79  	tag, err := names.ParseUserTag(result.Tag)
    80  	if err != nil {
    81  		return names.UserTag{}, nil, errors.Trace(err)
    82  	}
    83  	return tag, result.SecretKey, nil
    84  }
    85  
    86  func (c *Client) userCall(username string, methodCall string) error {
    87  	if !names.IsValidUser(username) {
    88  		return errors.Errorf("%q is not a valid username", username)
    89  	}
    90  	tag := names.NewUserTag(username)
    91  
    92  	var results params.ErrorResults
    93  	args := params.Entities{
    94  		[]params.Entity{{tag.String()}},
    95  	}
    96  	err := c.facade.FacadeCall(methodCall, args, &results)
    97  	if err != nil {
    98  		return errors.Trace(err)
    99  	}
   100  	return results.OneError()
   101  }
   102  
   103  // DisableUser disables a user.  If the user is already disabled, the action
   104  // is consided a success.
   105  func (c *Client) DisableUser(username string) error {
   106  	return c.userCall(username, "DisableUser")
   107  }
   108  
   109  // EnableUser enables a users.  If the user is already enabled, the action is
   110  // consided a success.
   111  func (c *Client) EnableUser(username string) error {
   112  	return c.userCall(username, "EnableUser")
   113  }
   114  
   115  // IncludeDisabled is a type alias to avoid bare true/false values
   116  // in calls to the client method.
   117  type IncludeDisabled bool
   118  
   119  var (
   120  	// ActiveUsers indicates to only return active users.
   121  	ActiveUsers IncludeDisabled = false
   122  	// AllUsers indicates that both enabled and disabled users should be
   123  	// returned.
   124  	AllUsers IncludeDisabled = true
   125  )
   126  
   127  // UserInfo returns information about the specified users.  If no users are
   128  // specified, the call should return all users.  If includeDisabled is set to
   129  // ActiveUsers, only enabled users are returned.
   130  func (c *Client) UserInfo(usernames []string, all IncludeDisabled) ([]params.UserInfo, error) {
   131  	var results params.UserInfoResults
   132  	var entities []params.Entity
   133  	for _, username := range usernames {
   134  		if !names.IsValidUser(username) {
   135  			return nil, errors.Errorf("%q is not a valid username", username)
   136  		}
   137  		tag := names.NewUserTag(username)
   138  		entities = append(entities, params.Entity{Tag: tag.String()})
   139  	}
   140  	args := params.UserInfoRequest{
   141  		Entities:        entities,
   142  		IncludeDisabled: bool(all),
   143  	}
   144  	err := c.facade.FacadeCall("UserInfo", args, &results)
   145  	if err != nil {
   146  		return nil, errors.Trace(err)
   147  	}
   148  	// Only need to look for errors if users were explicitly specified, because
   149  	// if we didn't ask for any, we should get all, and we shouldn't get any
   150  	// errors for listing all.  We care here because we index into the users
   151  	// slice.
   152  	if len(results.Results) == len(usernames) {
   153  		var errorStrings []string
   154  		for i, result := range results.Results {
   155  			if result.Error != nil {
   156  				annotated := errors.Annotate(result.Error, usernames[i])
   157  				errorStrings = append(errorStrings, annotated.Error())
   158  			}
   159  		}
   160  		if len(errorStrings) > 0 {
   161  			return nil, errors.New(strings.Join(errorStrings, ", "))
   162  		}
   163  	}
   164  	info := []params.UserInfo{}
   165  	for i, result := range results.Results {
   166  		if result.Result == nil {
   167  			return nil, errors.Errorf("unexpected nil result at position %d", i)
   168  		}
   169  		info = append(info, *result.Result)
   170  	}
   171  	return info, nil
   172  }
   173  
   174  // SetPassword changes the password for the specified user.
   175  func (c *Client) SetPassword(username, password string) error {
   176  	if !names.IsValidUser(username) {
   177  		return errors.Errorf("%q is not a valid username", username)
   178  	}
   179  	tag := names.NewUserTag(username)
   180  	args := params.EntityPasswords{
   181  		Changes: []params.EntityPassword{{
   182  			Tag:      tag.String(),
   183  			Password: password}},
   184  	}
   185  	var results params.ErrorResults
   186  	err := c.facade.FacadeCall("SetPassword", args, &results)
   187  	if err != nil {
   188  		return err
   189  	}
   190  	return results.OneError()
   191  }
   192  
   193  // CreateLocalLoginMacaroon creates a local login macaroon for the
   194  // authenticated user.
   195  func (c *Client) CreateLocalLoginMacaroon(tag names.UserTag) (*macaroon.Macaroon, error) {
   196  	args := params.Entities{Entities: []params.Entity{{tag.String()}}}
   197  	var results params.MacaroonResults
   198  	if err := c.facade.FacadeCall("CreateLocalLoginMacaroon", args, &results); err != nil {
   199  		return nil, errors.Trace(err)
   200  	}
   201  	if n := len(results.Results); n != 1 {
   202  		logger.Errorf("expected 1 result, got %#v", results)
   203  		return nil, errors.Errorf("expected 1 result, got %d", n)
   204  	}
   205  	result := results.Results[0]
   206  	if result.Error != nil {
   207  		return nil, errors.Trace(result.Error)
   208  	}
   209  	return result.Result, nil
   210  }