Files
zitadel/backend/v3/storage/database/repository/user_machine.go
T
5df8a97f9d feat(repository): implement user repository (#10967)
## Summary

This PR introduces the relational user repository for backend v3,
including schema, repository APIs, reducers/projections, and tests for
both human and machine users.

## What changed

* Added user-focused relational migrations (users, verifications,
metadata, PATs, machine keys, passkeys, identity provider links) and
related FK wiring.
* Implemented/expanded repository domain contracts and storage
implementations for human and machine user flows.
* Added support for machine secret handling and aligned access token
type modeling.
* Extended user domain models (verifications, password/email/phone/TOTP
state, passkeys, metadata, invite handling, identity provider links).
* Updated projection/reduction coverage and integration tests for key
user lifecycle events.
* Regenerated and updated mocks/enums plus supporting database
change/column utilities.

## Notes for reviewers

* This PR is large because it establishes the foundational relational
user storage layer and related test scaffolding. Sorry for that.

## Closes

Closes #10218
Closes #10224
Closes #10225
Closes #10209
Closes #10802

---------

Co-authored-by: Iraq Jaber <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Marco A. <[email protected]>
Co-authored-by: Tim Möhlmann <[email protected]>
2026-02-23 16:03:47 +01:00

104 lines
3.5 KiB
Go

package repository
import (
"context"
"github.com/zitadel/zitadel/backend/v3/domain"
"github.com/zitadel/zitadel/backend/v3/storage/database"
)
type userMachine struct {
user
machineKey
userPersonalAccessToken
}
func (u userMachine) create(ctx context.Context, builder *database.StatementBuilder, client database.QueryExecutor, user *domain.User) error {
changes := make(database.Changes, 0, len(user.Machine.Keys)+len(user.Machine.PATs))
for _, key := range user.Machine.Keys {
changes = append(changes, u.AddKey(key))
}
for _, pat := range user.Machine.PATs {
changes = append(changes, u.AddPersonalAccessToken(pat))
}
for _, metadata := range user.Metadata {
changes = append(changes, u.SetMetadata(metadata))
}
for i, change := range changes {
sessionCTE(change, i, 0, builder)
}
builder.WriteString("INSERT INTO zitadel.users (" +
"instance_id, organization_id, id, username, state, type" +
", name, description, secret, access_token_type, created_at, updated_at) VALUES (",
)
var createdAt any = database.NowInstruction
if !user.CreatedAt.IsZero() {
createdAt = user.CreatedAt
}
builder.WriteArgs(
user.InstanceID, user.OrganizationID, user.ID, user.Username, user.State, "machine",
user.Machine.Name, user.Machine.Description, user.Machine.Secret, uint8(user.Machine.AccessTokenType),
createdAt, createdAt,
)
builder.WriteString(") RETURNING created_at, updated_at")
return client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&user.CreatedAt, &user.UpdatedAt)
}
// Update implements [domain.MachineUserRepository].
func (u userMachine) Update(ctx context.Context, client database.QueryExecutor, condition database.Condition, changes ...database.Change) (int64, error) {
if !condition.IsRestrictingColumn(u.TypeColumn()) {
condition = database.And(condition, u.TypeCondition(domain.UserTypeMachine))
}
return u.user.Update(ctx, client, condition, changes...)
}
// -------------------------------------------------------------
// changes
// -------------------------------------------------------------
// SetAccessTokenType implements [domain.MachineUserRepository].
func (u userMachine) SetAccessTokenType(tokenType domain.AccessTokenType) database.Change {
return database.NewChange(u.accessTokenTypeColumn(), tokenType)
}
// SetDescription implements [domain.MachineUserRepository].
func (u userMachine) SetDescription(description string) database.Change {
return database.NewChange(u.descriptionColumn(), description)
}
// SetName implements [domain.MachineUserRepository].
func (u userMachine) SetName(name string) database.Change {
return database.NewChange(u.nameColumn(), name)
}
// SetSecret implements [domain.MachineUserRepository].
func (u userMachine) SetSecret(secret *string) database.Change {
return database.NewChangePtr(u.secretColumn(), secret)
}
var _ domain.MachineUserRepository = (*userMachine)(nil)
// -------------------------------------------------------------
// columns
// -------------------------------------------------------------
func (u userMachine) nameColumn() database.Column {
return database.NewColumn(u.user.unqualifiedTableName(), "name")
}
func (u userMachine) descriptionColumn() database.Column {
return database.NewColumn(u.user.unqualifiedTableName(), "description")
}
func (u userMachine) accessTokenTypeColumn() database.Column {
return database.NewColumn(u.user.unqualifiedTableName(), "access_token_type")
}
func (u userMachine) secretColumn() database.Column {
return database.NewColumn(u.user.unqualifiedTableName(), "secret")
}