github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/common/password.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/loggo"
     9  	"github.com/juju/names/v5"
    10  
    11  	apiservererrors "github.com/juju/juju/apiserver/errors"
    12  	"github.com/juju/juju/rpc/params"
    13  	"github.com/juju/juju/state"
    14  )
    15  
    16  var logger = loggo.GetLogger("juju.apiserver.common")
    17  
    18  // PasswordChanger implements a common SetPasswords method for use by
    19  // various facades.
    20  type PasswordChanger struct {
    21  	st           state.EntityFinder
    22  	getCanChange GetAuthFunc
    23  }
    24  
    25  // NewPasswordChanger returns a new PasswordChanger. The GetAuthFunc will be
    26  // used on each invocation of SetPasswords to determine current permissions.
    27  func NewPasswordChanger(st state.EntityFinder, getCanChange GetAuthFunc) *PasswordChanger {
    28  	return &PasswordChanger{
    29  		st:           st,
    30  		getCanChange: getCanChange,
    31  	}
    32  }
    33  
    34  // SetPasswords sets the given password for each supplied entity, if possible.
    35  func (pc *PasswordChanger) SetPasswords(args params.EntityPasswords) (params.ErrorResults, error) {
    36  	result := params.ErrorResults{
    37  		Results: make([]params.ErrorResult, len(args.Changes)),
    38  	}
    39  	if len(args.Changes) == 0 {
    40  		return result, nil
    41  	}
    42  	canChange, err := pc.getCanChange()
    43  	if err != nil {
    44  		return params.ErrorResults{}, errors.Trace(err)
    45  	}
    46  	for i, param := range args.Changes {
    47  		tag, err := names.ParseTag(param.Tag)
    48  		if err != nil {
    49  			result.Results[i].Error = apiservererrors.ServerError(apiservererrors.ErrPerm)
    50  			continue
    51  		}
    52  		if !canChange(tag) {
    53  			result.Results[i].Error = apiservererrors.ServerError(apiservererrors.ErrPerm)
    54  			continue
    55  		}
    56  		if err := pc.setPassword(tag, param.Password); err != nil {
    57  			result.Results[i].Error = apiservererrors.ServerError(err)
    58  		}
    59  	}
    60  	return result, nil
    61  }
    62  
    63  func (pc *PasswordChanger) setMongoPassword(entity state.Entity, password string) error {
    64  	type mongoPassworder interface {
    65  		SetMongoPassword(password string) error
    66  	}
    67  	// We set the mongo password first on the grounds that
    68  	// if it fails, the agent in question should still be able
    69  	// to authenticate to another API server and ask it to change
    70  	// its password.
    71  	if entity0, ok := entity.(mongoPassworder); ok {
    72  		if err := entity0.SetMongoPassword(password); err != nil {
    73  			return err
    74  		}
    75  		logger.Infof("setting mongo password for %q", entity.Tag())
    76  		return nil
    77  	}
    78  	// TODO(dfc) fix
    79  	return apiservererrors.NotSupportedError(entity.Tag(), "mongo access")
    80  }
    81  
    82  func (pc *PasswordChanger) setPassword(tag names.Tag, password string) error {
    83  	type isManager interface {
    84  		IsManager() bool
    85  	}
    86  	var err error
    87  	entity0, err := pc.st.FindEntity(tag)
    88  	if err != nil {
    89  		return err
    90  	}
    91  	entity, ok := entity0.(state.Authenticator)
    92  	if !ok {
    93  		return apiservererrors.NotSupportedError(tag, "authentication")
    94  	}
    95  	if entity, ok := entity0.(isManager); ok && entity.IsManager() {
    96  		err = pc.setMongoPassword(entity0, password)
    97  	}
    98  	if err == nil {
    99  		err = entity.SetPassword(password)
   100  		logger.Infof("setting password for %q", tag)
   101  	}
   102  	return err
   103  }