Exemple #1
0
// 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
	}
	user := &User{
		st: st,
		doc: userDoc{
			Name:         name,
			DisplayName:  displayName,
			PasswordHash: utils.UserPasswordHash(password, salt),
			PasswordSalt: salt,
			CreatedBy:    creator,
			DateCreated:  nowToTheSecond(),
		},
	}
	ops := []txn.Op{{
		C:      usersC,
		Id:     name,
		Assert: txn.DocMissing,
		Insert: &user.doc,
	}}
	err = st.runTransaction(ops)
	if err == txn.ErrAborted {
		err = errors.New("user already exists")
	}
	if err != nil {
		return nil, errors.Trace(err)
	}
	return user, nil
}
Exemple #2
0
// RemoveUser removes a user.
func (api *UserManagerAPI) RemoveUser(args params.Entities) (params.ErrorResults, error) {
	result := params.ErrorResults{
		Results: make([]params.ErrorResult, len(args.Entities)),
	}
	if len(args.Entities) == 0 {
		return result, nil
	}
	for i, arg := range args.Entities {
		if !names.IsValidUserName(arg.Tag) {
			result.Results[i].Error = common.ServerError(errors.Errorf("%q is not a valid username", arg.Tag))
			continue
		}
		user, err := api.state.User(names.NewLocalUserTag(arg.Tag))
		if err != nil {
			result.Results[i].Error = common.ServerError(common.ErrPerm)
			continue
		}
		err = user.Deactivate()
		if err != nil {
			result.Results[i].Error = common.ServerError(fmt.Errorf("Failed to remove user: %s", err))
			continue
		}
	}
	return result, nil
}
Exemple #3
0
func (s *userSuite) TestIsValidUserNameOrDomain(c *gc.C) {
	for i, t := range []struct {
		string string
		expect bool
	}{
		{"", false},
		{"bob", true},
		{"Bob", true},
		{"bOB", true},
		{"b^b", false},
		{"bob1", true},
		{"bob-1", true},
		{"bob+1", true},
		{"bob+", false},
		{"+bob", false},
		{"bob.1", true},
		{"1bob", true},
		{"1-bob", true},
		{"1+bob", true},
		{"1.bob", true},
		{"jim.bob+99-1.", false},
		{"a", false},
		{"0foo", true},
		{"foo bar", false},
		{"bar{}", false},
		{"bar+foo", true},
		{"bar_foo", false},
		{"bar!", false},
		{"bar^", false},
		{"bar*", false},
		{"foo=bar", false},
		{"foo?", false},
		{"[bar]", false},
		{"'foo'", false},
		{"%bar", false},
		{"&bar", false},
		{"#1foo", false},
		{"[email protected]", false},
		{"bar@local", false},
		{"bar@ubuntuone", false},
		{"bar@", false},
		{"@local", false},
		{"not/valid", false},
	} {
		c.Logf("test %d: %s", i, t.string)
		c.Assert(names.IsValidUserName(t.string), gc.Equals, t.expect, gc.Commentf("%s", t.string))
		c.Assert(names.IsValidUserDomain(t.string), gc.Equals, t.expect, gc.Commentf("%s", t.string))
	}
}
Exemple #4
0
func (c *Client) userCall(username string, methodCall string) error {
	if !names.IsValidUserName(username) {
		return errors.Errorf("%q is not a valid username", username)
	}
	tag := names.NewLocalUserTag(username)

	var results params.ErrorResults
	args := params.Entities{
		[]params.Entity{{tag.String()}},
	}
	err := c.facade.FacadeCall(methodCall, args, &results)
	if err != nil {
		return errors.Trace(err)
	}
	return results.OneError()
}
Exemple #5
0
// SetPassword changes the password for the specified user.
func (c *Client) SetPassword(username, password string) error {
	if !names.IsValidUserName(username) {
		return errors.Errorf("%q is not a valid username", username)
	}
	tag := names.NewLocalUserTag(username)
	args := params.EntityPasswords{
		Changes: []params.EntityPassword{{
			Tag:      tag.String(),
			Password: password}},
	}
	var results params.ErrorResults
	err := c.facade.FacadeCall("SetPassword", args, &results)
	if err != nil {
		return err
	}
	return results.OneError()
}
Exemple #6
0
// UserInfo returns information about the specified users.  If no users are
// specified, the call should return all users.  If includeDisabled is set to
// ActiveUsers, only enabled users are returned.
func (c *Client) UserInfo(usernames []string, all IncludeDisabled) ([]params.UserInfo, error) {
	var results params.UserInfoResults
	var entities []params.Entity
	for _, username := range usernames {
		if !names.IsValidUserName(username) {
			return nil, errors.Errorf("%q is not a valid username", username)
		}
		tag := names.NewLocalUserTag(username)
		entities = append(entities, params.Entity{Tag: tag.String()})
	}
	args := params.UserInfoRequest{
		Entities:        entities,
		IncludeDisabled: bool(all),
	}
	err := c.facade.FacadeCall("UserInfo", args, &results)
	if err != nil {
		return nil, errors.Trace(err)
	}
	// Only need to look for errors if users were explicitly specified, because
	// if we didn't ask for any, we should get all, and we shouldn't get any
	// errors for listing all.  We care here because we index into the users
	// slice.
	if len(results.Results) == len(usernames) {
		var errorStrings []string
		for i, result := range results.Results {
			if result.Error != nil {
				annotated := errors.Annotate(result.Error, usernames[i])
				errorStrings = append(errorStrings, annotated.Error())
			}
		}
		if len(errorStrings) > 0 {
			return nil, errors.New(strings.Join(errorStrings, ", "))
		}
	}
	info := []params.UserInfo{}
	for i, result := range results.Results {
		if result.Result == nil {
			return nil, errors.Errorf("unexpected nil result at position %d", i)
		}
		info = append(info, *result.Result)
	}
	return info, nil
}
Exemple #7
0
func (api *UserManagerAPI) SetPassword(args ModifyUsers) (params.ErrorResults, error) {
	result := params.ErrorResults{
		Results: make([]params.ErrorResult, len(args.Changes)),
	}
	if len(args.Changes) == 0 {
		return result, nil
	}
	for i, arg := range args.Changes {
		loggedInUser := api.getLoggedInUser()
		if _, ok := loggedInUser.(names.UserTag); !ok {
			result.Results[i].Error = common.ServerError(fmt.Errorf("Not a user"))
			continue
		}

		username := arg.Username
		if username == "" {
			username = arg.Tag
		}

		if !names.IsValidUserName(username) {
			result.Results[i].Error = common.ServerError(errors.Errorf("%q is not a valid username", arg.Tag))
			continue
		}

		argUser, err := api.state.User(names.NewLocalUserTag(username))
		if err != nil {
			result.Results[i].Error = common.ServerError(fmt.Errorf("Failed to find user %v", err))
			continue
		}

		if loggedInUser != argUser.Tag() {
			result.Results[i].Error = common.ServerError(fmt.Errorf("Can only change the password of the current user (%s)", loggedInUser.Id()))
			continue
		}

		err = argUser.SetPassword(arg.Password)
		if err != nil {
			result.Results[i].Error = common.ServerError(fmt.Errorf("Failed to set password %v", err))
			continue
		}
	}
	return result, nil
}
Exemple #8
0
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)

	user := &User{
		st: st,
		doc: userDoc{
			DocID:       nameToLower,
			Name:        name,
			DisplayName: displayName,
			SecretKey:   secretKey,
			CreatedBy:   creator,
			DateCreated: nowToTheSecond(),
		},
	}

	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,
	}}
	err := st.runTransaction(ops)
	if err == txn.ErrAborted {
		err = errors.AlreadyExistsf("user")
	}
	if err != nil {
		return nil, errors.Trace(err)
	}
	return user, nil
}
Exemple #9
0
// Authenticate authenticates the provided entity. If there is no macaroon provided, it will
// return a *DischargeRequiredError containing a macaroon that can be used to grant access.
func (m *ExternalMacaroonAuthenticator) Authenticate(entityFinder EntityFinder, _ names.Tag, req params.LoginRequest) (state.Entity, error) {
	declared, err := m.Service.CheckAny(req.Macaroons, nil, checkers.New(checkers.TimeBefore))
	if _, ok := errors.Cause(err).(*bakery.VerificationError); ok {
		return nil, m.newDischargeRequiredError(err)
	}
	if err != nil {
		return nil, errors.Trace(err)
	}
	username := declared[usernameKey]
	var tag names.UserTag
	if names.IsValidUserName(username) {
		// The name is a local name without an explicit @local suffix.
		// In this case, for compatibility with 3rd parties that don't
		// care to add their own domain, we add an @external domain
		// to ensure there is no confusion between local and external
		// users.
		// TODO(rog) remove this logic when deployed dischargers
		// always add an @ domain.
		tag = names.NewLocalUserTag(username).WithDomain("external")
	} else {
		// We have a name with an explicit domain (or an invalid user name).
		if !names.IsValidUser(username) {
			return nil, errors.Errorf("%q is an invalid user name", username)
		}
		tag = names.NewUserTag(username)
		if tag.IsLocal() {
			return nil, errors.Errorf("external identity provider has provided ostensibly local name %q", username)
		}
	}
	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
}
// Run implements Command.Run.
func (c *changePasswordCommand) Run(ctx *cmd.Context) error {
	if c.api == nil {
		api, err := c.NewUserManagerAPIClient()
		if err != nil {
			return errors.Trace(err)
		}
		c.api = api
		defer c.api.Close()
	}

	newPassword, err := generateOrReadPassword(ctx, c.Generate)
	if err != nil {
		return errors.Trace(err)
	}

	var accountName string
	controllerName := c.ControllerName()
	store := c.ClientStore()
	if c.User != "" {
		if !names.IsValidUserName(c.User) {
			return errors.NotValidf("user name %q", c.User)
		}
		accountName = names.NewUserTag(c.User).Canonical()
	} else {
		accountName, err = store.CurrentAccount(controllerName)
		if err != nil {
			return errors.Trace(err)
		}
	}
	accountDetails, err := store.AccountByName(controllerName, accountName)
	if err != nil && !errors.IsNotFound(err) {
		return errors.Trace(err)
	}

	if accountDetails != nil && accountDetails.Macaroon == "" {
		// Generate a macaroon first to guard against I/O failures
		// occurring after the password has been changed, preventing
		// future logins.
		userTag := names.NewUserTag(accountName)
		macaroon, err := c.api.CreateLocalLoginMacaroon(userTag)
		if err != nil {
			return errors.Trace(err)
		}
		accountDetails.Password = ""

		// TODO(axw) update jujuclient with code for marshalling
		// and unmarshalling macaroons as YAML.
		macaroonJSON, err := macaroon.MarshalJSON()
		if err != nil {
			return errors.Trace(err)
		}
		accountDetails.Macaroon = string(macaroonJSON)

		if err := store.UpdateAccount(controllerName, accountName, *accountDetails); err != nil {
			return errors.Annotate(err, "failed to update client credentials")
		}
	}

	if err := c.api.SetPassword(accountName, newPassword); err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}
	if accountDetails == nil {
		ctx.Infof("Password for %q has been updated.", c.User)
	} else {
		ctx.Infof("Your password has been updated.")
	}
	return nil
}
Exemple #11
0
// Run implements Command.Run.
func (c *loginCommand) Run(ctx *cmd.Context) error {
	controllerName := c.ControllerName()
	store := c.ClientStore()

	user := c.User
	if user == "" {
		// The username has not been specified, so prompt for it.
		fmt.Fprint(ctx.Stderr, "username: "******"" {
			return errors.Errorf("you must specify a username")
		}
	}
	if !names.IsValidUserName(user) {
		return errors.NotValidf("user name %q", user)
	}
	userTag := names.NewUserTag(user)
	accountName := userTag.Canonical()

	// Make sure that the client is not already logged in,
	// or if it is, that it is logged in as the specified
	// user.
	currentAccountName, err := store.CurrentAccount(controllerName)
	if err == nil {
		if currentAccountName != accountName {
			return errors.New(`already logged in

Run "juju logout" first before attempting to log in as a different user.
`)
		}
	} else if !errors.IsNotFound(err) {
		return errors.Trace(err)
	}
	accountDetails, err := store.AccountByName(controllerName, accountName)
	if err != nil && !errors.IsNotFound(err) {
		return errors.Trace(err)
	}

	// Read password from the terminal, and attempt to log in using that.
	password, err := readAndConfirmPassword(ctx)
	if err != nil {
		return errors.Trace(err)
	}
	params, err := c.NewAPIConnectionParams(store, controllerName, "", "")
	if err != nil {
		return errors.Trace(err)
	}
	if accountDetails != nil {
		accountDetails.Password = password
	} else {
		accountDetails = &jujuclient.AccountDetails{
			User:     accountName,
			Password: password,
		}
	}
	params.AccountDetails = accountDetails
	api, err := c.newLoginAPI(params)
	if err != nil {
		return errors.Annotate(err, "creating API connection")
	}
	defer api.Close()

	// Create a new local login macaroon, and update the account details
	// in the client store, removing the recorded password (if any) and
	// storing the macaroon.
	macaroon, err := api.CreateLocalLoginMacaroon(userTag)
	if err != nil {
		return errors.Annotate(err, "failed to create a temporary credential")
	}
	macaroonJSON, err := macaroon.MarshalJSON()
	if err != nil {
		return errors.Annotate(err, "marshalling temporary credential to JSON")
	}
	accountDetails.Password = ""
	accountDetails.Macaroon = string(macaroonJSON)
	if err := store.UpdateAccount(controllerName, accountName, *accountDetails); err != nil {
		return errors.Annotate(err, "failed to record temporary credential")
	}
	if err := store.SetCurrentAccount(controllerName, accountName); err != nil {
		return errors.Annotate(err, "failed to set current account")
	}
	ctx.Infof("You are now logged in to %q as %q.", controllerName, accountName)
	return nil
}