coder/cli/server_createadminuser_test.go

250 lines
7.3 KiB
Go

package cli_test
import (
"context"
"database/sql"
"fmt"
"runtime"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/coder/coder/cli/clitest"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/postgres"
"github.com/coder/coder/coderd/rbac"
"github.com/coder/coder/coderd/userpassword"
"github.com/coder/coder/pty/ptytest"
"github.com/coder/coder/testutil"
)
//nolint:paralleltest, tparallel
func TestServerCreateAdminUser(t *testing.T) {
const (
username = "dean"
email = "dean@example.com"
password = "SecurePa$$word123"
)
verifyUser := func(t *testing.T, dbURL, username, email, password string) {
t.Helper()
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
defer cancel()
sqlDB, err := sql.Open("postgres", dbURL)
require.NoError(t, err)
defer sqlDB.Close()
db := database.New(sqlDB)
pingCtx, pingCancel := context.WithTimeout(ctx, testutil.WaitShort)
defer pingCancel()
_, err = db.Ping(pingCtx)
require.NoError(t, err, "ping db")
user, err := db.GetUserByEmailOrUsername(ctx, database.GetUserByEmailOrUsernameParams{
Email: email,
})
require.NoError(t, err)
require.Equal(t, username, user.Username, "username does not match")
require.Equal(t, email, user.Email, "email does not match")
ok, err := userpassword.Compare(string(user.HashedPassword), password)
require.NoError(t, err)
require.True(t, ok, "password does not match")
require.EqualValues(t, []string{rbac.RoleOwner()}, user.RBACRoles, "user does not have owner role")
// Check that user is admin in every org.
orgs, err := db.GetOrganizations(ctx)
require.NoError(t, err)
orgIDs := make(map[uuid.UUID]struct{}, len(orgs))
for _, org := range orgs {
orgIDs[org.ID] = struct{}{}
}
orgMemberships, err := db.GetOrganizationMembershipsByUserID(ctx, user.ID)
require.NoError(t, err)
orgIDs2 := make(map[uuid.UUID]struct{}, len(orgMemberships))
for _, membership := range orgMemberships {
orgIDs2[membership.OrganizationID] = struct{}{}
assert.Equal(t, []string{rbac.RoleOrgAdmin(membership.OrganizationID)}, membership.Roles, "user is not org admin")
}
require.Equal(t, orgIDs, orgIDs2, "user is not in all orgs")
}
t.Run("OK", func(t *testing.T) {
t.Parallel()
if runtime.GOOS != "linux" || testing.Short() {
// Skip on non-Linux because it spawns a PostgreSQL instance.
t.SkipNow()
}
connectionURL, closeFunc, err := postgres.Open()
require.NoError(t, err)
defer closeFunc()
sqlDB, err := sql.Open("postgres", connectionURL)
require.NoError(t, err)
defer sqlDB.Close()
db := database.New(sqlDB)
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
defer cancel()
pingCtx, pingCancel := context.WithTimeout(ctx, testutil.WaitShort)
defer pingCancel()
_, err = db.Ping(pingCtx)
require.NoError(t, err, "ping db")
// Insert a few orgs.
org1Name, org1ID := "org1", uuid.New()
org2Name, org2ID := "org2", uuid.New()
_, err = db.InsertOrganization(ctx, database.InsertOrganizationParams{
ID: org1ID,
Name: org1Name,
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
})
require.NoError(t, err)
_, err = db.InsertOrganization(ctx, database.InsertOrganizationParams{
ID: org2ID,
Name: org2Name,
CreatedAt: database.Now(),
UpdatedAt: database.Now(),
})
require.NoError(t, err)
inv, _ := clitest.New(t,
"server", "create-admin-user",
"--postgres-url", connectionURL,
"--ssh-keygen-algorithm", "ed25519",
"--username", username,
"--email", email,
"--password", password,
)
pty := ptytest.New(t)
inv.Stdout = pty.Output()
inv.Stderr = pty.Output()
clitest.Start(t, inv)
pty.ExpectMatchContext(ctx, "Creating user...")
pty.ExpectMatchContext(ctx, "Generating user SSH key...")
pty.ExpectMatchContext(ctx, fmt.Sprintf("Adding user to organization %q (%s) as admin...", org1Name, org1ID.String()))
pty.ExpectMatchContext(ctx, fmt.Sprintf("Adding user to organization %q (%s) as admin...", org2Name, org2ID.String()))
pty.ExpectMatchContext(ctx, "User created successfully.")
pty.ExpectMatchContext(ctx, username)
pty.ExpectMatchContext(ctx, email)
pty.ExpectMatchContext(ctx, "****")
verifyUser(t, connectionURL, username, email, password)
})
t.Run("Env", func(t *testing.T) {
t.Parallel()
if runtime.GOOS != "linux" || testing.Short() {
// Skip on non-Linux because it spawns a PostgreSQL instance.
t.SkipNow()
}
connectionURL, closeFunc, err := postgres.Open()
require.NoError(t, err)
defer closeFunc()
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
defer cancel()
inv, _ := clitest.New(t, "server", "create-admin-user")
inv.Environ.Set("CODER_POSTGRES_URL", connectionURL)
inv.Environ.Set("CODER_SSH_KEYGEN_ALGORITHM", "ed25519")
inv.Environ.Set("CODER_USERNAME", username)
inv.Environ.Set("CODER_EMAIL", email)
inv.Environ.Set("CODER_PASSWORD", password)
pty := ptytest.New(t)
inv.Stdout = pty.Output()
inv.Stderr = pty.Output()
clitest.Start(t, inv)
pty.ExpectMatchContext(ctx, "User created successfully.")
pty.ExpectMatchContext(ctx, username)
pty.ExpectMatchContext(ctx, email)
pty.ExpectMatchContext(ctx, "****")
verifyUser(t, connectionURL, username, email, password)
})
t.Run("Stdin", func(t *testing.T) {
t.Parallel()
if runtime.GOOS != "linux" || testing.Short() {
// Skip on non-Linux because it spawns a PostgreSQL instance.
t.SkipNow()
}
connectionURL, closeFunc, err := postgres.Open()
require.NoError(t, err)
defer closeFunc()
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
defer cancel()
inv, _ := clitest.New(t,
"server", "create-admin-user",
"--postgres-url", connectionURL,
"--ssh-keygen-algorithm", "ed25519",
)
pty := ptytest.New(t).Attach(inv)
clitest.Start(t, inv)
pty.ExpectMatchContext(ctx, "Username")
pty.WriteLine(username)
pty.ExpectMatchContext(ctx, "Email")
pty.WriteLine(email)
pty.ExpectMatchContext(ctx, "Password")
pty.WriteLine(password)
pty.ExpectMatchContext(ctx, "Confirm password")
pty.WriteLine(password)
pty.ExpectMatchContext(ctx, "User created successfully.")
pty.ExpectMatchContext(ctx, username)
pty.ExpectMatchContext(ctx, email)
pty.ExpectMatchContext(ctx, "****")
verifyUser(t, connectionURL, username, email, password)
})
t.Run("Validates", func(t *testing.T) {
t.Parallel()
if runtime.GOOS != "linux" || testing.Short() {
// Skip on non-Linux because it spawns a PostgreSQL instance.
t.SkipNow()
}
connectionURL, closeFunc, err := postgres.Open()
require.NoError(t, err)
defer closeFunc()
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
root, _ := clitest.New(t,
"server", "create-admin-user",
"--postgres-url", connectionURL,
"--ssh-keygen-algorithm", "rsa4096",
"--username", "$",
"--email", "not-an-email",
"--password", "x",
)
pty := ptytest.New(t)
root.Stdout = pty.Output()
root.Stderr = pty.Output()
err = root.WithContext(ctx).Run()
require.Error(t, err)
require.ErrorContains(t, err, "'email' failed on the 'email' tag")
require.ErrorContains(t, err, "'username' failed on the 'username' tag")
})
}