Always include an organization for every user
This commit is contained in:
parent
447e52c6a0
commit
53ee020fe0
10 changed files with 85 additions and 85 deletions
23
auth/auth.go
23
auth/auth.go
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/Gleipnir-Technology/nidus-sync/db/sql"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/debug"
|
||||
"github.com/aarondl/opt/omit"
|
||||
"github.com/aarondl/opt/omitnull"
|
||||
"github.com/rs/zerolog/log"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
|
@ -122,22 +123,34 @@ func SigninUser(r *http.Request, username string, password string) (*models.User
|
|||
return user, nil
|
||||
}
|
||||
|
||||
func SignupUser(username string, name string, password string) (*models.User, error) {
|
||||
func SignupUser(ctx context.Context, username string, name string, password string) (*models.User, error) {
|
||||
passwordHash, err := hashPassword(password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Cannot signup user: %w", err)
|
||||
return nil, fmt.Errorf("Cannot signup user, failed to create hashed password: %w", err)
|
||||
}
|
||||
setter := models.UserSetter{
|
||||
o_setter := models.OrganizationSetter{
|
||||
Name: omitnull.From(fmt.Sprintf("%s's organization", username)),
|
||||
ArcgisID: omitnull.From(""),
|
||||
ArcgisName: omitnull.From(""),
|
||||
FieldseekerURL: omitnull.From(""),
|
||||
}
|
||||
o, err := models.Organizations.Insert(&o_setter).One(ctx, db.PGInstance.BobDB)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to create organization: %w", err)
|
||||
}
|
||||
log.Info().Int32("id", o.ID).Msg("Created organization")
|
||||
u_setter := models.UserSetter{
|
||||
DisplayName: omit.From(name),
|
||||
OrganizationID: omit.From(o.ID),
|
||||
PasswordHash: omit.From(passwordHash),
|
||||
PasswordHashType: omit.From(enums.HashtypeBcrypt14),
|
||||
Username: omit.From(username),
|
||||
}
|
||||
u, err := models.Users.Insert(&setter).One(context.TODO(), db.PGInstance.BobDB)
|
||||
u, err := models.Users.Insert(&u_setter).One(ctx, db.PGInstance.BobDB)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to create user: %w", err)
|
||||
}
|
||||
log.Info().Int("ID", int(u.ID)).Str("username", u.Username).Msg("Created user")
|
||||
log.Info().Int32("id", u.ID).Str("username", u.Username).Msg("Created user")
|
||||
|
||||
return u, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,9 +90,9 @@ var Users = Table[
|
|||
OrganizationID: column{
|
||||
Name: "organization_id",
|
||||
DBType: "integer",
|
||||
Default: "NULL",
|
||||
Default: "",
|
||||
Comment: "",
|
||||
Nullable: true,
|
||||
Nullable: false,
|
||||
Generated: false,
|
||||
AutoIncr: false,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2523,7 +2523,7 @@ func (f *Factory) FromExistingUser(m *models.User) *UserTemplate {
|
|||
o.ArcgisRole = func() null.Val[string] { return m.ArcgisRole }
|
||||
o.DisplayName = func() string { return m.DisplayName }
|
||||
o.Email = func() null.Val[string] { return m.Email }
|
||||
o.OrganizationID = func() null.Val[int32] { return m.OrganizationID }
|
||||
o.OrganizationID = func() int32 { return m.OrganizationID }
|
||||
o.Username = func() string { return m.Username }
|
||||
o.PasswordHashType = func() enums.Hashtype { return m.PasswordHashType }
|
||||
o.PasswordHash = func() string { return m.PasswordHash }
|
||||
|
|
|
|||
|
|
@ -630,7 +630,7 @@ func (t OrganizationTemplate) setModelRels(o *models.Organization) {
|
|||
for _, r := range t.r.User {
|
||||
related := r.o.BuildMany(r.number)
|
||||
for _, rel := range related {
|
||||
rel.OrganizationID = null.From(o.ID) // h2
|
||||
rel.OrganizationID = o.ID // h2
|
||||
rel.R.Organization = o
|
||||
}
|
||||
rel = append(rel, related...)
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ type UserTemplate struct {
|
|||
ArcgisRole func() null.Val[string]
|
||||
DisplayName func() string
|
||||
Email func() null.Val[string]
|
||||
OrganizationID func() null.Val[int32]
|
||||
OrganizationID func() int32
|
||||
Username func() string
|
||||
PasswordHashType func() enums.Hashtype
|
||||
PasswordHash func() string
|
||||
|
|
@ -186,7 +186,7 @@ func (t UserTemplate) setModelRels(o *models.User) {
|
|||
if t.r.Organization != nil {
|
||||
rel := t.r.Organization.o.Build()
|
||||
rel.R.User = append(rel.R.User, o)
|
||||
o.OrganizationID = null.From(rel.ID) // h2
|
||||
o.OrganizationID = rel.ID // h2
|
||||
o.R.Organization = rel
|
||||
}
|
||||
}
|
||||
|
|
@ -230,7 +230,7 @@ func (o UserTemplate) BuildSetter() *models.UserSetter {
|
|||
}
|
||||
if o.OrganizationID != nil {
|
||||
val := o.OrganizationID()
|
||||
m.OrganizationID = omitnull.FromNull(val)
|
||||
m.OrganizationID = omit.From(val)
|
||||
}
|
||||
if o.Username != nil {
|
||||
val := o.Username()
|
||||
|
|
@ -326,6 +326,10 @@ func ensureCreatableUser(m *models.UserSetter) {
|
|||
val := random_string(nil, "200")
|
||||
m.DisplayName = omit.From(val)
|
||||
}
|
||||
if !(m.OrganizationID.IsValue()) {
|
||||
val := random_int32(nil)
|
||||
m.OrganizationID = omit.From(val)
|
||||
}
|
||||
if !(m.Username.IsValue()) {
|
||||
val := random_string(nil)
|
||||
m.Username = omit.From(val)
|
||||
|
|
@ -466,25 +470,6 @@ func (o *UserTemplate) insertOptRels(ctx context.Context, exec bob.Executor, m *
|
|||
}
|
||||
}
|
||||
|
||||
isOrganizationDone, _ := userRelOrganizationCtx.Value(ctx)
|
||||
if !isOrganizationDone && o.r.Organization != nil {
|
||||
ctx = userRelOrganizationCtx.WithValue(ctx, true)
|
||||
if o.r.Organization.o.alreadyPersisted {
|
||||
m.R.Organization = o.r.Organization.o.Build()
|
||||
} else {
|
||||
var rel6 *models.Organization
|
||||
rel6, err = o.r.Organization.o.Create(ctx, exec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = m.AttachOrganization(ctx, exec, rel6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -495,11 +480,30 @@ func (o *UserTemplate) Create(ctx context.Context, exec bob.Executor) (*models.U
|
|||
opt := o.BuildSetter()
|
||||
ensureCreatableUser(opt)
|
||||
|
||||
if o.r.Organization == nil {
|
||||
UserMods.WithNewOrganization().Apply(ctx, o)
|
||||
}
|
||||
|
||||
var rel6 *models.Organization
|
||||
|
||||
if o.r.Organization.o.alreadyPersisted {
|
||||
rel6 = o.r.Organization.o.Build()
|
||||
} else {
|
||||
rel6, err = o.r.Organization.o.Create(ctx, exec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
opt.OrganizationID = omit.From(rel6.ID)
|
||||
|
||||
m, err := models.Users.Insert(opt).One(ctx, exec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.R.Organization = rel6
|
||||
|
||||
if err := o.insertOptRels(ctx, exec, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -973,14 +977,14 @@ func (m userMods) RandomEmailNotNull(f *faker.Faker) UserMod {
|
|||
}
|
||||
|
||||
// Set the model columns to this value
|
||||
func (m userMods) OrganizationID(val null.Val[int32]) UserMod {
|
||||
func (m userMods) OrganizationID(val int32) UserMod {
|
||||
return UserModFunc(func(_ context.Context, o *UserTemplate) {
|
||||
o.OrganizationID = func() null.Val[int32] { return val }
|
||||
o.OrganizationID = func() int32 { return val }
|
||||
})
|
||||
}
|
||||
|
||||
// Set the Column from the function
|
||||
func (m userMods) OrganizationIDFunc(f func() null.Val[int32]) UserMod {
|
||||
func (m userMods) OrganizationIDFunc(f func() int32) UserMod {
|
||||
return UserModFunc(func(_ context.Context, o *UserTemplate) {
|
||||
o.OrganizationID = f
|
||||
})
|
||||
|
|
@ -995,32 +999,10 @@ func (m userMods) UnsetOrganizationID() UserMod {
|
|||
|
||||
// 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) RandomOrganizationID(f *faker.Faker) UserMod {
|
||||
return UserModFunc(func(_ context.Context, o *UserTemplate) {
|
||||
o.OrganizationID = func() null.Val[int32] {
|
||||
if f == nil {
|
||||
f = &defaultFaker
|
||||
}
|
||||
|
||||
val := random_int32(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) RandomOrganizationIDNotNull(f *faker.Faker) UserMod {
|
||||
return UserModFunc(func(_ context.Context, o *UserTemplate) {
|
||||
o.OrganizationID = func() null.Val[int32] {
|
||||
if f == nil {
|
||||
f = &defaultFaker
|
||||
}
|
||||
|
||||
val := random_int32(f)
|
||||
return null.From(val)
|
||||
o.OrganizationID = func() int32 {
|
||||
return random_int32(f)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
5
db/migrations/00023_user_org_not_null.sql
Normal file
5
db/migrations/00023_user_org_not_null.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
-- +goose Up
|
||||
ALTER TABLE user_ ALTER COLUMN organization_id SET NOT NULL;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE user_ ALTER COLUMN organization_id DROP NOT NULL;
|
||||
|
|
@ -3348,7 +3348,7 @@ func (organization0 *Organization) AttachNoteImages(ctx context.Context, exec bo
|
|||
|
||||
func insertOrganizationUser0(ctx context.Context, exec bob.Executor, users1 []*UserSetter, organization0 *Organization) (UserSlice, error) {
|
||||
for i := range users1 {
|
||||
users1[i].OrganizationID = omitnull.From(organization0.ID)
|
||||
users1[i].OrganizationID = omit.From(organization0.ID)
|
||||
}
|
||||
|
||||
ret, err := Users.Insert(bob.ToMods(users1...)).All(ctx, exec)
|
||||
|
|
@ -3361,7 +3361,7 @@ func insertOrganizationUser0(ctx context.Context, exec bob.Executor, users1 []*U
|
|||
|
||||
func attachOrganizationUser0(ctx context.Context, exec bob.Executor, count int, users1 UserSlice, organization0 *Organization) (UserSlice, error) {
|
||||
setter := &UserSetter{
|
||||
OrganizationID: omitnull.From(organization0.ID),
|
||||
OrganizationID: omit.From(organization0.ID),
|
||||
}
|
||||
|
||||
err := users1.UpdateAll(ctx, exec, *setter)
|
||||
|
|
@ -6169,10 +6169,7 @@ func (os OrganizationSlice) LoadUser(ctx context.Context, exec bob.Executor, mod
|
|||
|
||||
for _, rel := range users {
|
||||
|
||||
if !rel.OrganizationID.IsValue() {
|
||||
continue
|
||||
}
|
||||
if !(rel.OrganizationID.IsValue() && o.ID == rel.OrganizationID.MustGet()) {
|
||||
if !(o.ID == rel.OrganizationID) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ type User struct {
|
|||
ArcgisRole null.Val[string] `db:"arcgis_role" `
|
||||
DisplayName string `db:"display_name" `
|
||||
Email null.Val[string] `db:"email" `
|
||||
OrganizationID null.Val[int32] `db:"organization_id" `
|
||||
OrganizationID int32 `db:"organization_id" `
|
||||
Username string `db:"username" `
|
||||
PasswordHashType enums.Hashtype `db:"password_hash_type" `
|
||||
PasswordHash string `db:"password_hash" `
|
||||
|
|
@ -122,7 +122,7 @@ type UserSetter struct {
|
|||
ArcgisRole omitnull.Val[string] `db:"arcgis_role" `
|
||||
DisplayName omit.Val[string] `db:"display_name" `
|
||||
Email omitnull.Val[string] `db:"email" `
|
||||
OrganizationID omitnull.Val[int32] `db:"organization_id" `
|
||||
OrganizationID omit.Val[int32] `db:"organization_id" `
|
||||
Username omit.Val[string] `db:"username" `
|
||||
PasswordHashType omit.Val[enums.Hashtype] `db:"password_hash_type" `
|
||||
PasswordHash omit.Val[string] `db:"password_hash" `
|
||||
|
|
@ -154,7 +154,7 @@ func (s UserSetter) SetColumns() []string {
|
|||
if !s.Email.IsUnset() {
|
||||
vals = append(vals, "email")
|
||||
}
|
||||
if !s.OrganizationID.IsUnset() {
|
||||
if s.OrganizationID.IsValue() {
|
||||
vals = append(vals, "organization_id")
|
||||
}
|
||||
if s.Username.IsValue() {
|
||||
|
|
@ -194,8 +194,8 @@ func (s UserSetter) Overwrite(t *User) {
|
|||
if !s.Email.IsUnset() {
|
||||
t.Email = s.Email.MustGetNull()
|
||||
}
|
||||
if !s.OrganizationID.IsUnset() {
|
||||
t.OrganizationID = s.OrganizationID.MustGetNull()
|
||||
if s.OrganizationID.IsValue() {
|
||||
t.OrganizationID = s.OrganizationID.MustGet()
|
||||
}
|
||||
if s.Username.IsValue() {
|
||||
t.Username = s.Username.MustGet()
|
||||
|
|
@ -263,8 +263,8 @@ func (s *UserSetter) Apply(q *dialect.InsertQuery) {
|
|||
vals[7] = psql.Raw("DEFAULT")
|
||||
}
|
||||
|
||||
if !s.OrganizationID.IsUnset() {
|
||||
vals[8] = psql.Arg(s.OrganizationID.MustGetNull())
|
||||
if s.OrganizationID.IsValue() {
|
||||
vals[8] = psql.Arg(s.OrganizationID.MustGet())
|
||||
} else {
|
||||
vals[8] = psql.Raw("DEFAULT")
|
||||
}
|
||||
|
|
@ -354,7 +354,7 @@ func (s UserSetter) Expressions(prefix ...string) []bob.Expression {
|
|||
}})
|
||||
}
|
||||
|
||||
if !s.OrganizationID.IsUnset() {
|
||||
if s.OrganizationID.IsValue() {
|
||||
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
|
||||
psql.Quote(append(prefix, "organization_id")...),
|
||||
psql.Arg(s.OrganizationID),
|
||||
|
|
@ -760,7 +760,7 @@ func (o *User) Organization(mods ...bob.Mod[*dialect.SelectQuery]) Organizations
|
|||
}
|
||||
|
||||
func (os UserSlice) Organization(mods ...bob.Mod[*dialect.SelectQuery]) OrganizationsQuery {
|
||||
pkOrganizationID := make(pgtypes.Array[null.Val[int32]], 0, len(os))
|
||||
pkOrganizationID := make(pgtypes.Array[int32], 0, len(os))
|
||||
for _, o := range os {
|
||||
if o == nil {
|
||||
continue
|
||||
|
|
@ -1186,7 +1186,7 @@ func (user0 *User) AttachUserOauthTokens(ctx context.Context, exec bob.Executor,
|
|||
|
||||
func attachUserOrganization0(ctx context.Context, exec bob.Executor, count int, user0 *User, organization1 *Organization) (*User, error) {
|
||||
setter := &UserSetter{
|
||||
OrganizationID: omitnull.From(organization1.ID),
|
||||
OrganizationID: omit.From(organization1.ID),
|
||||
}
|
||||
|
||||
err := user0.Update(ctx, exec, setter)
|
||||
|
|
@ -1241,7 +1241,7 @@ type userWhere[Q psql.Filterable] struct {
|
|||
ArcgisRole psql.WhereNullMod[Q, string]
|
||||
DisplayName psql.WhereMod[Q, string]
|
||||
Email psql.WhereNullMod[Q, string]
|
||||
OrganizationID psql.WhereNullMod[Q, int32]
|
||||
OrganizationID psql.WhereMod[Q, int32]
|
||||
Username psql.WhereMod[Q, string]
|
||||
PasswordHashType psql.WhereMod[Q, enums.Hashtype]
|
||||
PasswordHash psql.WhereMod[Q, string]
|
||||
|
|
@ -1261,7 +1261,7 @@ func buildUserWhere[Q psql.Filterable](cols userColumns) userWhere[Q] {
|
|||
ArcgisRole: psql.WhereNull[Q, string](cols.ArcgisRole),
|
||||
DisplayName: psql.Where[Q, string](cols.DisplayName),
|
||||
Email: psql.WhereNull[Q, string](cols.Email),
|
||||
OrganizationID: psql.WhereNull[Q, int32](cols.OrganizationID),
|
||||
OrganizationID: psql.Where[Q, int32](cols.OrganizationID),
|
||||
Username: psql.Where[Q, string](cols.Username),
|
||||
PasswordHashType: psql.Where[Q, enums.Hashtype](cols.PasswordHashType),
|
||||
PasswordHash: psql.Where[Q, string](cols.PasswordHash),
|
||||
|
|
@ -1885,11 +1885,8 @@ func (os UserSlice) LoadOrganization(ctx context.Context, exec bob.Executor, mod
|
|||
}
|
||||
|
||||
for _, rel := range organizations {
|
||||
if !o.OrganizationID.IsValue() {
|
||||
continue
|
||||
}
|
||||
|
||||
if !(o.OrganizationID.IsValue() && o.OrganizationID.MustGet() == rel.ID) {
|
||||
if !(o.OrganizationID == rel.ID) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ type UserByUsernameRow = struct {
|
|||
ArcgisRole null.Val[string] `db:"arcgis_role"`
|
||||
DisplayName string `db:"display_name"`
|
||||
Email null.Val[string] `db:"email"`
|
||||
OrganizationID null.Val[int32] `db:"organization_id"`
|
||||
OrganizationID int32 `db:"organization_id"`
|
||||
Username string `db:"username"`
|
||||
PasswordHashType enums.Hashtype `db:"password_hash_type"`
|
||||
PasswordHash string `db:"password_hash"`
|
||||
|
|
|
|||
14
endpoint.go
14
endpoint.go
|
|
@ -133,9 +133,15 @@ func getQRCodeReport(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func getRoot(w http.ResponseWriter, r *http.Request) {
|
||||
user, err := auth.GetAuthenticatedUser(r)
|
||||
if err != nil && !errors.Is(err, &auth.NoCredentialsError{}) {
|
||||
respondError(w, "Failed to get root", err, http.StatusInternalServerError)
|
||||
return
|
||||
if err != nil {
|
||||
// No credentials or user not found: go to login
|
||||
if errors.Is(err, &auth.NoCredentialsError{}) || errors.Is(err, &auth.NoUserError{}) {
|
||||
http.Redirect(w, r, "/signin", http.StatusFound)
|
||||
return
|
||||
} else {
|
||||
respondError(w, "Failed to get root", err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
if user == nil {
|
||||
errorCode := r.URL.Query().Get("error")
|
||||
|
|
@ -297,7 +303,7 @@ func postSignup(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
user, err := auth.SignupUser(username, name, password)
|
||||
user, err := auth.SignupUser(r.Context(), username, name, password)
|
||||
if err != nil {
|
||||
respondError(w, "Failed to signup user", err, http.StatusInternalServerError)
|
||||
return
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue