Beispiel #1
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.Username()}}).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, LastConnection: doc.LastConnection})
	}

	return result, nil
}
Beispiel #2
0
func fakeLocalLoginMacaroon(tag names.UserTag) *macaroon.Macaroon {
	mac, err := macaroon.New([]byte("abcdefghijklmnopqrstuvwx"), tag.Canonical(), "juju")
	if err != nil {
		panic(err)
	}
	return mac
}
Beispiel #3
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. 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
}
Beispiel #4
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
}
Beispiel #5
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{},
	}
}
Beispiel #6
0
// createUniqueOwnerEnvNameOp returns the operation needed to create
// an userenvnameC document with the given owner and environment name.
func createUniqueOwnerEnvNameOp(owner names.UserTag, envName string) txn.Op {
	return txn.Op{
		C:      userenvnameC,
		Id:     userEnvNameIndex(owner.Username(), envName),
		Assert: txn.DocMissing,
		Insert: bson.M{},
	}
}
Beispiel #7
0
func (s *modelManagerSuite) modifyAccess(c *gc.C, user names.UserTag, action params.ModelAction, access params.ModelAccessPermission, model names.ModelTag) error {
	args := params.ModifyModelAccessRequest{
		Changes: []params.ModifyModelAccess{{
			UserTag:  user.String(),
			Action:   action,
			Access:   access,
			ModelTag: model.String(),
		}}}
	result, err := s.modelmanager.ModifyModelAccess(args)
	c.Assert(err, jc.ErrorIsNil)
	return result.OneError()
}
Beispiel #8
0
func (s *modelManagerSuite) createArgs(c *gc.C, owner names.UserTag) params.ModelCreateArgs {
	return params.ModelCreateArgs{
		OwnerTag: owner.String(),
		Account:  make(map[string]interface{}),
		Config: map[string]interface{}{
			"name":            "test-model",
			"authorized-keys": "ssh-key",
			// And to make it a valid dummy config
			"state-server": false,
		},
	}
}
Beispiel #9
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()

	id := envUserID(st.EnvironTag().Id(), user.Username())
	err := envUsers.FindId(id).One(&envUser.doc)
	if err == mgo.ErrNotFound {
		return nil, errors.NotFoundf("envUser %q", user.Username())
	}
	return envUser, nil
}
Beispiel #10
0
func addTestingService(c *gc.C, st *State, series, name string, ch *Charm, owner names.UserTag, bindings map[string]string, storage map[string]StorageConstraints) *Service {
	c.Assert(ch, gc.NotNil)
	service, err := st.AddService(AddServiceArgs{
		Name:             name,
		Series:           series,
		Owner:            owner.String(),
		Charm:            ch,
		EndpointBindings: bindings,
		Storage:          storage,
	})
	c.Assert(err, jc.ErrorIsNil)
	return service
}
Beispiel #11
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
}
Beispiel #12
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, doc := createEnvUserOpAndDoc(envuuid, user, createdBy, displayName)
	err := st.runTransaction([]txn.Op{op})
	if err == txn.ErrAborted {
		err = errors.AlreadyExistsf("environment user %q", user.Username())
	}
	if err != nil {
		return nil, errors.Trace(err)
	}
	return &EnvironmentUser{st: st, doc: *doc}, nil
}
Beispiel #13
0
// createEnvironmentOp returns the operation needed to create
// an environment document with the given name and UUID.
func createEnvironmentOp(st *State, owner names.UserTag, name, uuid, server string) txn.Op {
	doc := &environmentDoc{
		UUID:       uuid,
		Name:       name,
		Life:       Alive,
		Owner:      owner.Username(),
		ServerUUID: server,
	}
	return txn.Op{
		C:      environmentsC,
		Id:     uuid,
		Assert: txn.DocMissing,
		Insert: doc,
	}
}
Beispiel #14
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.Username())
	err := envUsers.FindId(username).One(&envUser.doc)
	if err == mgo.ErrNotFound {
		return nil, errors.NotFoundf("environment user %q", user.Username())
	}
	// 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
}
Beispiel #15
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
}
Beispiel #16
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,
	}
}
Beispiel #17
0
// CreateLocalLoginMacaroon creates a local login macaroon for the
// authenticated user.
func (c *Client) CreateLocalLoginMacaroon(tag names.UserTag) (*macaroon.Macaroon, error) {
	args := params.Entities{Entities: []params.Entity{{tag.String()}}}
	var results params.MacaroonResults
	if err := c.facade.FacadeCall("CreateLocalLoginMacaroon", args, &results); err != nil {
		return nil, errors.Trace(err)
	}
	if n := len(results.Results); n != 1 {
		logger.Errorf("expected 1 result, got %#v", results)
		return nil, errors.Errorf("expected 1 result, got %d", n)
	}
	result := results.Results[0]
	if result.Error != nil {
		return nil, errors.Trace(result.Error)
	}
	return result.Result, nil
}
Beispiel #18
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(nil, fmt.Sprintf("model user %q does not exist", user.Canonical()))
	}
	if err != nil {
		return errors.Trace(err)
	}
	return nil
}
Beispiel #19
0
func createInitialUserOp(st *State, user names.UserTag, password string) txn.Op {
	doc := userDoc{
		Name:         user.Name(),
		DisplayName:  user.Name(),
		PasswordHash: password,
		// Empty PasswordSalt means utils.CompatSalt
		CreatedBy:   user.Name(),
		DateCreated: nowToTheSecond(),
	}
	return txn.Op{
		C:      usersC,
		Id:     doc.Name,
		Assert: txn.DocMissing,
		Insert: &doc,
	}
}
Beispiel #20
0
// RemoveEnvironmentUser removes a user from the database.
func (st *State) RemoveEnvironmentUser(user names.UserTag) error {
	ops := []txn.Op{{
		C:      envUsersC,
		Id:     envUserID(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.Username()))
	}
	if err != nil {
		return errors.Trace(err)
	}
	return nil
}
Beispiel #21
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
}
Beispiel #22
0
func createEnvUserOp(envuuid string, user, createdBy names.UserTag, displayName string) txn.Op {
	creatorname := createdBy.Username()
	doc := &envUserDoc{
		ID:          envUserID(user),
		EnvUUID:     envuuid,
		UserName:    user.Username(),
		DisplayName: displayName,
		CreatedBy:   creatorname,
		DateCreated: nowToTheSecond(),
	}
	return txn.Op{
		C:      envUsersC,
		Id:     envUserID(user),
		Assert: txn.DocMissing,
		Insert: doc,
	}
}
Beispiel #23
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,
	}
}
Beispiel #24
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,
	}
}
Beispiel #25
0
// IsSystemAdministrator returns true if the user specified has access to the
// state server environment (the system environment).
func (st *State) IsSystemAdministrator(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.EnvironmentTag.Id()

	envUsers, userCloser := st.getRawCollection(envUsersC)
	defer userCloser()

	count, err := envUsers.Find(bson.D{
		{"env-uuid", serverUUID},
		{"user", user.Username()},
	}).Count()
	if err != nil {
		return false, errors.Trace(err)
	}
	return count == 1, nil
}
Beispiel #26
0
func createEnvUserOpAndDoc(envuuid string, user, createdBy names.UserTag, displayName string) (txn.Op, *envUserDoc) {
	username := user.Username()
	creatorname := createdBy.Username()
	id := envUserID(envuuid, username)
	doc := &envUserDoc{
		ID:          id,
		EnvUUID:     envuuid,
		UserName:    username,
		DisplayName: displayName,
		CreatedBy:   creatorname,
		DateCreated: nowToTheSecond(),
	}
	op := txn.Op{
		C:      envUsersC,
		Id:     id,
		Assert: txn.DocMissing,
		Insert: doc,
	}
	return op, doc
}
Beispiel #27
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
}
Beispiel #28
0
func (st *State) envSetupOps(cfg *config.Config, envUUID, serverUUID string, owner names.UserTag) ([]txn.Op, error) {
	if err := checkEnvironConfig(cfg); err != nil {
		return nil, errors.Trace(err)
	}

	// When creating the state server environment, the new environment
	// UUID is also used as the state server UUID.
	if serverUUID == "" {
		serverUUID = envUUID
	}
	envUserOp := createEnvUserOp(envUUID, owner, owner, owner.Name(), false)
	ops := []txn.Op{
		createConstraintsOp(st, environGlobalKey, constraints.Value{}),
		createSettingsOp(environGlobalKey, cfg.AllAttrs()),
		incHostedEnvironCountOp(),
		createEnvironmentOp(st, owner, cfg.Name(), envUUID, serverUUID),
		createUniqueOwnerEnvNameOp(owner, cfg.Name()),
		envUserOp,
	}
	return ops, nil
}
Beispiel #29
0
func (st *State) envSetupOps(cfg *config.Config, modelUUID, serverUUID string, owner names.UserTag) ([]txn.Op, error) {
	if err := checkModelConfig(cfg); err != nil {
		return nil, errors.Trace(err)
	}

	// When creating the state server model, the new model
	// UUID is also used as the state server UUID.
	if serverUUID == "" {
		serverUUID = modelUUID
	}
	modelUserOp := createModelUserOp(modelUUID, owner, owner, owner.Name(), false)
	ops := []txn.Op{
		createConstraintsOp(st, modelGlobalKey, constraints.Value{}),
		createSettingsOp(modelGlobalKey, cfg.AllAttrs()),
		incHostedModelCountOp(),
		createModelOp(st, owner, cfg.Name(), modelUUID, serverUUID),
		createUniqueOwnerModelNameOp(owner, cfg.Name()),
		modelUserOp,
	}
	return ops, nil
}
Beispiel #30
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.Username())
		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.Username() == user.Username() {
		return nil
	}
	return common.ErrPerm
}