github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/useraccess.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/juju/errors"
    12  	"gopkg.in/juju/names.v2"
    13  	"gopkg.in/mgo.v2/txn"
    14  
    15  	"github.com/juju/juju/permission"
    16  )
    17  
    18  type userAccessDoc struct {
    19  	ID          string    `bson:"_id"`
    20  	ObjectUUID  string    `bson:"object-uuid"`
    21  	UserName    string    `bson:"user"`
    22  	DisplayName string    `bson:"displayname"`
    23  	CreatedBy   string    `bson:"createdby"`
    24  	DateCreated time.Time `bson:"datecreated"`
    25  }
    26  
    27  // UserAccessSpec defines the attributes that can be set when adding a new
    28  // user access.
    29  type UserAccessSpec struct {
    30  	User        names.UserTag
    31  	CreatedBy   names.UserTag
    32  	DisplayName string
    33  	Access      permission.Access
    34  }
    35  
    36  // userAccessTarget defines the target of a user access granting.
    37  type userAccessTarget struct {
    38  	uuid      string
    39  	globalKey string
    40  }
    41  
    42  // AddModelUser adds a new user for the model identified by modelUUID to the database.
    43  func (st *State) AddModelUser(modelUUID string, spec UserAccessSpec) (permission.UserAccess, error) {
    44  	if err := permission.ValidateModelAccess(spec.Access); err != nil {
    45  		return permission.UserAccess{}, errors.Annotate(err, "adding model user")
    46  	}
    47  	target := userAccessTarget{
    48  		uuid:      modelUUID,
    49  		globalKey: modelGlobalKey,
    50  	}
    51  	return st.addUserAccess(spec, target)
    52  }
    53  
    54  // AddControllerUser adds a new user for the curent controller to the database.
    55  func (st *State) AddControllerUser(spec UserAccessSpec) (permission.UserAccess, error) {
    56  	if err := permission.ValidateControllerAccess(spec.Access); err != nil {
    57  		return permission.UserAccess{}, errors.Annotate(err, "adding controller user")
    58  	}
    59  	return st.addUserAccess(spec, userAccessTarget{globalKey: controllerGlobalKey})
    60  }
    61  
    62  func (st *State) addUserAccess(spec UserAccessSpec, target userAccessTarget) (permission.UserAccess, error) {
    63  	// Ensure local user exists in state before adding them as an model user.
    64  	if spec.User.IsLocal() {
    65  		localUser, err := st.User(spec.User)
    66  		if err != nil {
    67  			return permission.UserAccess{}, errors.Annotate(err, fmt.Sprintf("user %q does not exist locally", spec.User.Name()))
    68  		}
    69  		if spec.DisplayName == "" {
    70  			spec.DisplayName = localUser.DisplayName()
    71  		}
    72  	}
    73  
    74  	// Ensure local createdBy user exists.
    75  	if spec.CreatedBy.IsLocal() {
    76  		if _, err := st.User(spec.CreatedBy); err != nil {
    77  			return permission.UserAccess{}, errors.Annotatef(err, "createdBy user %q does not exist locally", spec.CreatedBy.Name())
    78  		}
    79  	}
    80  	var (
    81  		ops       []txn.Op
    82  		err       error
    83  		targetTag names.Tag
    84  	)
    85  	switch target.globalKey {
    86  	case modelGlobalKey:
    87  		ops = createModelUserOps(
    88  			target.uuid,
    89  			spec.User,
    90  			spec.CreatedBy,
    91  			spec.DisplayName,
    92  			st.NowToTheSecond(),
    93  			spec.Access)
    94  		targetTag = names.NewModelTag(target.uuid)
    95  	case controllerGlobalKey:
    96  		ops = createControllerUserOps(
    97  			st.ControllerUUID(),
    98  			spec.User,
    99  			spec.CreatedBy,
   100  			spec.DisplayName,
   101  			st.NowToTheSecond(),
   102  			spec.Access)
   103  		targetTag = st.controllerTag
   104  	default:
   105  		return permission.UserAccess{}, errors.NotSupportedf("user access global key %q", target.globalKey)
   106  	}
   107  	err = st.runTransactionFor(target.uuid, ops)
   108  	if err == txn.ErrAborted {
   109  		err = errors.AlreadyExistsf("user access %q", spec.User.Canonical())
   110  	}
   111  	if err != nil {
   112  		return permission.UserAccess{}, errors.Trace(err)
   113  	}
   114  	return st.UserAccess(spec.User, targetTag)
   115  }
   116  
   117  // userAccessID returns the document id of the user access.
   118  func userAccessID(user names.UserTag) string {
   119  	username := user.Canonical()
   120  	return strings.ToLower(username)
   121  }
   122  
   123  // NewModelUserAccess returns a new permission.UserAccess for the given userDoc and
   124  // current Model.
   125  func NewModelUserAccess(st *State, userDoc userAccessDoc) (permission.UserAccess, error) {
   126  	perm, err := st.userPermission(modelKey(userDoc.ObjectUUID), userGlobalKey(strings.ToLower(userDoc.UserName)))
   127  	if err != nil {
   128  		return permission.UserAccess{}, errors.Annotate(err, "obtaining model permission")
   129  	}
   130  	return newUserAccess(perm, userDoc, names.NewModelTag(userDoc.ObjectUUID)), nil
   131  }
   132  
   133  // NewControllerUserAccess returns a new permission.UserAccess for the given userDoc and
   134  // current Controller.
   135  func NewControllerUserAccess(st *State, userDoc userAccessDoc) (permission.UserAccess, error) {
   136  	perm, err := st.controllerUserPermission(controllerKey(st.ControllerUUID()), userGlobalKey(strings.ToLower(userDoc.UserName)))
   137  	if err != nil {
   138  		return permission.UserAccess{}, errors.Annotate(err, "obtaining controller permission")
   139  	}
   140  	return newUserAccess(perm, userDoc, names.NewControllerTag(userDoc.ObjectUUID)), nil
   141  }
   142  
   143  func newUserAccess(perm *userPermission, userDoc userAccessDoc, object names.Tag) permission.UserAccess {
   144  	return permission.UserAccess{
   145  		UserID:      userDoc.ID,
   146  		UserTag:     names.NewUserTag(userDoc.UserName),
   147  		Object:      object,
   148  		Access:      perm.access(),
   149  		CreatedBy:   names.NewUserTag(userDoc.CreatedBy),
   150  		DateCreated: userDoc.DateCreated.UTC(),
   151  		DisplayName: userDoc.DisplayName,
   152  		UserName:    userDoc.UserName,
   153  	}
   154  }
   155  
   156  // UserAccess returns a new permission.UserAccess for the passed subject and target.
   157  func (st *State) UserAccess(subject names.UserTag, target names.Tag) (permission.UserAccess, error) {
   158  	if subject.IsLocal() {
   159  		_, err := st.User(subject)
   160  		if err != nil {
   161  			return permission.UserAccess{}, errors.Trace(err)
   162  		}
   163  	}
   164  
   165  	var (
   166  		userDoc userAccessDoc
   167  		err     error
   168  	)
   169  	switch target.Kind() {
   170  	case names.ModelTagKind:
   171  		userDoc, err = st.modelUser(target.Id(), subject)
   172  		if err == nil {
   173  			return NewModelUserAccess(st, userDoc)
   174  		}
   175  	case names.ControllerTagKind:
   176  		userDoc, err = st.controllerUser(subject)
   177  		if err == nil {
   178  			return NewControllerUserAccess(st, userDoc)
   179  		}
   180  	default:
   181  		return permission.UserAccess{}, errors.NotValidf("%q as a target", target.Kind())
   182  	}
   183  	return permission.UserAccess{}, errors.Trace(err)
   184  }
   185  
   186  // SetUserAccess sets <access> level on <target> to <subject>.
   187  func (st *State) SetUserAccess(subject names.UserTag, target names.Tag, access permission.Access) (permission.UserAccess, error) {
   188  	err := access.Validate()
   189  	if err != nil {
   190  		return permission.UserAccess{}, errors.Trace(err)
   191  	}
   192  	switch target.Kind() {
   193  	case names.ModelTagKind:
   194  		err = st.setModelAccess(access, userGlobalKey(userAccessID(subject)), target.Id())
   195  	case names.ControllerTagKind:
   196  		err = st.setControllerAccess(access, userGlobalKey(userAccessID(subject)))
   197  	default:
   198  		return permission.UserAccess{}, errors.NotValidf("%q as a target", target.Kind())
   199  	}
   200  	if err != nil {
   201  		return permission.UserAccess{}, errors.Trace(err)
   202  	}
   203  	return st.UserAccess(subject, target)
   204  }
   205  
   206  // RemoveUserAccess removes access for subject to the passed tag.
   207  func (st *State) RemoveUserAccess(subject names.UserTag, target names.Tag) error {
   208  	switch target.Kind() {
   209  	case names.ModelTagKind:
   210  		return errors.Trace(st.removeModelUser(subject))
   211  	case names.ControllerTagKind:
   212  		return errors.Trace(st.removeControllerUser(subject))
   213  	}
   214  	return errors.NotValidf("%q as a target", target.Kind())
   215  }