Add user sessions and login

This isn't quite perfect, but gets much of the hard work done.
This commit is contained in:
Eli Ribble 2025-11-05 17:15:33 +00:00
parent e311464b51
commit 486c148bf7
No known key found for this signature in database
28 changed files with 1701 additions and 30 deletions

View file

@ -0,0 +1,84 @@
// Code generated by BobGen psql v0.41.1. DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.
package sql
import (
"strconv"
"strings"
"time"
enums "github.com/Gleipnir-Technology/nidus-sync/enums"
"github.com/jaswdr/faker/v2"
"github.com/stephenafamo/bob"
pg_query "github.com/wasilibs/go-pgquery"
)
// Set the testDB to enable tests that use the database
var testDB bob.Transactor[bob.Tx]
func formatQuery(s string) (string, error) {
aTree, err := pg_query.Parse(s)
if err != nil {
return "", err
}
return pg_query.Deparse(aTree)
}
var defaultFaker = faker.New()
func random_enums_ArcgisLicenseType(f *faker.Faker, limits ...string) enums.ArcgisLicenseType {
if f == nil {
f = &defaultFaker
}
var e enums.ArcgisLicenseType
all := e.All()
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
}
return f.Int32()
}
func random_string(f *faker.Faker, limits ...string) string {
if f == nil {
f = &defaultFaker
}
val := strings.Join(f.Lorem().Words(f.IntBetween(1, 5)), " ")
if len(limits) == 0 {
return val
}
limitInt, _ := strconv.Atoi(limits[0])
if limitInt > 0 && limitInt < len(val) {
val = val[:limitInt]
}
return val
}
func random_time_Time(f *faker.Faker, limits ...string) time.Time {
if f == nil {
f = &defaultFaker
}
year := time.Hour * 24 * 365
min := time.Now().Add(-year)
max := time.Now().Add(year)
return f.Time().TimeBetween(min, max)
}

116
sql/user_by_username.bob.go Normal file
View file

@ -0,0 +1,116 @@
// Code generated by BobGen psql v0.41.1. DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.
package sql
import (
"context"
_ "embed"
"io"
"iter"
"time"
enums "github.com/Gleipnir-Technology/nidus-sync/enums"
"github.com/aarondl/opt/null"
"github.com/stephenafamo/bob"
"github.com/stephenafamo/bob/dialect/psql"
"github.com/stephenafamo/bob/dialect/psql/dialect"
"github.com/stephenafamo/bob/orm"
"github.com/stephenafamo/scan"
)
//go:embed user_by_username.bob.sql
var formattedQueries_user_by_username string
var userByUsernameSQL = formattedQueries_user_by_username[152:773]
type UserByUsernameQuery = orm.ModQuery[*dialect.SelectQuery, userByUsername, UserByUsernameRow, []UserByUsernameRow, userByUsernameTransformer]
func UserByUsername(Username string) *UserByUsernameQuery {
var expressionTypArgs userByUsername
expressionTypArgs.Username = psql.Arg(Username)
return &UserByUsernameQuery{
Query: orm.Query[userByUsername, UserByUsernameRow, []UserByUsernameRow, userByUsernameTransformer]{
ExecQuery: orm.ExecQuery[userByUsername]{
BaseQuery: bob.BaseQuery[userByUsername]{
Expression: expressionTypArgs,
Dialect: dialect.Dialect,
QueryType: bob.QueryTypeSelect,
},
},
Scanner: func(context.Context, []string) (func(*scan.Row) (any, error), func(any) (UserByUsernameRow, error)) {
return func(row *scan.Row) (any, error) {
var t UserByUsernameRow
row.ScheduleScanByIndex(0, &t.ID)
row.ScheduleScanByIndex(1, &t.ArcgisAccessToken)
row.ScheduleScanByIndex(2, &t.ArcgisLicense)
row.ScheduleScanByIndex(3, &t.ArcgisRefreshToken)
row.ScheduleScanByIndex(4, &t.ArcgisRefreshTokenExpires)
row.ScheduleScanByIndex(5, &t.ArcgisRole)
row.ScheduleScanByIndex(6, &t.DisplayName)
row.ScheduleScanByIndex(7, &t.Email)
row.ScheduleScanByIndex(8, &t.OrganizationID)
row.ScheduleScanByIndex(9, &t.Username)
row.ScheduleScanByIndex(10, &t.PasswordHashType)
row.ScheduleScanByIndex(11, &t.PasswordHash)
return &t, nil
}, func(v any) (UserByUsernameRow, error) {
return *(v.(*UserByUsernameRow)), nil
}
},
},
Mod: bob.ModFunc[*dialect.SelectQuery](func(q *dialect.SelectQuery) {
q.AppendSelect(expressionTypArgs.subExpr(7, 551))
q.SetTable(expressionTypArgs.subExpr(557, 562))
q.AppendWhere(expressionTypArgs.subExpr(570, 621))
}),
}
}
type UserByUsernameRow = struct {
ID int32 `db:"id"`
ArcgisAccessToken null.Val[string] `db:"arcgis_access_token"`
ArcgisLicense null.Val[enums.ArcgisLicenseType] `db:"arcgis_license"`
ArcgisRefreshToken null.Val[string] `db:"arcgis_refresh_token"`
ArcgisRefreshTokenExpires null.Val[time.Time] `db:"arcgis_refresh_token_expires"`
ArcgisRole null.Val[string] `db:"arcgis_role"`
DisplayName null.Val[string] `db:"display_name"`
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"`
}
type userByUsernameTransformer = bob.SliceTransformer[UserByUsernameRow, []UserByUsernameRow]
type userByUsername struct {
Username bob.Expression
}
func (o userByUsername) args() iter.Seq[orm.ArgWithPosition] {
return func(yield func(arg orm.ArgWithPosition) bool) {
if !yield(orm.ArgWithPosition{
Name: "username",
Start: 581,
Stop: 583,
Expression: o.Username,
}) {
return
}
}
}
func (o userByUsername) raw(from, to int) string {
return userByUsernameSQL[from:to]
}
func (o userByUsername) subExpr(from, to int) bob.Expression {
return orm.ArgsToExpression(userByUsernameSQL, from, to, o.args())
}
func (o userByUsername) WriteSQL(ctx context.Context, w io.Writer, d bob.Dialect, start int) ([]any, error) {
return o.subExpr(0, len(userByUsernameSQL)).WriteSQL(ctx, w, d, start)
}

View file

@ -0,0 +1,7 @@
-- Code generated by BobGen psql v0.41.1. DO NOT EDIT.
-- This file is meant to be re-generated in place and/or deleted at any time.
-- UserByUsername
SELECT "user_"."id" AS "id", "user_"."arcgis_access_token" AS "arcgis_access_token", "user_"."arcgis_license" AS "arcgis_license", "user_"."arcgis_refresh_token" AS "arcgis_refresh_token", "user_"."arcgis_refresh_token_expires" AS "arcgis_refresh_token_expires", "user_"."arcgis_role" AS "arcgis_role", "user_"."display_name" AS "display_name", "user_"."email" AS "email", "user_"."organization_id" AS "organization_id", "user_"."username" AS "username", "user_"."password_hash_type" AS "password_hash_type", "user_"."password_hash" AS "password_hash" FROM user_ WHERE
username = $1 AND
password_hash_type = 'bcrypt-14';

View file

@ -0,0 +1,139 @@
// Code generated by BobGen psql v0.41.1. DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.
package sql
import (
"context"
"fmt"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stephenafamo/bob"
"github.com/stephenafamo/bob/dialect/psql"
testutils "github.com/stephenafamo/bob/test/utils"
)
func TestUserByUsername(t *testing.T) {
t.Run("Base", func(t *testing.T) {
var sb strings.Builder
query := UserByUsername(random_string(nil))
if _, err := query.WriteQuery(t.Context(), &sb, 1); err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(userByUsernameSQL, sb.String()); diff != "" {
t.Fatalf("unexpected result (-got +want):\n%s", diff)
}
})
t.Run("Mod", func(t *testing.T) {
var sb strings.Builder
query := UserByUsername(random_string(nil))
if _, err := psql.Select(query).WriteQuery(t.Context(), &sb, 1); err != nil {
t.Fatal(err)
}
queryDiff, err := testutils.QueryDiff(userByUsernameSQL, sb.String(), formatQuery)
if err != nil {
t.Fatal(err)
}
if queryDiff != "" {
fmt.Println(sb.String())
t.Fatalf("unexpected result (-got +want):\n%s", queryDiff)
}
})
t.Run("Scanning", func(t *testing.T) {
if testDB == nil {
t.Skip("skipping test, no DSN provided")
}
ctxTx, cancel := context.WithCancel(t.Context())
defer cancel()
tx, err := testDB.Begin(ctxTx)
if err != nil {
t.Fatalf("Error starting transaction: %v", err)
}
defer func() {
if err := tx.Rollback(ctxTx); err != nil {
t.Fatalf("Error rolling back transaction: %v", err)
}
}()
query, args, err := bob.Build(ctxTx, psql.Select(UserByUsername(random_string(nil))))
if err != nil {
t.Fatal(err)
}
rows, err := tx.QueryContext(ctxTx, query, args...)
if err != nil {
t.Fatal(err)
}
defer rows.Close()
columns, err := rows.Columns()
if err != nil {
t.Fatal(err)
}
if len(columns) != 12 {
t.Fatalf("expected %d columns, got %d", 12, len(columns))
}
if columns[0] != "id" {
t.Fatalf("expected column %d to be %s, got %s", 0, "id", columns[0])
}
if columns[1] != "arcgis_access_token" {
t.Fatalf("expected column %d to be %s, got %s", 1, "arcgis_access_token", columns[1])
}
if columns[2] != "arcgis_license" {
t.Fatalf("expected column %d to be %s, got %s", 2, "arcgis_license", columns[2])
}
if columns[3] != "arcgis_refresh_token" {
t.Fatalf("expected column %d to be %s, got %s", 3, "arcgis_refresh_token", columns[3])
}
if columns[4] != "arcgis_refresh_token_expires" {
t.Fatalf("expected column %d to be %s, got %s", 4, "arcgis_refresh_token_expires", columns[4])
}
if columns[5] != "arcgis_role" {
t.Fatalf("expected column %d to be %s, got %s", 5, "arcgis_role", columns[5])
}
if columns[6] != "display_name" {
t.Fatalf("expected column %d to be %s, got %s", 6, "display_name", columns[6])
}
if columns[7] != "email" {
t.Fatalf("expected column %d to be %s, got %s", 7, "email", columns[7])
}
if columns[8] != "organization_id" {
t.Fatalf("expected column %d to be %s, got %s", 8, "organization_id", columns[8])
}
if columns[9] != "username" {
t.Fatalf("expected column %d to be %s, got %s", 9, "username", columns[9])
}
if columns[10] != "password_hash_type" {
t.Fatalf("expected column %d to be %s, got %s", 10, "password_hash_type", columns[10])
}
if columns[11] != "password_hash" {
t.Fatalf("expected column %d to be %s, got %s", 11, "password_hash", columns[11])
}
})
}

4
sql/user_by_username.sql Normal file
View file

@ -0,0 +1,4 @@
-- UserByUsername
SELECT * FROM user_ WHERE
username = $1 AND
password_hash_type = 'bcrypt-14';