feat: backend support for creating and storing service accounts (#22698)

Add is_service_account column to users table with CHECK constraints
enforcing login_type='none' and empty email for service accounts.
Update user creation API to validate service account constraints.

Related to:
https://linear.app/codercom/issue/PLAT-27/feat-backend-support-for-creating-and-storing-service-accounts
This commit is contained in:
George K
2026-03-11 10:19:08 -07:00
committed by GitHub
parent e96cd5cbb2
commit e5c19d0af4
28 changed files with 522 additions and 87 deletions
+78
View File
@@ -1854,6 +1854,84 @@ func TestUpdateSystemUser(t *testing.T) {
require.NoError(t, err)
}
func TestInsertUserServiceAccountConstraints(t *testing.T) {
t.Parallel()
db, _ := dbtestutil.NewDB(t)
// Happy path: should succeed.
t.Run("ServiceAccountWithEmptyEmailAndLoginNone", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitLong)
user, err := db.InsertUser(ctx, database.InsertUserParams{
Email: "",
LoginType: database.LoginTypeNone,
ID: uuid.New(),
Username: "sa-ok",
RBACRoles: []string{},
IsServiceAccount: true,
})
require.NoError(t, err)
require.True(t, user.IsServiceAccount)
require.Empty(t, user.Email)
})
// Service account with a non-empty email should be rejected
// by the users_email_not_empty constraint.
t.Run("ServiceAccountWithNonEmptyEmail", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitLong)
_, err := db.InsertUser(ctx, database.InsertUserParams{
Email: "sa@coder.com",
LoginType: database.LoginTypeNone,
ID: uuid.New(),
Username: "sa-with-email",
RBACRoles: []string{},
IsServiceAccount: true,
})
require.Error(t, err)
require.True(t, database.IsCheckViolation(err, database.CheckUsersEmailNotEmpty))
})
// A non-service-account with empty email should be rejected
// by the users_email_not_empty constraint.
t.Run("RegularUserWithEmptyEmail", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitLong)
_, err := db.InsertUser(ctx, database.InsertUserParams{
Email: "",
LoginType: database.LoginTypePassword,
ID: uuid.New(),
Username: "regular-no-email",
RBACRoles: []string{},
IsServiceAccount: false,
})
require.Error(t, err)
require.True(t, database.IsCheckViolation(err, database.CheckUsersEmailNotEmpty))
})
// Service account with login_type!=none should be rejected
// by the users_service_account_login_type constraint.
t.Run("ServiceAccountWithPasswordLoginType", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitLong)
_, err := db.InsertUser(ctx, database.InsertUserParams{
Email: "",
LoginType: database.LoginTypePassword,
ID: uuid.New(),
Username: "sa-with-password",
RBACRoles: []string{},
IsServiceAccount: true,
})
require.Error(t, err)
require.True(t, database.IsCheckViolation(err, database.CheckUsersServiceAccountLoginType))
})
}
func TestUserChangeLoginType(t *testing.T) {
t.Parallel()
if testing.Short() {