Example #1
0
// AddEnvironmentUser adds a new user to the database.
func (st *State) AddEnvironmentUser(user, createdBy names.UserTag, displayName string) (*EnvironmentUser, error) {
	// Ensure local user exists in state before adding them as an environment user.
	if user.IsLocal() {
		localUser, err := st.User(user)
		if err != nil {
			return nil, errors.Annotate(err, fmt.Sprintf("user %q does not exist locally", user.Name()))
		}
		if displayName == "" {
			displayName = localUser.DisplayName()
		}
	}

	// Ensure local createdBy user exists.
	if createdBy.IsLocal() {
		if _, err := st.User(createdBy); err != nil {
			return nil, errors.Annotate(err, fmt.Sprintf("createdBy user %q does not exist locally", createdBy.Name()))
		}
	}

	envuuid := st.EnvironUUID()
	op := createEnvUserOp(envuuid, user, createdBy, displayName)
	err := st.runTransaction([]txn.Op{op})
	if err == txn.ErrAborted {
		err = errors.AlreadyExistsf("environment user %q", user.Canonical())
	}
	if err != nil {
		return nil, errors.Trace(err)
	}
	// Re-read from DB to get the multi-env updated values.
	return st.EnvironmentUser(user)
}
Example #2
0
File: user.go Project: makyo/juju
// CreateLocalLoginMacaroon creates a time-limited macaroon for a local user
// to log into the controller with. The macaroon will be valid for use with
// UserAuthenticator.Authenticate until the time limit expires, or the Juju
// controller agent restarts.
//
// NOTE(axw) this method will generate a key for a previously unseen user,
// and store it in the bakery.Service's storage. Callers should first ensure
// the user is valid before calling this, to avoid filling storage with keys
// for invalid users.
func (u *UserAuthenticator) CreateLocalLoginMacaroon(tag names.UserTag) (*macaroon.Macaroon, error) {

	expiryTime := u.Clock.Now().Add(localLoginExpiryTime)

	// Ensure that the private key that we generate and store will be
	// removed from storage once the expiry time has elapsed.
	bakeryService, err := u.Service.ExpireStorageAt(expiryTime)
	if err != nil {
		return nil, errors.Trace(err)
	}

	// We create the macaroon with a random ID and random root key, which
	// enables multiple clients to login as the same user and obtain separate
	// macaroons without having them use the same root key.
	m, err := bakeryService.NewMacaroon("", nil, []checkers.Caveat{
		// The macaroon may only be used to log in as the user
		// specified by the tag passed to CreateLocalUserMacaroon.
		checkers.DeclaredCaveat(usernameKey, tag.Canonical()),
	})
	if err != nil {
		return nil, errors.Annotate(err, "cannot create macaroon")
	}
	if err := addMacaroonTimeBeforeCaveat(bakeryService, m, expiryTime); err != nil {
		return nil, errors.Trace(err)
	}
	return m, nil
}
Example #3
0
// ModelsForUser returns a list of models that the user
// is able to access.
func (st *State) ModelsForUser(user names.UserTag) ([]*UserModel, error) {
	// Since there are no groups at this stage, the simplest way to get all
	// the models that a particular user can see is to look through the
	// model user collection. A raw collection is required to support
	// queries across multiple models.
	modelUsers, userCloser := st.getRawCollection(modelUsersC)
	defer userCloser()

	// TODO: consider adding an index to the modelUsers collection on the username.
	var userSlice []modelUserDoc
	err := modelUsers.Find(bson.D{{"user", user.Canonical()}}).Select(bson.D{{"model-uuid", 1}, {"_id", 1}}).All(&userSlice)
	if err != nil {
		return nil, err
	}

	var result []*UserModel
	for _, doc := range userSlice {
		modelTag := names.NewModelTag(doc.ModelUUID)
		env, err := st.GetModel(modelTag)
		if err != nil {
			return nil, errors.Trace(err)
		}

		result = append(result, &UserModel{Model: env, User: user})
	}

	return result, nil
}
Example #4
0
// EnvironmentsForUser returns a list of enviroments that the user
// is able to access.
func (st *State) EnvironmentsForUser(user names.UserTag) ([]*UserEnvironment, error) {
	// Since there are no groups at this stage, the simplest way to get all
	// the environments that a particular user can see is to look through the
	// environment user collection. A raw collection is required to support
	// queries across multiple environments.
	envUsers, userCloser := st.getRawCollection(envUsersC)
	defer userCloser()

	// TODO: consider adding an index to the envUsers collection on the username.
	var userSlice []envUserDoc
	err := envUsers.Find(bson.D{{"user", user.Canonical()}}).Select(bson.D{{"env-uuid", 1}, {"_id", 1}}).All(&userSlice)
	if err != nil {
		return nil, err
	}

	var result []*UserEnvironment
	for _, doc := range userSlice {
		envTag := names.NewEnvironTag(doc.EnvUUID)
		env, err := st.GetEnvironment(envTag)
		if err != nil {
			return nil, errors.Trace(err)
		}

		result = append(result, &UserEnvironment{Environment: env, User: user})
	}

	return result, nil
}
Example #5
0
func fakeLocalLoginMacaroon(tag names.UserTag) *macaroon.Macaroon {
	mac, err := macaroon.New([]byte("abcdefghijklmnopqrstuvwx"), tag.Canonical(), "juju")
	if err != nil {
		panic(err)
	}
	return mac
}
Example #6
0
// NewModel creates a new model with its own UUID and
// prepares it for use. Model and State instances for the new
// model are returned.
//
// The controller model's UUID is attached to the new
// model's document. Having the server UUIDs stored with each
// model document means that we have a way to represent external
// models, perhaps for future use around cross model
// relations.
func (st *State) NewModel(cfg *config.Config, owner names.UserTag) (_ *Model, _ *State, err error) {
	if owner.IsLocal() {
		if _, err := st.User(owner); err != nil {
			return nil, nil, errors.Annotate(err, "cannot create model")
		}
	}

	ssEnv, err := st.ControllerModel()
	if err != nil {
		return nil, nil, errors.Annotate(err, "could not load controller model")
	}

	uuid := cfg.UUID()
	newState, err := st.ForModel(names.NewModelTag(uuid))
	if err != nil {
		return nil, nil, errors.Annotate(err, "could not create state for new model")
	}
	defer func() {
		if err != nil {
			newState.Close()
		}
	}()

	ops, err := newState.envSetupOps(cfg, uuid, ssEnv.UUID(), owner)
	if err != nil {
		return nil, nil, errors.Annotate(err, "failed to create new model")
	}
	err = newState.runTransaction(ops)
	if err == txn.ErrAborted {

		// We have a  unique key restriction on the "owner" and "name" fields,
		// which will cause the insert to fail if there is another record with
		// the same "owner" and "name" in the collection. If the txn is
		// aborted, check if it is due to the unique key restriction.
		models, closer := st.getCollection(modelsC)
		defer closer()
		envCount, countErr := models.Find(bson.D{
			{"owner", owner.Canonical()},
			{"name", cfg.Name()}},
		).Count()
		if countErr != nil {
			err = errors.Trace(countErr)
		} else if envCount > 0 {
			err = errors.AlreadyExistsf("model %q for %s", cfg.Name(), owner.Canonical())
		} else {
			err = errors.New("model already exists")
		}
	}
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	newEnv, err := newState.Model()
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	return newEnv, newState, nil
}
Example #7
0
// createUniqueOwnerModelNameOp returns the operation needed to create
// an usermodelnameC document with the given owner and model name.
func createUniqueOwnerModelNameOp(owner names.UserTag, envName string) txn.Op {
	return txn.Op{
		C:      usermodelnameC,
		Id:     userModelNameIndex(owner.Canonical(), envName),
		Assert: txn.DocMissing,
		Insert: bson.M{},
	}
}
Example #8
0
File: user.go Project: imoapps/juju
// User returns the state User for the given name.
func (st *State) User(tag names.UserTag) (*User, error) {
	if !tag.IsLocal() {
		return nil, errors.NotFoundf("user %q", tag.Canonical())
	}
	user := &User{st: st}
	if err := st.getUser(tag.Name(), &user.doc); err != nil {
		return nil, errors.Trace(err)
	}
	return user, nil
}
Example #9
0
// authCheck checks if the user is acting on their own behalf, or if they
// are an administrator acting on behalf of another user.
func (m *ModelManagerAPI) authCheck(user names.UserTag) error {
	if m.isAdmin {
		logger.Tracef("%q is a controller admin", m.apiUser.Canonical())
		return nil
	}

	// We can't just compare the UserTags themselves as the provider part
	// may be unset, and gets replaced with 'local'. We must compare against
	// the Canonical value of the user tag.
	if m.apiUser.Canonical() == user.Canonical() {
		return nil
	}
	return common.ErrPerm
}
Example #10
0
// ModelUser returns the model user.
func (st *State) ModelUser(user names.UserTag) (*ModelUser, error) {
	modelUser := &ModelUser{st: st}
	modelUsers, closer := st.getCollection(modelUsersC)
	defer closer()

	username := strings.ToLower(user.Canonical())
	err := modelUsers.FindId(username).One(&modelUser.doc)
	if err == mgo.ErrNotFound {
		return nil, errors.NotFoundf("model user %q", user.Canonical())
	}
	// DateCreated is inserted as UTC, but read out as local time. So we
	// convert it back to UTC here.
	modelUser.doc.DateCreated = modelUser.doc.DateCreated.UTC()
	return modelUser, nil
}
Example #11
0
// createModelOp returns the operation needed to create
// an model document with the given name and UUID.
func createModelOp(st *State, owner names.UserTag, name, uuid, server string) txn.Op {
	doc := &modelDoc{
		UUID:       uuid,
		Name:       name,
		Life:       Alive,
		Owner:      owner.Canonical(),
		ServerUUID: server,
	}
	return txn.Op{
		C:      modelsC,
		Id:     uuid,
		Assert: txn.DocMissing,
		Insert: doc,
	}
}
Example #12
0
// EnvironmentUser returns the environment user.
func (st *State) EnvironmentUser(user names.UserTag) (*EnvironmentUser, error) {
	envUser := &EnvironmentUser{st: st}
	envUsers, closer := st.getCollection(envUsersC)
	defer closer()

	username := strings.ToLower(user.Canonical())
	err := envUsers.FindId(username).One(&envUser.doc)
	if err == mgo.ErrNotFound {
		return nil, errors.NotFoundf("environment user %q", user.Canonical())
	}
	// DateCreated is inserted as UTC, but read out as local time. So we
	// convert it back to UTC here.
	envUser.doc.DateCreated = envUser.doc.DateCreated.UTC()
	return envUser, nil
}
Example #13
0
// RemoveModelUser removes a user from the database.
func (st *State) RemoveModelUser(user names.UserTag) error {
	ops := []txn.Op{{
		C:      modelUsersC,
		Id:     modelUserID(user),
		Assert: txn.DocExists,
		Remove: true,
	}}
	err := st.runTransaction(ops)
	if err == txn.ErrAborted {
		err = errors.NewNotFound(err, fmt.Sprintf("env user %q does not exist", user.Canonical()))
	}
	if err != nil {
		return errors.Trace(err)
	}
	return nil
}
Example #14
0
func createEnvUserOp(envuuid string, user, createdBy names.UserTag, displayName string) txn.Op {
	creatorname := createdBy.Canonical()
	doc := &envUserDoc{
		ID:          envUserID(user),
		EnvUUID:     envuuid,
		UserName:    user.Canonical(),
		DisplayName: displayName,
		CreatedBy:   creatorname,
		DateCreated: nowToTheSecond(),
	}
	return txn.Op{
		C:      envUsersC,
		Id:     envUserID(user),
		Assert: txn.DocMissing,
		Insert: doc,
	}
}
Example #15
0
func (u *UserAuthenticator) authenticateMacaroons(
	entityFinder EntityFinder, tag names.UserTag, req params.LoginRequest,
) (state.Entity, error) {
	// Check for a valid request macaroon.
	assert := map[string]string{usernameKey: tag.Canonical()}
	_, err := u.Service.CheckAny(req.Macaroons, assert, checkers.New(checkers.TimeBefore))
	if err != nil {
		return nil, errors.Trace(err)
	}
	entity, err := entityFinder.FindEntity(tag)
	if errors.IsNotFound(err) {
		return nil, errors.Trace(common.ErrBadCreds)
	} else if err != nil {
		return nil, errors.Trace(err)
	}
	return entity, nil
}
Example #16
0
func createModelUserOp(modelUUID string, user, createdBy names.UserTag, displayName string, readOnly bool) txn.Op {
	creatorname := createdBy.Canonical()
	doc := &modelUserDoc{
		ID:          modelUserID(user),
		ModelUUID:   modelUUID,
		UserName:    user.Canonical(),
		DisplayName: displayName,
		ReadOnly:    readOnly,
		CreatedBy:   creatorname,
		DateCreated: nowToTheSecond(),
	}
	return txn.Op{
		C:      modelUsersC,
		Id:     modelUserID(user),
		Assert: txn.DocMissing,
		Insert: doc,
	}
}
Example #17
0
func createModelUserOp(modelUUID string, user, createdBy names.UserTag, displayName string, dateCreated time.Time, access ModelAccess) txn.Op {
	creatorname := createdBy.Canonical()
	doc := &modelUserDoc{
		ID:          modelUserID(user),
		ModelUUID:   modelUUID,
		UserName:    user.Canonical(),
		DisplayName: displayName,
		Access:      access,
		CreatedBy:   creatorname,
		DateCreated: dateCreated,
	}
	return txn.Op{
		C:      modelUsersC,
		Id:     modelUserID(user),
		Assert: txn.DocMissing,
		Insert: doc,
	}
}
Example #18
0
// IsControllerAdministrator returns true if the user specified has access to the
// state server model (the system model).
func (st *State) IsControllerAdministrator(user names.UserTag) (bool, error) {
	ssinfo, err := st.StateServerInfo()
	if err != nil {
		return false, errors.Annotate(err, "could not get state server info")
	}

	serverUUID := ssinfo.ModelTag.Id()

	modelUsers, userCloser := st.getRawCollection(modelUsersC)
	defer userCloser()

	count, err := modelUsers.Find(bson.D{
		{"model-uuid", serverUUID},
		{"user", user.Canonical()},
	}).Count()
	if err != nil {
		return false, errors.Trace(err)
	}
	return count == 1, nil
}
Example #19
0
// CreateLocalLoginMacaroon creates a time-limited macaroon for a local user
// to log into the controller with. The macaroon will be valid for use with
// UserAuthenticator.Authenticate until the time limit expires, or the Juju
// controller agent restarts.
//
// NOTE(axw) this method will generate a key for a previously unseen user,
// and store it in the bakery.Service's storage, which is currently in-memory.
// Callers should first ensure the user is valid before calling this, to avoid
// filling memory with keys for invalid users.
func (u *UserAuthenticator) CreateLocalLoginMacaroon(tag names.UserTag) (*macaroon.Macaroon, error) {
	// We create the macaroon with a random ID and random root key, which
	// enables multiple clients to login as the same user and obtain separate
	// macaroons without having them use the same root key.
	//
	// TODO(axw) check with rogpeppe about this. bakery.Service doesn't
	// currently garbage collect, so this will grow until the controller
	// agent restarts.
	m, err := u.Service.NewMacaroon("", nil, []checkers.Caveat{
		// The macaroon may only be used to log in as the user
		// specified by the tag passed to CreateLocalUserMacaroon.
		checkers.DeclaredCaveat(usernameKey, tag.Canonical()),
	})
	if err != nil {
		return nil, errors.Annotate(err, "cannot create macaroon")
	}
	if err := addMacaroonTimeBeforeCaveat(u.Service, m, localLoginExpiryTime); err != nil {
		return nil, errors.Trace(err)
	}
	return m, nil
}
Example #20
0
// authCheck checks if the user is acting on their own behalf, or if they
// are an administrator acting on behalf of another user.
func (em *EnvironmentManagerAPI) authCheck(user names.UserTag) error {
	// Since we know this is a user tag (because AuthClient is true),
	// we just do the type assertion to the UserTag.
	apiUser, _ := em.authorizer.GetAuthTag().(names.UserTag)
	isAdmin, err := em.state.IsSystemAdministrator(apiUser)
	if err != nil {
		return errors.Trace(err)
	}
	if isAdmin {
		logger.Tracef("%q is a system admin", apiUser.Canonical())
		return nil
	}

	// We can't just compare the UserTags themselves as the provider part
	// may be unset, and gets replaced with 'local'. We must compare against
	// the Username of the user tag.
	if apiUser.Canonical() == user.Canonical() {
		return nil
	}
	return common.ErrPerm
}
Example #21
0
func (st *mockState) IsControllerAdministrator(user names.UserTag) (bool, error) {
	st.MethodCall(st, "IsControllerAdministrator", user)
	return user.Canonical() == "admin@local", st.NextErr()
}
Example #22
0
func (s *modelManagerSuite) assertNewUser(c *gc.C, modelUser *state.ModelUser, userTag, creatorTag names.UserTag) {
	c.Assert(modelUser.UserTag(), gc.Equals, userTag)
	c.Assert(modelUser.CreatedBy(), gc.Equals, creatorTag.Canonical())
	_, err := modelUser.LastConnection()
	c.Assert(err, jc.Satisfies, state.IsNeverConnectedError)
}
Example #23
0
// modelUserID returns the document id of the model user
func modelUserID(user names.UserTag) string {
	username := user.Canonical()
	return strings.ToLower(username)
}