github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/usermanager/usermanager.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  	"time"
     8  
     9  	"gopkg.in/macaroon.v1"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/loggo"
    13  	"github.com/juju/names"
    14  
    15  	"github.com/juju/juju/apiserver/common"
    16  	"github.com/juju/juju/apiserver/modelmanager"
    17  	"github.com/juju/juju/apiserver/params"
    18  	"github.com/juju/juju/state"
    19  )
    20  
    21  var logger = loggo.GetLogger("juju.apiserver.usermanager")
    22  
    23  func init() {
    24  	common.RegisterStandardFacade("UserManager", 1, NewUserManagerAPI)
    25  }
    26  
    27  // UserManagerAPI implements the user manager interface and is the concrete
    28  // implementation of the api end point.
    29  type UserManagerAPI struct {
    30  	state                    *state.State
    31  	authorizer               common.Authorizer
    32  	createLocalLoginMacaroon func(names.UserTag) (*macaroon.Macaroon, error)
    33  	check                    *common.BlockChecker
    34  	apiUser                  names.UserTag
    35  	isAdmin                  bool
    36  }
    37  
    38  func NewUserManagerAPI(
    39  	st *state.State,
    40  	resources *common.Resources,
    41  	authorizer common.Authorizer,
    42  ) (*UserManagerAPI, error) {
    43  	if !authorizer.AuthClient() {
    44  		return nil, common.ErrPerm
    45  	}
    46  
    47  	// Since we know this is a user tag (because AuthClient is true),
    48  	// we just do the type assertion to the UserTag.
    49  	apiUser, _ := authorizer.GetAuthTag().(names.UserTag)
    50  	// Pretty much all of the user manager methods have special casing for admin
    51  	// users, so look once when we start and remember if the user is an admin.
    52  	isAdmin, err := st.IsControllerAdministrator(apiUser)
    53  	if err != nil {
    54  		return nil, errors.Trace(err)
    55  	}
    56  
    57  	resource, ok := resources.Get("createLocalLoginMacaroon").(common.ValueResource)
    58  	if !ok {
    59  		return nil, errors.NotFoundf("userAuth resource")
    60  	}
    61  	createLocalLoginMacaroon, ok := resource.Value.(func(names.UserTag) (*macaroon.Macaroon, error))
    62  	if !ok {
    63  		return nil, errors.NotValidf("userAuth resource")
    64  	}
    65  
    66  	return &UserManagerAPI{
    67  		state:                    st,
    68  		authorizer:               authorizer,
    69  		createLocalLoginMacaroon: createLocalLoginMacaroon,
    70  		check:   common.NewBlockChecker(st),
    71  		apiUser: apiUser,
    72  		isAdmin: isAdmin,
    73  	}, nil
    74  }
    75  
    76  // AddUser adds a user with a username, and either a password or
    77  // a randomly generated secret key which will be returned.
    78  func (api *UserManagerAPI) AddUser(args params.AddUsers) (params.AddUserResults, error) {
    79  	result := params.AddUserResults{
    80  		Results: make([]params.AddUserResult, len(args.Users)),
    81  	}
    82  	if err := api.check.ChangeAllowed(); err != nil {
    83  		return result, errors.Trace(err)
    84  	}
    85  
    86  	if len(args.Users) == 0 {
    87  		return result, nil
    88  	}
    89  	if !api.isAdmin {
    90  		return result, common.ErrPerm
    91  	}
    92  
    93  	for i, arg := range args.Users {
    94  		var user *state.User
    95  		var err error
    96  		if arg.Password != "" {
    97  			user, err = api.state.AddUser(arg.Username, arg.DisplayName, arg.Password, api.apiUser.Id())
    98  		} else {
    99  			user, err = api.state.AddUserWithSecretKey(arg.Username, arg.DisplayName, api.apiUser.Id())
   100  		}
   101  		if err != nil {
   102  			err = errors.Annotate(err, "failed to create user")
   103  			result.Results[i].Error = common.ServerError(err)
   104  			continue
   105  		} else {
   106  			result.Results[i] = params.AddUserResult{
   107  				Tag:       user.Tag().String(),
   108  				SecretKey: user.SecretKey(),
   109  			}
   110  		}
   111  
   112  		if len(arg.SharedModelTags) > 0 {
   113  			modelAccess, err := modelmanager.FromModelAccessParam(arg.ModelAccess)
   114  			if err != nil {
   115  				err = errors.Annotatef(err, "user %q created but models not shared", arg.Username)
   116  				result.Results[i].Error = common.ServerError(err)
   117  				continue
   118  			}
   119  			userTag := user.Tag().(names.UserTag)
   120  			for _, modelTagStr := range arg.SharedModelTags {
   121  				modelTag, err := names.ParseModelTag(modelTagStr)
   122  				if err != nil {
   123  					err = errors.Annotatef(err, "user %q created but model %q not shared", arg.Username, modelTagStr)
   124  					result.Results[i].Error = common.ServerError(err)
   125  					break
   126  				}
   127  				err = modelmanager.ChangeModelAccess(
   128  					modelmanager.NewStateBackend(api.state), modelTag, api.apiUser,
   129  					userTag, params.GrantModelAccess, modelAccess, api.isAdmin)
   130  				if err != nil {
   131  					err = errors.Annotatef(err, "user %q created but model %q not shared", arg.Username, modelTagStr)
   132  					result.Results[i].Error = common.ServerError(err)
   133  					break
   134  				}
   135  			}
   136  		}
   137  	}
   138  	return result, nil
   139  }
   140  
   141  func (api *UserManagerAPI) getUser(tag string) (*state.User, error) {
   142  	userTag, err := names.ParseUserTag(tag)
   143  	if err != nil {
   144  		return nil, errors.Trace(err)
   145  	}
   146  	user, err := api.state.User(userTag)
   147  	if err != nil {
   148  		return nil, errors.Wrap(err, common.ErrPerm)
   149  	}
   150  	return user, nil
   151  }
   152  
   153  // EnableUser enables one or more users.  If the user is already enabled,
   154  // the action is consided a success.
   155  func (api *UserManagerAPI) EnableUser(users params.Entities) (params.ErrorResults, error) {
   156  	if err := api.check.ChangeAllowed(); err != nil {
   157  		return params.ErrorResults{}, errors.Trace(err)
   158  	}
   159  	return api.enableUserImpl(users, "enable", (*state.User).Enable)
   160  }
   161  
   162  // DisableUser disables one or more users.  If the user is already disabled,
   163  // the action is consided a success.
   164  func (api *UserManagerAPI) DisableUser(users params.Entities) (params.ErrorResults, error) {
   165  	if err := api.check.ChangeAllowed(); err != nil {
   166  		return params.ErrorResults{}, errors.Trace(err)
   167  	}
   168  	return api.enableUserImpl(users, "disable", (*state.User).Disable)
   169  }
   170  
   171  func (api *UserManagerAPI) enableUserImpl(args params.Entities, action string, method func(*state.User) error) (params.ErrorResults, error) {
   172  	result := params.ErrorResults{
   173  		Results: make([]params.ErrorResult, len(args.Entities)),
   174  	}
   175  	if len(args.Entities) == 0 {
   176  		return result, nil
   177  	}
   178  	if !api.isAdmin {
   179  		return result, common.ErrPerm
   180  	}
   181  
   182  	for i, arg := range args.Entities {
   183  		user, err := api.getUser(arg.Tag)
   184  		if err != nil {
   185  			result.Results[i].Error = common.ServerError(err)
   186  			continue
   187  		}
   188  		err = method(user)
   189  		if err != nil {
   190  			result.Results[i].Error = common.ServerError(errors.Errorf("failed to %s user: %s", action, err))
   191  		}
   192  	}
   193  	return result, nil
   194  }
   195  
   196  // UserInfo returns information on a user.
   197  func (api *UserManagerAPI) UserInfo(request params.UserInfoRequest) (params.UserInfoResults, error) {
   198  	var results params.UserInfoResults
   199  	var infoForUser = func(user *state.User) params.UserInfoResult {
   200  		var lastLogin *time.Time
   201  		userLastLogin, err := user.LastLogin()
   202  		if err != nil {
   203  			if !state.IsNeverLoggedInError(err) {
   204  				logger.Debugf("error getting last login: %v", err)
   205  			}
   206  		} else {
   207  			lastLogin = &userLastLogin
   208  		}
   209  		return params.UserInfoResult{
   210  			Result: &params.UserInfo{
   211  				Username:       user.Name(),
   212  				DisplayName:    user.DisplayName(),
   213  				CreatedBy:      user.CreatedBy(),
   214  				DateCreated:    user.DateCreated(),
   215  				LastConnection: lastLogin,
   216  				Disabled:       user.IsDisabled(),
   217  			},
   218  		}
   219  	}
   220  
   221  	argCount := len(request.Entities)
   222  	if argCount == 0 {
   223  		users, err := api.state.AllUsers(request.IncludeDisabled)
   224  		if err != nil {
   225  			return results, errors.Trace(err)
   226  		}
   227  		for _, user := range users {
   228  			results.Results = append(results.Results, infoForUser(user))
   229  		}
   230  		return results, nil
   231  	}
   232  
   233  	results.Results = make([]params.UserInfoResult, argCount)
   234  	for i, arg := range request.Entities {
   235  		user, err := api.getUser(arg.Tag)
   236  		if err != nil {
   237  			results.Results[i].Error = common.ServerError(err)
   238  			continue
   239  		}
   240  		results.Results[i] = infoForUser(user)
   241  	}
   242  
   243  	return results, nil
   244  }
   245  
   246  // SetPassword changes the stored password for the specified users.
   247  func (api *UserManagerAPI) SetPassword(args params.EntityPasswords) (params.ErrorResults, error) {
   248  	if err := api.check.ChangeAllowed(); err != nil {
   249  		return params.ErrorResults{}, errors.Trace(err)
   250  	}
   251  	result := params.ErrorResults{
   252  		Results: make([]params.ErrorResult, len(args.Changes)),
   253  	}
   254  	if len(args.Changes) == 0 {
   255  		return result, nil
   256  	}
   257  	for i, arg := range args.Changes {
   258  		if err := api.setPassword(arg); err != nil {
   259  			result.Results[i].Error = common.ServerError(err)
   260  		}
   261  	}
   262  	return result, nil
   263  }
   264  
   265  func (api *UserManagerAPI) setPassword(arg params.EntityPassword) error {
   266  	user, err := api.getUser(arg.Tag)
   267  	if err != nil {
   268  		return errors.Trace(err)
   269  	}
   270  	if api.apiUser != user.UserTag() && !api.isAdmin {
   271  		return errors.Trace(common.ErrPerm)
   272  	}
   273  	if arg.Password == "" {
   274  		return errors.New("cannot use an empty password")
   275  	}
   276  	if err := user.SetPassword(arg.Password); err != nil {
   277  		return errors.Annotate(err, "failed to set password")
   278  	}
   279  	return nil
   280  }
   281  
   282  // CreateLocalLoginMacaroon creates a macaroon for the specified users to use
   283  // for future logins.
   284  func (api *UserManagerAPI) CreateLocalLoginMacaroon(args params.Entities) (params.MacaroonResults, error) {
   285  	results := params.MacaroonResults{
   286  		Results: make([]params.MacaroonResult, len(args.Entities)),
   287  	}
   288  	createLocalLoginMacaroon := func(arg params.Entity) (*macaroon.Macaroon, error) {
   289  		user, err := api.getUser(arg.Tag)
   290  		if err != nil {
   291  			return nil, errors.Trace(err)
   292  		}
   293  		if api.apiUser != user.UserTag() && !api.isAdmin {
   294  			return nil, errors.Trace(common.ErrPerm)
   295  		}
   296  		return api.createLocalLoginMacaroon(user.UserTag())
   297  	}
   298  	for i, arg := range args.Entities {
   299  		m, err := createLocalLoginMacaroon(arg)
   300  		if err != nil {
   301  			results.Results[i].Error = common.ServerError(err)
   302  			continue
   303  		}
   304  		results.Results[i].Result = m
   305  	}
   306  	return results, nil
   307  }