Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cli/usercreate.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ func (r *RootCmd) userCreate() *serpent.Command {
if disableLogin && loginType != "" {
return xerrors.New("You cannot specify both --disable-login and --login-type")
}
if disableLogin && !serviceAccount {
return xerrors.New("--disable-login is deprecated. Use --service-account for machine-to-machine access.")
}
if loginType == string(codersdk.LoginTypeNone) && !serviceAccount {
return xerrors.New("Login type 'none' requires --service-account.")
}

client, err := r.InitClient(inv)
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-- Irreversible: original login_type and emails aren't recorded, nothing to restore.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- Convert legacy login_type='none' users into service accounts.
-- Service accounts must have an empty email (see 000433), so this blanks the
-- email of each converted user. Destructive and one-way. System users skipped.
UPDATE users
SET is_service_account = true, email = ''
WHERE login_type = 'none' AND is_service_account = false AND is_system = false;
16 changes: 11 additions & 5 deletions coderd/userauth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,19 @@ func TestUserLogin(t *testing.T) {

t.Run("LoginTypeNone", func(t *testing.T) {
t.Parallel()
anotherClient, anotherUser := coderdtest.CreateAnotherUserMutators(t, client, user.OrganizationID, nil, func(r *codersdk.CreateUserRequestWithOrgs) {
r.Password = ""
r.UserLoginType = codersdk.LoginTypeNone
client, db := coderdtest.NewWithDatabase(t, nil)
first := coderdtest.CreateFirstUser(t, client)

noneUser := dbgen.User(t, db, database.User{
LoginType: database.LoginTypeNone,
})
dbgen.OrganizationMember(t, db, database.OrganizationMember{
OrganizationID: first.OrganizationID,
UserID: noneUser.ID,
})

_, err := anotherClient.LoginWithPassword(context.Background(), codersdk.LoginWithPasswordRequest{
Email: anotherUser.Email,
_, err := client.LoginWithPassword(context.Background(), codersdk.LoginWithPasswordRequest{
Email: noneUser.Email,
Password: "SomeSecurePassword!",
})
require.Error(t, err)
Expand Down
7 changes: 7 additions & 0 deletions coderd/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,13 @@ func (api *API) postUser(rw http.ResponseWriter, r *http.Request) {
req.UserLoginType = codersdk.LoginTypePassword
}

if !req.ServiceAccount && req.UserLoginType == codersdk.LoginTypeNone {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Login type 'none' requires a service account.",
})
return
}

if req.UserLoginType != codersdk.LoginTypePassword && req.Password != "" {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: fmt.Sprintf("Password cannot be set for non-password (%q) authentication.", req.UserLoginType),
Expand Down
11 changes: 5 additions & 6 deletions coderd/users_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -952,18 +952,17 @@ func TestPostUsers(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()

user, err := client.CreateUserWithOrgs(ctx, codersdk.CreateUserRequestWithOrgs{
_, err := client.CreateUserWithOrgs(ctx, codersdk.CreateUserRequestWithOrgs{
OrganizationIDs: []uuid.UUID{first.OrganizationID},
Email: "another@user.org",
Username: "someone-else",
Password: "",
UserLoginType: codersdk.LoginTypeNone,
})
require.NoError(t, err)

found, err := client.User(ctx, user.ID.String())
require.NoError(t, err)
require.Equal(t, found.LoginType, codersdk.LoginTypeNone)
var apiErr *codersdk.Error
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
require.Contains(t, apiErr.Message, "service account")
})

t.Run("CreateOIDCLoginType", func(t *testing.T) {
Expand Down
Loading