// AddUser adds a user to the state. func (st *State) AddUser(username, displayName, password, creator string) (*User, error) { if !names.IsValidUser(username) { return nil, errors.Errorf("invalid user name %q", username) } salt, err := utils.RandomSalt() if err != nil { return nil, err } timestamp := time.Now().Round(time.Second).UTC() u := &User{ st: st, doc: userDoc{ Name: username, DisplayName: displayName, PasswordHash: utils.UserPasswordHash(password, salt), PasswordSalt: salt, CreatedBy: creator, DateCreated: timestamp, }, } ops := []txn.Op{{ C: usersC, Id: username, Assert: txn.DocMissing, Insert: &u.doc, }} err = st.runTransaction(ops) if err == txn.ErrAborted { err = errors.New("user already exists") } if err != nil { return nil, errors.Trace(err) } return u, nil }
// SetPassword sets the password associated with the Identity. func (i *Identity) SetPassword(password string) error { salt, err := utils.RandomSalt() if err != nil { return err } return i.setPasswordHash(utils.UserPasswordHash(password, salt), salt) }
// SetPassword sets the password associated with the user. func (u *User) SetPassword(password string) error { salt, err := utils.RandomSalt() if err != nil { return err } return u.SetPasswordHash(utils.UserPasswordHash(password, salt), salt) }
func (*passwordSuite) TestRandomSalt(c *gc.C) { salt, err := utils.RandomSalt() c.Assert(err, gc.IsNil) if len(salt) < 12 { c.Errorf("salt too short: %q", salt) } // check we're not adding base64 padding. c.Assert(salt, gc.Matches, base64Chars) }
// SetPassword sets the password associated with the User. func (u *User) SetPassword(password string) error { if err := u.ensureNotDeleted(); err != nil { return errors.Annotate(err, "cannot set password") } salt, err := utils.RandomSalt() if err != nil { return err } return u.SetPasswordHash(utils.UserPasswordHash(password, salt), salt) }
func (st *State) addUser(name, displayName, password, creator string, secretKey []byte) (*User, error) { if !names.IsValidUserName(name) { return nil, errors.Errorf("invalid user name %q", name) } nameToLower := strings.ToLower(name) dateCreated := st.NowToTheSecond() user := &User{ st: st, doc: userDoc{ DocID: nameToLower, Name: name, DisplayName: displayName, SecretKey: secretKey, CreatedBy: creator, DateCreated: dateCreated, }, } if password != "" { salt, err := utils.RandomSalt() if err != nil { return nil, err } user.doc.PasswordHash = utils.UserPasswordHash(password, salt) user.doc.PasswordSalt = salt } ops := []txn.Op{{ C: usersC, Id: nameToLower, Assert: txn.DocMissing, Insert: &user.doc, }} controllerUserOps := createControllerUserOps(st.ControllerUUID(), names.NewUserTag(name), names.NewUserTag(creator), displayName, dateCreated, defaultControllerPermission) ops = append(ops, controllerUserOps...) err := st.runTransaction(ops) if err == txn.ErrAborted { err = errors.AlreadyExistsf("user") } if err != nil { return nil, errors.Trace(err) } return user, nil }
func (s *UserSuite) TestSetPasswordHash(c *gc.C) { user := s.Factory.MakeUser(c, nil) salt, err := utils.RandomSalt() c.Assert(err, jc.ErrorIsNil) err = user.SetPasswordHash(utils.UserPasswordHash("foo", salt), salt) c.Assert(err, jc.ErrorIsNil) c.Assert(user.PasswordValid("foo"), jc.IsTrue) c.Assert(user.PasswordValid("bar"), jc.IsFalse) // User passwords should *not* use the fast PasswordHash function hash := utils.AgentPasswordHash("foo-12345678901234567890") c.Assert(err, jc.ErrorIsNil) err = user.SetPasswordHash(hash, "") c.Assert(err, jc.ErrorIsNil) c.Assert(user.PasswordValid("foo-12345678901234567890"), jc.IsFalse) }
// AddIdentity adds an identity to the database. func (st *State) AddIdentity(name, displayName, password, creator string) (*Identity, error) { // The name of the identity happens to match the regex we use to confirm user names. // Identities do not have tags, so there is no special function for identities. Given // the relationships between users and identities it seems reasonable to use the same // validation check. if !names.IsValidUser(name) { return nil, errors.Errorf("invalid identity name %q", name) } salt, err := utils.RandomSalt() if err != nil { return nil, err } identity := &Identity{ st: st, doc: identityDoc{ Name: name, DisplayName: displayName, PasswordHash: utils.UserPasswordHash(password, salt), PasswordSalt: salt, CreatedBy: creator, DateCreated: nowToTheSecond(), }, } ops := []txn.Op{{ C: identityCollectionName, Id: name, Assert: txn.DocMissing, Insert: &identity.doc, }} err = st.runTransaction(ops) if err == txn.ErrAborted { err = errors.New("identity already exists") } if err != nil { return nil, errors.Trace(err) } return identity, nil }
// AddUser adds a user to the database. func (st *State) AddUser(name, displayName, password, creator string) (*User, error) { if !names.IsValidUserName(name) { return nil, errors.Errorf("invalid user name %q", name) } salt, err := utils.RandomSalt() if err != nil { return nil, err } nameToLower := strings.ToLower(name) user := &User{ st: st, doc: userDoc{ DocID: nameToLower, Name: name, DisplayName: displayName, PasswordHash: utils.UserPasswordHash(password, salt), PasswordSalt: salt, CreatedBy: creator, DateCreated: nowToTheSecond(), }, } ops := []txn.Op{{ C: usersC, Id: nameToLower, Assert: txn.DocMissing, Insert: &user.doc, }} err = st.runTransaction(ops) if err == txn.ErrAborted { err = errors.AlreadyExistsf("user") } if err != nil { return nil, errors.Trace(err) } return user, nil }
// Initialize sets up an initial empty state and returns it. // This needs to be performed only once for the initial controller model. // It returns unauthorizedError if access is unauthorized. func Initialize(args InitializeParams) (_ *State, err error) { if err := args.Validate(); err != nil { return nil, errors.Annotate(err, "validating initialization args") } // When creating the controller model, the new model // UUID is also used as the controller UUID. modelTag := names.NewModelTag(args.ControllerModelArgs.Config.UUID()) st, err := open(modelTag, args.MongoInfo, args.MongoDialOpts, args.NewPolicy, args.Clock) if err != nil { return nil, errors.Trace(err) } defer func() { if err != nil { if closeErr := st.Close(); closeErr != nil { logger.Errorf("error closing state while aborting Initialize: %v", closeErr) } } }() st.controllerModelTag = modelTag // A valid model is used as a signal that the // state has already been initalized. If this is the case // do nothing. if _, err := st.Model(); err == nil { return nil, errors.New("already initialized") } else if !errors.IsNotFound(err) { return nil, errors.Trace(err) } logger.Infof("initializing controller model %s", modelTag.Id()) modelOps, err := st.modelSetupOps( args.ControllerConfig.ControllerUUID(), args.ControllerModelArgs, &lineage{ ControllerConfig: args.ControllerInheritedConfig, RegionConfig: args.RegionInheritedConfig, }) if err != nil { return nil, errors.Trace(err) } salt, err := utils.RandomSalt() if err != nil { return nil, err } dateCreated := st.NowToTheSecond() ops := createInitialUserOps( args.ControllerConfig.ControllerUUID(), args.ControllerModelArgs.Owner, args.MongoInfo.Password, salt, dateCreated, ) ops = append(ops, txn.Op{ C: controllersC, Id: modelGlobalKey, Assert: txn.DocMissing, Insert: &controllersDoc{ CloudName: args.CloudName, ModelUUID: st.ModelUUID(), }, }, createCloudOp(args.Cloud, args.CloudName), txn.Op{ C: controllersC, Id: apiHostPortsKey, Assert: txn.DocMissing, Insert: &apiHostPortsDoc{}, }, txn.Op{ C: controllersC, Id: stateServingInfoKey, Assert: txn.DocMissing, Insert: &StateServingInfo{}, }, txn.Op{ C: controllersC, Id: hostedModelCountKey, Assert: txn.DocMissing, Insert: &hostedModelCountDoc{}, }, createSettingsOp(controllersC, controllerSettingsGlobalKey, args.ControllerConfig), createSettingsOp(globalSettingsC, controllerInheritedSettingsGlobalKey, args.ControllerInheritedConfig), ) for k, v := range args.Cloud.RegionConfig { // Create an entry keyed on cloudname#<key>, value for each region in // region-config. The values here are themselves // map[string]interface{}. ops = append(ops, createSettingsOp(globalSettingsC, regionSettingsGlobalKey(args.CloudName, k), v)) } for tag, cred := range args.CloudCredentials { ops = append(ops, createCloudCredentialOp(tag, cred)) } ops = append(ops, modelOps...) if err := st.runTransaction(ops); err != nil { return nil, errors.Trace(err) } controllerTag := names.NewControllerTag(args.ControllerConfig.ControllerUUID()) if err := st.start(controllerTag); err != nil { return nil, errors.Trace(err) } return st, nil }
// Initialize sets up an initial empty state and returns it. // This needs to be performed only once for the initial controller model. // It returns unauthorizedError if access is unauthorized. func Initialize(owner names.UserTag, info *mongo.MongoInfo, cfg *config.Config, opts mongo.DialOpts, policy Policy) (_ *State, err error) { uuid := cfg.UUID() modelTag := names.NewModelTag(uuid) st, err := open(modelTag, info, opts, policy) if err != nil { return nil, errors.Trace(err) } defer func() { if err != nil { if closeErr := st.Close(); closeErr != nil { logger.Errorf("error closing state while aborting Initialize: %v", closeErr) } } }() // A valid model is used as a signal that the // state has already been initalized. If this is the case // do nothing. if _, err := st.Model(); err == nil { return nil, errors.New("already initialized") } else if !errors.IsNotFound(err) { return nil, errors.Trace(err) } // When creating the controller model, the new model // UUID is also used as the controller UUID. logger.Infof("initializing controller model %s", uuid) modelOps, err := st.modelSetupOps(cfg, uuid, uuid, owner, MigrationModeActive) if err != nil { return nil, errors.Trace(err) } salt, err := utils.RandomSalt() if err != nil { return nil, err } ops := []txn.Op{ createInitialUserOp(st, owner, info.Password, salt), txn.Op{ C: controllersC, Id: modelGlobalKey, Assert: txn.DocMissing, Insert: &controllersDoc{ ModelUUID: st.ModelUUID(), }, }, txn.Op{ C: controllersC, Id: apiHostPortsKey, Assert: txn.DocMissing, Insert: &apiHostPortsDoc{}, }, txn.Op{ C: controllersC, Id: stateServingInfoKey, Assert: txn.DocMissing, Insert: &StateServingInfo{}, }, txn.Op{ C: controllersC, Id: hostedModelCountKey, Assert: txn.DocMissing, Insert: &hostedModelCountDoc{}, }, } ops = append(ops, modelOps...) if err := st.runTransaction(ops); err != nil { return nil, errors.Trace(err) } if err := st.start(modelTag); err != nil { return nil, errors.Trace(err) } return st, nil }