diff --git a/dbinfo/user_.bob.go b/dbinfo/user_.bob.go index 10c9aff2..3566799b 100644 --- a/dbinfo/user_.bob.go +++ b/dbinfo/user_.bob.go @@ -105,6 +105,24 @@ var Users = Table[ Generated: false, AutoIncr: false, }, + PasswordHashType: column{ + Name: "password_hash_type", + DBType: "public.hashtype", + Default: "NULL", + Comment: "", + Nullable: true, + Generated: false, + AutoIncr: false, + }, + PasswordHash: column{ + Name: "password_hash", + DBType: "text", + Default: "NULL", + Comment: "", + Nullable: true, + Generated: false, + AutoIncr: false, + }, }, Indexes: userIndexes{ UserPkey: index{ @@ -156,11 +174,13 @@ type userColumns struct { Email column OrganizationID column Username column + PasswordHashType column + PasswordHash column } func (c userColumns) AsSlice() []column { return []column{ - c.ID, c.ArcgisAccessToken, c.ArcgisLicense, c.ArcgisRefreshToken, c.ArcgisRefreshTokenExpires, c.ArcgisRole, c.DisplayName, c.Email, c.OrganizationID, c.Username, + c.ID, c.ArcgisAccessToken, c.ArcgisLicense, c.ArcgisRefreshToken, c.ArcgisRefreshTokenExpires, c.ArcgisRole, c.DisplayName, c.Email, c.OrganizationID, c.Username, c.PasswordHashType, c.PasswordHash, } } diff --git a/endpoint.go b/endpoint.go index 870cd81c..f26d7725 100644 --- a/endpoint.go +++ b/endpoint.go @@ -1,7 +1,7 @@ package main import ( - "log" + "log/slog" "net/http" ) func getFavicon(w http.ResponseWriter, r *http.Request) { @@ -23,19 +23,37 @@ func getSignup(w http.ResponseWriter, r *http.Request) { } } +func respondError(w http.ResponseWriter, m string, e error, s int) { + slog.Error(m, slog.Int("status", s), slog.String("err", e.Error())) + http.Error(w, m, http.StatusBadRequest) +} + func postSignup(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { - log.Printf("Error parsing form: %v", err) - http.Error(w, "Failed to process form", http.StatusBadRequest) + respondError(w, "Could not parse form", err, http.StatusBadRequest) return } - email := r.FormValue("email") + username := r.FormValue("username") name := r.FormValue("name") + password := r.FormValue("password") terms := r.FormValue("terms") - log.Printf("Signup - Email: %s, Name: %s, Terms: %s", email, name, terms) + slog.Info("Signup", + slog.String("username", username), + slog.String("name", name)) + if terms != "on" { + slog.Error("Terms not agreed", slog.String("terms", terms)) + http.Error(w, "You must agree to the terms to register", http.StatusBadRequest) + return + } + + if err := signupUser(username, name, password); err != nil { + respondError(w, "Failed to signup user", err, http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusOK) w.Write([]byte("Form received")) } diff --git a/enums/enums.bob.go b/enums/enums.bob.go index d294026d..3b624691 100644 --- a/enums/enums.bob.go +++ b/enums/enums.bob.go @@ -116,3 +116,73 @@ func (e *ArcgisLicenseType) Scan(value any) error { return nil } + +// Enum values for Hashtype +const ( + HashtypeBcrypt14 Hashtype = "bcrypt-14" +) + +func AllHashtype() []Hashtype { + return []Hashtype{ + HashtypeBcrypt14, + } +} + +type Hashtype string + +func (e Hashtype) String() string { + return string(e) +} + +func (e Hashtype) Valid() bool { + switch e { + case HashtypeBcrypt14: + return true + default: + return false + } +} + +// useful when testing in other packages +func (e Hashtype) All() []Hashtype { + return AllHashtype() +} + +func (e Hashtype) MarshalText() ([]byte, error) { + return []byte(e), nil +} + +func (e *Hashtype) UnmarshalText(text []byte) error { + return e.Scan(text) +} + +func (e Hashtype) MarshalBinary() ([]byte, error) { + return []byte(e), nil +} + +func (e *Hashtype) UnmarshalBinary(data []byte) error { + return e.Scan(data) +} + +func (e Hashtype) Value() (driver.Value, error) { + return string(e), nil +} + +func (e *Hashtype) Scan(value any) error { + switch x := value.(type) { + case string: + *e = Hashtype(x) + case []byte: + *e = Hashtype(x) + case nil: + return fmt.Errorf("cannot nil into Hashtype") + default: + return fmt.Errorf("cannot scan type %T: %v", value, value) + } + + if !e.Valid() { + return fmt.Errorf("invalid Hashtype value: %s", *e) + } + + return nil +} diff --git a/factory/bobfactory_main.bob.go b/factory/bobfactory_main.bob.go index c9e1dd62..b71b0ba7 100644 --- a/factory/bobfactory_main.bob.go +++ b/factory/bobfactory_main.bob.go @@ -108,6 +108,8 @@ func (f *Factory) FromExistingUser(m *models.User) *UserTemplate { o.Email = func() null.Val[string] { return m.Email } o.OrganizationID = func() null.Val[int32] { return m.OrganizationID } o.Username = func() string { return m.Username } + o.PasswordHashType = func() null.Val[enums.Hashtype] { return m.PasswordHashType } + o.PasswordHash = func() null.Val[string] { return m.PasswordHash } ctx := context.Background() if m.R.Organization != nil { diff --git a/factory/bobfactory_random.bob.go b/factory/bobfactory_random.bob.go index e4fb8bcc..7d409353 100644 --- a/factory/bobfactory_random.bob.go +++ b/factory/bobfactory_random.bob.go @@ -32,6 +32,16 @@ func random_enums_ArcgisLicenseType(f *faker.Faker, limits ...string) enums.Arcg return all[f.IntBetween(0, len(all)-1)] } +func random_enums_Hashtype(f *faker.Faker, limits ...string) enums.Hashtype { + if f == nil { + f = &defaultFaker + } + + var e enums.Hashtype + all := e.All() + return all[f.IntBetween(0, len(all)-1)] +} + func random_int32(f *faker.Faker, limits ...string) int32 { if f == nil { f = &defaultFaker diff --git a/factory/user_.bob.go b/factory/user_.bob.go index 19071aec..080df3b8 100644 --- a/factory/user_.bob.go +++ b/factory/user_.bob.go @@ -48,6 +48,8 @@ type UserTemplate struct { Email func() null.Val[string] OrganizationID func() null.Val[int32] Username func() string + PasswordHashType func() null.Val[enums.Hashtype] + PasswordHash func() null.Val[string] r userR f *Factory @@ -126,6 +128,14 @@ func (o UserTemplate) BuildSetter() *models.UserSetter { val := o.Username() m.Username = omit.From(val) } + if o.PasswordHashType != nil { + val := o.PasswordHashType() + m.PasswordHashType = omitnull.FromNull(val) + } + if o.PasswordHash != nil { + val := o.PasswordHash() + m.PasswordHash = omitnull.FromNull(val) + } return m } @@ -178,6 +188,12 @@ func (o UserTemplate) Build() *models.User { if o.Username != nil { m.Username = o.Username() } + if o.PasswordHashType != nil { + m.PasswordHashType = o.PasswordHashType() + } + if o.PasswordHash != nil { + m.PasswordHash = o.PasswordHash() + } o.setModelRels(m) @@ -331,6 +347,8 @@ func (m userMods) RandomizeAllColumns(f *faker.Faker) UserMod { UserMods.RandomEmail(f), UserMods.RandomOrganizationID(f), UserMods.RandomUsername(f), + UserMods.RandomPasswordHashType(f), + UserMods.RandomPasswordHash(f), } } @@ -820,6 +838,112 @@ func (m userMods) RandomUsername(f *faker.Faker) UserMod { }) } +// Set the model columns to this value +func (m userMods) PasswordHashType(val null.Val[enums.Hashtype]) UserMod { + return UserModFunc(func(_ context.Context, o *UserTemplate) { + o.PasswordHashType = func() null.Val[enums.Hashtype] { return val } + }) +} + +// Set the Column from the function +func (m userMods) PasswordHashTypeFunc(f func() null.Val[enums.Hashtype]) UserMod { + return UserModFunc(func(_ context.Context, o *UserTemplate) { + o.PasswordHashType = f + }) +} + +// Clear any values for the column +func (m userMods) UnsetPasswordHashType() UserMod { + return UserModFunc(func(_ context.Context, o *UserTemplate) { + o.PasswordHashType = nil + }) +} + +// Generates a random value for the column using the given faker +// if faker is nil, a default faker is used +// The generated value is sometimes null +func (m userMods) RandomPasswordHashType(f *faker.Faker) UserMod { + return UserModFunc(func(_ context.Context, o *UserTemplate) { + o.PasswordHashType = func() null.Val[enums.Hashtype] { + if f == nil { + f = &defaultFaker + } + + val := random_enums_Hashtype(f) + return null.From(val) + } + }) +} + +// Generates a random value for the column using the given faker +// if faker is nil, a default faker is used +// The generated value is never null +func (m userMods) RandomPasswordHashTypeNotNull(f *faker.Faker) UserMod { + return UserModFunc(func(_ context.Context, o *UserTemplate) { + o.PasswordHashType = func() null.Val[enums.Hashtype] { + if f == nil { + f = &defaultFaker + } + + val := random_enums_Hashtype(f) + return null.From(val) + } + }) +} + +// Set the model columns to this value +func (m userMods) PasswordHash(val null.Val[string]) UserMod { + return UserModFunc(func(_ context.Context, o *UserTemplate) { + o.PasswordHash = func() null.Val[string] { return val } + }) +} + +// Set the Column from the function +func (m userMods) PasswordHashFunc(f func() null.Val[string]) UserMod { + return UserModFunc(func(_ context.Context, o *UserTemplate) { + o.PasswordHash = f + }) +} + +// Clear any values for the column +func (m userMods) UnsetPasswordHash() UserMod { + return UserModFunc(func(_ context.Context, o *UserTemplate) { + o.PasswordHash = nil + }) +} + +// Generates a random value for the column using the given faker +// if faker is nil, a default faker is used +// The generated value is sometimes null +func (m userMods) RandomPasswordHash(f *faker.Faker) UserMod { + return UserModFunc(func(_ context.Context, o *UserTemplate) { + o.PasswordHash = func() null.Val[string] { + if f == nil { + f = &defaultFaker + } + + val := random_string(f) + return null.From(val) + } + }) +} + +// Generates a random value for the column using the given faker +// if faker is nil, a default faker is used +// The generated value is never null +func (m userMods) RandomPasswordHashNotNull(f *faker.Faker) UserMod { + return UserModFunc(func(_ context.Context, o *UserTemplate) { + o.PasswordHash = func() null.Val[string] { + if f == nil { + f = &defaultFaker + } + + val := random_string(f) + return null.From(val) + } + }) +} + func (m userMods) WithParentsCascading() UserMod { return UserModFunc(func(ctx context.Context, o *UserTemplate) { if isDone, _ := userWithParentsCascadingCtx.Value(ctx); isDone { diff --git a/go.mod b/go.mod index 1e5fbc5c..8f648c83 100644 --- a/go.mod +++ b/go.mod @@ -3,23 +3,40 @@ module github.com/Gleipnir-Technology/nidus-sync go 1.24.9 require ( + github.com/aarondl/opt v0.0.0-20250607033636-982744e1bd65 github.com/alexedwards/scs/v2 v2.9.0 github.com/go-chi/chi/v5 v5.2.3 + github.com/go-webauthn/webauthn v0.14.0 + github.com/golang-jwt/jwt/v5 v5.3.0 github.com/jackc/pgx/v5 v5.7.6 + github.com/jaswdr/faker/v2 v2.8.1 + github.com/lib/pq v1.10.9 github.com/pressly/goose/v3 v3.26.0 github.com/stephenafamo/bob v0.41.1 + github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07 ) require ( + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-webauthn/x v0.1.25 // indirect + github.com/google/go-tpm v0.9.5 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/mfridman/interpolate v0.0.2 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pganalyze/pg_query_go/v6 v6.1.0 // indirect github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect github.com/stephenafamo/scan v0.7.0 // indirect + github.com/tetratelabs/wazero v1.9.0 // indirect + github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect + github.com/x448/float16 v0.8.4 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.40.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/text v0.27.0 // indirect + golang.org/x/crypto v0.42.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect + google.golang.org/protobuf v1.36.5 // indirect ) diff --git a/go.sum b/go.sum index 0b6ca6cb..bd7e148a 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,66 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/aarondl/opt v0.0.0-20250607033636-982744e1bd65 h1:lbdPe4LBNmNDzeQFwNhEc88w90841qv737MI4+aXSYU= github.com/aarondl/opt v0.0.0-20250607033636-982744e1bd65/go.mod h1:+xKBXrTAUOvrDXO5PRwIr4E1wciHY3Glgl+6OkCXknU= github.com/alexedwards/scs/v2 v2.9.0 h1:xa05mVpwTBm1iLeTMNFfAWpKUm4fXAW7CeAViqBVS90= github.com/alexedwards/scs/v2 v2.9.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v28.2.2+incompatible h1:CjwRSksz8Yo4+RmQ339Dp/D2tGO5JxwYeqtMOEe0LDw= +github.com/docker/docker v28.2.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= +github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-webauthn/webauthn v0.14.0 h1:ZLNPUgPcDlAeoxe+5umWG/tEeCoQIDr7gE2Zx2QnhL0= +github.com/go-webauthn/webauthn v0.14.0/go.mod h1:QZzPFH3LJ48u5uEPAu+8/nWJImoLBWM7iAH/kSVSo6k= +github.com/go-webauthn/x v0.1.25 h1:g/0noooIGcz/yCVqebcFgNnGIgBlJIccS+LYAa+0Z88= +github.com/go-webauthn/x v0.1.25/go.mod h1:ieblaPY1/BVCV0oQTsA/VAo08/TWayQuJuo5Q+XxmTY= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-tpm v0.9.5 h1:ocUmnDebX54dnW+MQWGQRbdaAcJELsa6PqZhJ48KwVU= +github.com/google/go-tpm v0.9.5/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= @@ -21,14 +71,52 @@ github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk= github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jaswdr/faker/v2 v2.8.1 h1:2AcPgHDBXYQregFUH9LgVZKfFupc4SIquYhp29sf5wQ= +github.com/jaswdr/faker/v2 v2.8.1/go.mod h1:jZq+qzNQr8/P+5fHd9t3txe2GNPnthrTfohtnJ7B+68= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= +github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/pganalyze/pg_query_go/v6 v6.1.0 h1:jG5ZLhcVgL1FAw4C/0VNQaVmX1SUJx71wBGdtTtBvls= +github.com/pganalyze/pg_query_go/v6 v6.1.0/go.mod h1:nvTHIuoud6e1SfrUaFwHqT0i4b5Nr+1rPWVds3B5+50= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/pressly/goose/v3 v3.26.0 h1:KJakav68jdH0WDvoAcj8+n61WqOIaPGgH0bJWS6jpmM= github.com/pressly/goose/v3 v3.26.0/go.mod h1:4hC1KrritdCxtuFsqgs1R4AU5bWtTAf+cnWvfhf2DNY= github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 h1:wSmWgpuccqS2IOfmYrbRiUgv+g37W5suLLLxwwniTSc= @@ -37,6 +125,10 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= +github.com/shirou/gopsutil/v4 v4.25.5 h1:rtd9piuSMGeU8g1RMXjZs9y9luK5BwtnG7dZaQUJAsc= +github.com/shirou/gopsutil/v4 v4.25.5/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stephenafamo/bob v0.41.1 h1:xcRPuRMCwtZZ9tS4JIVbZ5Erdm5Dy5dIvbS5kivwPpA= github.com/stephenafamo/bob v0.41.1/go.mod h1:8l55917DM36gF518Iz1MHjLds7KGAfkitJfxISYlth8= github.com/stephenafamo/fakedb v0.0.0-20221230081958-0b86f816ed97 h1:XItoZNmhOih06TC02jK7l3wlpZ0XT/sPQYutDcGOQjg= @@ -47,20 +139,55 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8= -github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/testcontainers/testcontainers-go v0.38.0 h1:d7uEapLcv2P8AvH8ahLqDMMxda2W9gQN1nRbHS28HBw= +github.com/testcontainers/testcontainers-go v0.38.0/go.mod h1:C52c9MoHpWO+C4aqmgSU+hxlR5jlEayWtgYrb8Pzz1w= +github.com/testcontainers/testcontainers-go/modules/postgres v0.38.0 h1:KFdx9A0yF94K70T6ibSuvgkQQeX1xKlZVF3hEagXEtY= +github.com/testcontainers/testcontainers-go/modules/postgres v0.38.0/go.mod h1:T/QRECND6N6tAKMxF1Za+G2tpwnGEHcODzHRsgIpw9M= +github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I= +github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07 h1:mJdDDPblDfPe7z7go8Dvv1AJQDI3eQ/5xith3q2mFlo= +github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07/go.mod h1:Ak17IJ037caFp4jpCw/iQQ7/W74Sqpb1YuKJU6HTKfM= +github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 h1:OvLBa8SqJnZ6P+mjlzc2K7PM22rRUPE1x32G9DTPrC4= +github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52/go.mod h1:jMeV4Vpbi8osrE/pKUxRZkVaA0EX7NZN0A9/oRzgpgY= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/migrations/00002_password.sql b/migrations/00002_password.sql new file mode 100644 index 00000000..8dc3667f --- /dev/null +++ b/migrations/00002_password.sql @@ -0,0 +1,10 @@ +-- +goose Up +CREATE TYPE HashType AS ENUM ( + 'bcrypt-14'); + +ALTER TABLE user_ ADD COLUMN password_hash_type HashType; +ALTER TABLE user_ ADD COLUMN password_hash TEXT; +-- +goose Down +ALTER TABLE user_ DROP COLUMN password_hash; +ALTER TABLE user_ DROP COLUMN password_hash_type; +DROP TYPE HashType; diff --git a/models/bob_types.bob_test.go b/models/bob_types.bob_test.go index b58bc40f..80312858 100644 --- a/models/bob_types.bob_test.go +++ b/models/bob_types.bob_test.go @@ -28,3 +28,9 @@ var _ sql.Scanner = (*enums.ArcgisLicenseType)(nil) // Make sure the type enums.ArcgisLicenseType satisfies database/sql/driver.Valuer var _ driver.Valuer = *new(enums.ArcgisLicenseType) + +// Make sure the type enums.Hashtype satisfies database/sql.Scanner +var _ sql.Scanner = (*enums.Hashtype)(nil) + +// Make sure the type enums.Hashtype satisfies database/sql/driver.Valuer +var _ driver.Valuer = *new(enums.Hashtype) diff --git a/models/user_.bob.go b/models/user_.bob.go index 885ac320..99fe1685 100644 --- a/models/user_.bob.go +++ b/models/user_.bob.go @@ -37,6 +37,8 @@ type User struct { Email null.Val[string] `db:"email" ` OrganizationID null.Val[int32] `db:"organization_id" ` Username string `db:"username" ` + PasswordHashType null.Val[enums.Hashtype] `db:"password_hash_type" ` + PasswordHash null.Val[string] `db:"password_hash" ` R userR `db:"-" ` } @@ -59,7 +61,7 @@ type userR struct { func buildUserColumns(alias string) userColumns { return userColumns{ ColumnsExpr: expr.NewColumnsExpr( - "id", "arcgis_access_token", "arcgis_license", "arcgis_refresh_token", "arcgis_refresh_token_expires", "arcgis_role", "display_name", "email", "organization_id", "username", + "id", "arcgis_access_token", "arcgis_license", "arcgis_refresh_token", "arcgis_refresh_token_expires", "arcgis_role", "display_name", "email", "organization_id", "username", "password_hash_type", "password_hash", ).WithParent("user_"), tableAlias: alias, ID: psql.Quote(alias, "id"), @@ -72,6 +74,8 @@ func buildUserColumns(alias string) userColumns { Email: psql.Quote(alias, "email"), OrganizationID: psql.Quote(alias, "organization_id"), Username: psql.Quote(alias, "username"), + PasswordHashType: psql.Quote(alias, "password_hash_type"), + PasswordHash: psql.Quote(alias, "password_hash"), } } @@ -88,6 +92,8 @@ type userColumns struct { Email psql.Expression OrganizationID psql.Expression Username psql.Expression + PasswordHashType psql.Expression + PasswordHash psql.Expression } func (c userColumns) Alias() string { @@ -112,10 +118,12 @@ type UserSetter struct { Email omitnull.Val[string] `db:"email" ` OrganizationID omitnull.Val[int32] `db:"organization_id" ` Username omit.Val[string] `db:"username" ` + PasswordHashType omitnull.Val[enums.Hashtype] `db:"password_hash_type" ` + PasswordHash omitnull.Val[string] `db:"password_hash" ` } func (s UserSetter) SetColumns() []string { - vals := make([]string, 0, 10) + vals := make([]string, 0, 12) if s.ID.IsValue() { vals = append(vals, "id") } @@ -146,6 +154,12 @@ func (s UserSetter) SetColumns() []string { if s.Username.IsValue() { vals = append(vals, "username") } + if !s.PasswordHashType.IsUnset() { + vals = append(vals, "password_hash_type") + } + if !s.PasswordHash.IsUnset() { + vals = append(vals, "password_hash") + } return vals } @@ -180,6 +194,12 @@ func (s UserSetter) Overwrite(t *User) { if s.Username.IsValue() { t.Username = s.Username.MustGet() } + if !s.PasswordHashType.IsUnset() { + t.PasswordHashType = s.PasswordHashType.MustGetNull() + } + if !s.PasswordHash.IsUnset() { + t.PasswordHash = s.PasswordHash.MustGetNull() + } } func (s *UserSetter) Apply(q *dialect.InsertQuery) { @@ -188,7 +208,7 @@ func (s *UserSetter) Apply(q *dialect.InsertQuery) { }) q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) { - vals := make([]bob.Expression, 10) + vals := make([]bob.Expression, 12) if s.ID.IsValue() { vals[0] = psql.Arg(s.ID.MustGet()) } else { @@ -249,6 +269,18 @@ func (s *UserSetter) Apply(q *dialect.InsertQuery) { vals[9] = psql.Raw("DEFAULT") } + if !s.PasswordHashType.IsUnset() { + vals[10] = psql.Arg(s.PasswordHashType.MustGetNull()) + } else { + vals[10] = psql.Raw("DEFAULT") + } + + if !s.PasswordHash.IsUnset() { + vals[11] = psql.Arg(s.PasswordHash.MustGetNull()) + } else { + vals[11] = psql.Raw("DEFAULT") + } + return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") })) } @@ -258,7 +290,7 @@ func (s UserSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] { } func (s UserSetter) Expressions(prefix ...string) []bob.Expression { - exprs := make([]bob.Expression, 0, 10) + exprs := make([]bob.Expression, 0, 12) if s.ID.IsValue() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ @@ -330,6 +362,20 @@ func (s UserSetter) Expressions(prefix ...string) []bob.Expression { }}) } + if !s.PasswordHashType.IsUnset() { + exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ + psql.Quote(append(prefix, "password_hash_type")...), + psql.Arg(s.PasswordHashType), + }}) + } + + if !s.PasswordHash.IsUnset() { + exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ + psql.Quote(append(prefix, "password_hash")...), + psql.Arg(s.PasswordHash), + }}) + } + return exprs } @@ -639,6 +685,8 @@ type userWhere[Q psql.Filterable] struct { Email psql.WhereNullMod[Q, string] OrganizationID psql.WhereNullMod[Q, int32] Username psql.WhereMod[Q, string] + PasswordHashType psql.WhereNullMod[Q, enums.Hashtype] + PasswordHash psql.WhereNullMod[Q, string] } func (userWhere[Q]) AliasedAs(alias string) userWhere[Q] { @@ -657,6 +705,8 @@ func buildUserWhere[Q psql.Filterable](cols userColumns) userWhere[Q] { Email: psql.WhereNull[Q, string](cols.Email), OrganizationID: psql.WhereNull[Q, int32](cols.OrganizationID), Username: psql.Where[Q, string](cols.Username), + PasswordHashType: psql.WhereNull[Q, enums.Hashtype](cols.PasswordHashType), + PasswordHash: psql.WhereNull[Q, string](cols.PasswordHash), } } diff --git a/templates/signup.html b/templates/signup.html index ce4644fa..0111243b 100644 --- a/templates/signup.html +++ b/templates/signup.html @@ -56,13 +56,18 @@
- +
- - + + +
+ +
+ +