// 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 }
// 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 }
// 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{}, } }
// 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.Username()) } user := &User{st: st} if err := st.getUser(tag.Name(), &user.doc); err != nil { return nil, errors.Trace(err) } return user, nil }
// 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 }
// 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 }
// 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, } }
// 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 }
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, } }
// 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.Provider() == names.LocalProvider { if _, err := st.User(user.Name()); err != nil { return nil, errors.Annotate(err, fmt.Sprintf("user %q does not exist locally", user.Name())) } } // Ensure local createdBy user exists. if createdBy.Provider() == names.LocalProvider { if _, err := st.User(createdBy.Name()); err != nil { return nil, errors.Annotate(err, fmt.Sprintf("createdBy user %q does not exist locally", createdBy.Name())) } } username := user.Username() envuuid := st.EnvironTag().Id() id := envUserID(envuuid, username) envUser := &EnvironmentUser{ st: st, doc: envUserDoc{ ID: id, EnvUUID: envuuid, UserName: username, DisplayName: displayName, CreatedBy: createdBy.Username(), DateCreated: nowToTheSecond(), }} ops := []txn.Op{{ C: envUsersC, Id: id, Assert: txn.DocMissing, Insert: &envUser.doc, }} err := st.runTransaction(ops) if err == txn.ErrAborted { err = errors.New("env user already exists") } if err != nil { return nil, errors.Trace(err) } return envUser, nil }
// 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 }
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 }
// 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 }
// envUserID returns the document id of the environment user func envUserID(user names.UserTag) string { username := user.Username() return strings.ToLower(username) }
// NewEnvironment creates a new environment with its own UUID and // prepares it for use. Environment and State instances for the new // environment are returned. // // The state server environment's UUID is attached to the new // environment's document. Having the server UUIDs stored with each // environment document means that we have a way to represent external // environments, perhaps for future use around cross environment // relations. func (st *State) NewEnvironment(cfg *config.Config, owner names.UserTag) (_ *Environment, _ *State, err error) { if owner.IsLocal() { if _, err := st.User(owner); err != nil { return nil, nil, errors.Annotate(err, "cannot create environment") } } ssEnv, err := st.StateServerEnvironment() if err != nil { return nil, nil, errors.Annotate(err, "could not load state server environment") } uuid, ok := cfg.UUID() if !ok { return nil, nil, errors.Errorf("environment uuid was not supplied") } newState, err := st.ForEnviron(names.NewEnvironTag(uuid)) if err != nil { return nil, nil, errors.Annotate(err, "could not create state for new environment") } 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 environment") } err = newState.runTransactionNoEnvAliveAssert(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. environments, closer := st.getCollection(environmentsC) defer closer() envCount, countErr := environments.Find(bson.D{ {"owner", owner.Username()}, {"name", cfg.Name()}}, ).Count() if countErr != nil { err = errors.Trace(countErr) } else if envCount > 0 { err = errors.AlreadyExistsf("environment %q for %s", cfg.Name(), owner.Username()) } else { err = errors.New("environment already exists") } } if err != nil { return nil, nil, errors.Trace(err) } newEnv, err := newState.Environment() if err != nil { return nil, nil, errors.Trace(err) } return newEnv, newState, nil }