github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/stateauthenticator/modeluser.go (about)

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package stateauthenticator
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/errors"
    10  	"gopkg.in/juju/names.v2"
    11  
    12  	"github.com/juju/juju/apiserver/common"
    13  	"github.com/juju/juju/permission"
    14  	"github.com/juju/juju/state"
    15  )
    16  
    17  // loginEntity defines the interface needed to log in as a user.
    18  // Notable implementations are *state.User and *modelUserEntity.
    19  type loginEntity interface {
    20  	state.Entity
    21  	state.Authenticator
    22  	LastLogin() (time.Time, error)
    23  	UpdateLastLogin() error
    24  }
    25  
    26  // modelUserEntityFinder implements state.EntityFinder by returning
    27  // an Entity value for model users, ensuring that the user exists in
    28  // the state's current model, while also supporting external users.
    29  type modelUserEntityFinder struct {
    30  	st *state.State
    31  }
    32  
    33  // FindEntity implements state.EntityFinder.
    34  func (f modelUserEntityFinder) FindEntity(tag names.Tag) (state.Entity, error) {
    35  	utag, ok := tag.(names.UserTag)
    36  	if !ok {
    37  		return f.st.FindEntity(tag)
    38  	}
    39  
    40  	model, err := f.st.Model()
    41  	if err != nil {
    42  		return nil, errors.Trace(err)
    43  	}
    44  	modelUser, err := f.st.UserAccess(utag, model.ModelTag())
    45  	if err != nil && !errors.IsNotFound(err) {
    46  		return nil, errors.Trace(err)
    47  	}
    48  
    49  	// No model user found, so see if the user has been granted
    50  	// access to the controller.
    51  	if permission.IsEmptyUserAccess(modelUser) {
    52  		controllerUser, err := state.ControllerAccess(f.st, utag)
    53  		if err != nil && !errors.IsNotFound(err) {
    54  			return nil, errors.Trace(err)
    55  		}
    56  		// TODO(perrito666) remove the following section about everyone group
    57  		// when groups are implemented, this accounts only for the lack of a local
    58  		// ControllerUser when logging in from an external user that has not been granted
    59  		// permissions on the controller but there are permissions for the special
    60  		// everyone group.
    61  		if permission.IsEmptyUserAccess(controllerUser) && !utag.IsLocal() {
    62  			everyoneTag := names.NewUserTag(common.EveryoneTagName)
    63  			controllerUser, err = f.st.UserAccess(everyoneTag, f.st.ControllerTag())
    64  			if err != nil && !errors.IsNotFound(err) {
    65  				return nil, errors.Annotatef(err, "obtaining ControllerUser for everyone group")
    66  			}
    67  		}
    68  		if permission.IsEmptyUserAccess(controllerUser) {
    69  			return nil, errors.NotFoundf("model or controller user")
    70  		}
    71  	}
    72  
    73  	u := &modelUserEntity{
    74  		st:        f.st,
    75  		modelUser: modelUser,
    76  		tag:       utag,
    77  	}
    78  	if utag.IsLocal() {
    79  		user, err := f.st.User(utag)
    80  		if err != nil {
    81  			return nil, errors.Trace(err)
    82  		}
    83  		u.user = user
    84  	}
    85  	return u, nil
    86  }
    87  
    88  // modelUserEntity encapsulates an model user
    89  // and, if the user is local, the local state user
    90  // as well. This enables us to implement FindEntity
    91  // in such a way that the authentication mechanisms
    92  // can work without knowing these details.
    93  type modelUserEntity struct {
    94  	st *state.State
    95  
    96  	modelUser permission.UserAccess
    97  	user      *state.User
    98  	tag       names.Tag
    99  }
   100  
   101  // Refresh implements state.Authenticator.Refresh.
   102  func (u *modelUserEntity) Refresh() error {
   103  	if u.user == nil {
   104  		return nil
   105  	}
   106  	return u.user.Refresh()
   107  }
   108  
   109  // SetPassword implements state.Authenticator.SetPassword
   110  // by setting the password on the local user.
   111  func (u *modelUserEntity) SetPassword(pass string) error {
   112  	if u.user == nil {
   113  		return errors.New("cannot set password on external user")
   114  	}
   115  	return u.user.SetPassword(pass)
   116  }
   117  
   118  // PasswordValid implements state.Authenticator.PasswordValid.
   119  func (u *modelUserEntity) PasswordValid(pass string) bool {
   120  	if u.user == nil {
   121  		return false
   122  	}
   123  	return u.user.PasswordValid(pass)
   124  }
   125  
   126  // Tag implements state.Entity.Tag.
   127  func (u *modelUserEntity) Tag() names.Tag {
   128  	return u.tag
   129  }
   130  
   131  // LastLogin implements loginEntity.LastLogin.
   132  func (u *modelUserEntity) LastLogin() (time.Time, error) {
   133  	// The last connection for the model takes precedence over
   134  	// the local user last login time.
   135  	var err error
   136  	var t time.Time
   137  
   138  	model, err := u.st.Model()
   139  	if err != nil {
   140  		return t, errors.Trace(err)
   141  	}
   142  
   143  	if !permission.IsEmptyUserAccess(u.modelUser) {
   144  		t, err = model.LastModelConnection(u.modelUser.UserTag)
   145  	} else {
   146  		err = state.NeverConnectedError("controller user")
   147  	}
   148  	if state.IsNeverConnectedError(err) || permission.IsEmptyUserAccess(u.modelUser) {
   149  		if u.user != nil {
   150  			// There's a global user, so use that login time instead.
   151  			return u.user.LastLogin()
   152  		}
   153  		// Since we're implementing LastLogin, we need
   154  		// to implement LastLogin error semantics too.
   155  		err = state.NeverLoggedInError(err.Error())
   156  	}
   157  	return t, errors.Trace(err)
   158  }
   159  
   160  // UpdateLastLogin implements loginEntity.UpdateLastLogin.
   161  func (u *modelUserEntity) UpdateLastLogin() error {
   162  	var err error
   163  
   164  	if !permission.IsEmptyUserAccess(u.modelUser) {
   165  		if u.modelUser.Object.Kind() != names.ModelTagKind {
   166  			return errors.NotValidf("%s as model user", u.modelUser.Object.Kind())
   167  		}
   168  
   169  		model, err := u.st.Model()
   170  		if err != nil {
   171  			return errors.Trace(err)
   172  		}
   173  
   174  		err = model.UpdateLastModelConnection(u.modelUser.UserTag)
   175  	}
   176  
   177  	if u.user != nil {
   178  		err1 := u.user.UpdateLastLogin()
   179  		if err == nil {
   180  			return err1
   181  		}
   182  	}
   183  	if err != nil {
   184  		return errors.Trace(err)
   185  	}
   186  	return nil
   187  }