github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/common/permissions.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common
     5  
     6  import (
     7  	"strings"
     8  
     9  	"github.com/juju/errors"
    10  	"gopkg.in/juju/names.v2"
    11  
    12  	"github.com/juju/juju/permission"
    13  	"github.com/juju/juju/state"
    14  )
    15  
    16  // EveryoneTagName represents a special group that encompasses
    17  // all external users.
    18  const EveryoneTagName = "everyone@external"
    19  
    20  // UserAccess returns the access the user has on the model state
    21  // and the host controller.
    22  func UserAccess(st *state.State, utag names.UserTag) (modelUser, controllerUser permission.UserAccess, err error) {
    23  	var none permission.UserAccess
    24  	modelUser, err = st.UserAccess(utag, st.ModelTag())
    25  	if err != nil && !errors.IsNotFound(err) {
    26  		return none, none, errors.Trace(err)
    27  	}
    28  
    29  	controllerUser, err = state.ControllerAccess(st, utag)
    30  	if err != nil && !errors.IsNotFound(err) {
    31  		return none, none, errors.Trace(err)
    32  	}
    33  
    34  	// TODO(perrito666) remove the following section about everyone group
    35  	// when groups are implemented, this accounts only for the lack of a local
    36  	// ControllerUser when logging in from an external user that has not been granted
    37  	// permissions on the controller but there are permissions for the special
    38  	// everyone group.
    39  	if !utag.IsLocal() {
    40  		controllerUser, err = maybeUseGroupPermission(st.UserAccess, controllerUser, st.ControllerTag(), utag)
    41  		if err != nil {
    42  			return none, none, errors.Annotatef(err, "obtaining ControllerUser for everyone group")
    43  		}
    44  	}
    45  
    46  	if permission.IsEmptyUserAccess(modelUser) &&
    47  		permission.IsEmptyUserAccess(controllerUser) {
    48  		return none, none, errors.NotFoundf("model or controller user")
    49  	}
    50  	return modelUser, controllerUser, nil
    51  }
    52  
    53  // HasPermission returns true if the specified user has the specified
    54  // permission on target.
    55  func HasPermission(userGetter userAccessFunc, utag names.Tag,
    56  	requestedPermission permission.Access, target names.Tag) (bool, error) {
    57  
    58  	validForKind := false
    59  	switch requestedPermission {
    60  	case permission.LoginAccess, permission.AddModelAccess, permission.SuperuserAccess:
    61  		validForKind = target.Kind() == names.ControllerTagKind
    62  	case permission.ReadAccess, permission.WriteAccess, permission.AdminAccess:
    63  		validForKind = target.Kind() == names.ModelTagKind
    64  	}
    65  
    66  	if !validForKind {
    67  		return false, nil
    68  	}
    69  
    70  	userTag, ok := utag.(names.UserTag)
    71  	if !ok {
    72  		// lets not reveal more than is strictly necessary
    73  		return false, nil
    74  	}
    75  
    76  	user, err := userGetter(userTag, target)
    77  	if err != nil && !errors.IsNotFound(err) {
    78  		return false, errors.Annotatef(err, "while obtaining %s user", target.Kind())
    79  	}
    80  	// there is a special case for external users, a group called everyone@external
    81  	if target.Kind() == names.ControllerTagKind && !userTag.IsLocal() {
    82  		controllerTag, ok := target.(names.ControllerTag)
    83  		if !ok {
    84  			return false, errors.NotValidf("controller tag")
    85  		}
    86  
    87  		// TODO(perrito666) remove the following section about everyone group
    88  		// when groups are implemented, this accounts only for the lack of a local
    89  		// ControllerUser when logging in from an external user that has not been granted
    90  		// permissions on the controller but there are permissions for the special
    91  		// everyone group.
    92  		user, err = maybeUseGroupPermission(userGetter, user, controllerTag, userTag)
    93  		if err != nil {
    94  			return false, errors.Trace(err)
    95  		}
    96  		if permission.IsEmptyUserAccess(user) {
    97  			return false, nil
    98  		}
    99  	}
   100  	// returning this kind of information would be too much information to reveal too.
   101  	if errors.IsNotFound(err) {
   102  		return false, nil
   103  	}
   104  	modelPermission := user.Access.EqualOrGreaterModelAccessThan(requestedPermission) && target.Kind() == names.ModelTagKind
   105  	controllerPermission := user.Access.EqualOrGreaterControllerAccessThan(requestedPermission) && target.Kind() == names.ControllerTagKind
   106  	if !controllerPermission && !modelPermission {
   107  		return false, nil
   108  	}
   109  	return true, nil
   110  }
   111  
   112  // maybeUseGroupPermission returns a permission.UserAccess updated
   113  // with the group permissions that apply to it if higher than
   114  // current.
   115  // If the passed UserAccess is empty (controller user lacks permissions)
   116  // but the group is not, a stand-in will be created to hold the group
   117  // permissions.
   118  func maybeUseGroupPermission(
   119  	userGetter userAccessFunc,
   120  	externalUser permission.UserAccess,
   121  	controllerTag names.ControllerTag,
   122  	userTag names.UserTag,
   123  ) (permission.UserAccess, error) {
   124  
   125  	everyoneTag := names.NewUserTag(EveryoneTagName)
   126  	everyone, err := userGetter(everyoneTag, controllerTag)
   127  	if errors.IsNotFound(err) {
   128  		return externalUser, nil
   129  	}
   130  	if err != nil {
   131  		return permission.UserAccess{}, errors.Trace(err)
   132  	}
   133  	if permission.IsEmptyUserAccess(externalUser) &&
   134  		!permission.IsEmptyUserAccess(everyone) {
   135  		externalUser = newControllerUserFromGroup(everyone, userTag)
   136  	}
   137  
   138  	if everyone.Access.EqualOrGreaterControllerAccessThan(externalUser.Access) {
   139  		externalUser.Access = everyone.Access
   140  	}
   141  	return externalUser, nil
   142  }
   143  
   144  type userAccessFunc func(names.UserTag, names.Tag) (permission.UserAccess, error)
   145  
   146  // newControllerUserFromGroup returns a permission.UserAccess that serves
   147  // as a stand-in for a user that has group access but no explicit user
   148  // access.
   149  func newControllerUserFromGroup(everyoneAccess permission.UserAccess,
   150  	userTag names.UserTag) permission.UserAccess {
   151  	everyoneAccess.UserTag = userTag
   152  	everyoneAccess.UserID = strings.ToLower(userTag.Canonical())
   153  	everyoneAccess.UserName = userTag.Canonical()
   154  	return everyoneAccess
   155  }