mirror of
https://github.com/1f349/tulip.git
synced 2025-01-28 18:26:40 +00:00
Finish up initdb implementation
This commit is contained in:
parent
1a7c13bb51
commit
20654eb70d
@ -3,13 +3,13 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"database/sql"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/1f349/mjwt"
|
"github.com/1f349/mjwt"
|
||||||
|
"github.com/1f349/tulip"
|
||||||
"github.com/1f349/tulip/database"
|
"github.com/1f349/tulip/database"
|
||||||
|
"github.com/1f349/tulip/database/types"
|
||||||
"github.com/1f349/tulip/mail/templates"
|
"github.com/1f349/tulip/mail/templates"
|
||||||
"github.com/1f349/tulip/pages"
|
"github.com/1f349/tulip/pages"
|
||||||
"github.com/1f349/tulip/server"
|
"github.com/1f349/tulip/server"
|
||||||
@ -20,6 +20,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type serveCmd struct{ configPath string }
|
type serveCmd struct{ configPath string }
|
||||||
@ -78,7 +79,7 @@ func normalLoad(startUp server.Conf, wd string) {
|
|||||||
log.Fatal("[Tulip] Failed to open signing key file:", err)
|
log.Fatal("[Tulip] Failed to open signing key file:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := database.Open(filepath.Join(wd, "tulip.db.sqlite"))
|
db, err := tulip.InitDB(filepath.Join(wd, "tulip.db.sqlite"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("[Tulip] Failed to open database:", err)
|
log.Fatal("[Tulip] Failed to open database:", err)
|
||||||
}
|
}
|
||||||
@ -102,42 +103,30 @@ func normalLoad(startUp server.Conf, wd string) {
|
|||||||
exit_reload.ExitReload("Tulip", func() {}, func() {
|
exit_reload.ExitReload("Tulip", func() {}, func() {
|
||||||
// stop http server
|
// stop http server
|
||||||
_ = srv.Close()
|
_ = srv.Close()
|
||||||
_ = db.Close()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func genHmacKey() []byte {
|
|
||||||
a := make([]byte, 32)
|
|
||||||
n, err := rand.Reader.Read(a)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("[Tulip] Failed to generate HMAC key")
|
|
||||||
}
|
|
||||||
if n != 32 {
|
|
||||||
log.Fatal("[Tulip] Failed to generate HMAC key")
|
|
||||||
}
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkDbHasUser(db *database.Queries) error {
|
func checkDbHasUser(db *database.Queries) error {
|
||||||
tx, err := db.Begin()
|
value, err := db.HasUser(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to start transaction: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
|
||||||
if err := tx.HasUser(); err != nil {
|
if !value {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
_, err := db.AddUser(context.Background(), database.AddUserParams{
|
||||||
_, err := tx.InsertUser("Admin", "admin", "admin", "admin@localhost", false, types.RoleAdmin, false)
|
Name: "Admin",
|
||||||
if err != nil {
|
Username: "admin",
|
||||||
return fmt.Errorf("failed to add user: %w", err)
|
Password: "admin",
|
||||||
}
|
Email: "admin@localhost",
|
||||||
if err := tx.Commit(); err != nil {
|
EmailVerified: false,
|
||||||
return fmt.Errorf("failed to commit transaction: %w", err)
|
Role: types.RoleAdmin,
|
||||||
}
|
UpdatedAt: time.Now(),
|
||||||
// continue normal operation now
|
Active: false,
|
||||||
return nil
|
})
|
||||||
} else {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to check if table has a user: %w", err)
|
return fmt.Errorf("failed to add user: %w", err)
|
||||||
}
|
}
|
||||||
|
// continue normal operation now
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -117,6 +117,24 @@ func (q *Queries) InsertClientApp(ctx context.Context, arg InsertClientAppParams
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const resetClientAppSecret = `-- name: ResetClientAppSecret :exec
|
||||||
|
UPDATE client_store
|
||||||
|
SET secret = ?
|
||||||
|
WHERE subject = ?
|
||||||
|
AND owner = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
type ResetClientAppSecretParams struct {
|
||||||
|
Secret string `json:"secret"`
|
||||||
|
Subject string `json:"subject"`
|
||||||
|
Owner string `json:"owner"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ResetClientAppSecret(ctx context.Context, arg ResetClientAppSecretParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, resetClientAppSecret, arg.Secret, arg.Subject, arg.Owner)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const updateClientApp = `-- name: UpdateClientApp :exec
|
const updateClientApp = `-- name: UpdateClientApp :exec
|
||||||
UPDATE client_store
|
UPDATE client_store
|
||||||
SET name = ?,
|
SET name = ?,
|
||||||
@ -150,21 +168,3 @@ func (q *Queries) UpdateClientApp(ctx context.Context, arg UpdateClientAppParams
|
|||||||
)
|
)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetClientAppSecret = `-- name: resetClientAppSecret :exec
|
|
||||||
UPDATE client_store
|
|
||||||
SET secret = ?
|
|
||||||
WHERE subject = ?
|
|
||||||
AND owner = ?
|
|
||||||
`
|
|
||||||
|
|
||||||
type resetClientAppSecretParams struct {
|
|
||||||
Secret string `json:"secret"`
|
|
||||||
Subject string `json:"subject"`
|
|
||||||
Owner string `json:"owner"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queries) resetClientAppSecret(ctx context.Context, arg resetClientAppSecretParams) error {
|
|
||||||
_, err := q.db.ExecContext(ctx, resetClientAppSecret, arg.Secret, arg.Subject, arg.Owner)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
@ -90,14 +90,14 @@ func (q *Queries) UpdateUserRole(ctx context.Context, arg UpdateUserRoleParams)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const userEmailExists = `-- name: UserEmailExists :one
|
const userEmailExists = `-- name: UserEmailExists :one
|
||||||
SELECT EXISTS(SELECT 1 FROM users WHERE email = ? AND email_verified = 1)
|
SELECT CAST(EXISTS(SELECT 1 FROM users WHERE email = ? AND email_verified = 1) AS BOOLEAN) AS email_exists
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) UserEmailExists(ctx context.Context, email string) (int64, error) {
|
func (q *Queries) UserEmailExists(ctx context.Context, email string) (bool, error) {
|
||||||
row := q.db.QueryRowContext(ctx, userEmailExists, email)
|
row := q.db.QueryRowContext(ctx, userEmailExists, email)
|
||||||
var column_1 int64
|
var email_exists bool
|
||||||
err := row.Scan(&column_1)
|
err := row.Scan(&email_exists)
|
||||||
return column_1, err
|
return email_exists, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const verifyUserEmail = `-- name: VerifyUserEmail :exec
|
const verifyUserEmail = `-- name: VerifyUserEmail :exec
|
||||||
|
@ -40,7 +40,8 @@ func (q *Queries) AddUser(ctx context.Context, arg AddUserParams) (string, error
|
|||||||
|
|
||||||
type CheckLoginResult struct {
|
type CheckLoginResult struct {
|
||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
HasTwoFactor bool `json:"hasTwoFactor"`
|
Name string `json:"name"`
|
||||||
|
HasOtp bool `json:"hasTwoFactor"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
EmailVerified bool `json:"email_verified"`
|
EmailVerified bool `json:"email_verified"`
|
||||||
}
|
}
|
||||||
@ -56,8 +57,26 @@ func (q *Queries) CheckLogin(ctx context.Context, un, pw string) (CheckLoginResu
|
|||||||
}
|
}
|
||||||
return CheckLoginResult{
|
return CheckLoginResult{
|
||||||
Subject: login.Subject,
|
Subject: login.Subject,
|
||||||
HasTwoFactor: login.HasOtp,
|
Name: login.Name,
|
||||||
|
HasOtp: login.HasOtp,
|
||||||
Email: login.Email,
|
Email: login.Email,
|
||||||
EmailVerified: login.EmailVerified,
|
EmailVerified: login.EmailVerified,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ChangePassword(ctx context.Context, subject, newPw string) error {
|
||||||
|
userPassword, err := q.getUserPassword(ctx, subject)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newPwHash, err := password.HashPassword(newPw)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return q.changeUserPassword(ctx, changeUserPasswordParams{
|
||||||
|
Password: newPwHash,
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
Subject: subject,
|
||||||
|
Password_2: userPassword,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -25,7 +25,7 @@ SET name = ?,
|
|||||||
WHERE subject = ?
|
WHERE subject = ?
|
||||||
AND owner = ?;
|
AND owner = ?;
|
||||||
|
|
||||||
-- name: resetClientAppSecret :exec
|
-- name: ResetClientAppSecret :exec
|
||||||
UPDATE client_store
|
UPDATE client_store
|
||||||
SET secret = ?
|
SET secret = ?
|
||||||
WHERE subject = ?
|
WHERE subject = ?
|
||||||
|
@ -23,4 +23,4 @@ SET email_verified = 1
|
|||||||
WHERE subject = ?;
|
WHERE subject = ?;
|
||||||
|
|
||||||
-- name: UserEmailExists :one
|
-- name: UserEmailExists :one
|
||||||
SELECT EXISTS(SELECT 1 FROM users WHERE email = ? AND email_verified = 1);
|
SELECT CAST(EXISTS(SELECT 1 FROM users WHERE email = ? AND email_verified = 1) AS BOOLEAN) AS email_exists;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
-- name: HasUser :one
|
-- name: HasUser :one
|
||||||
SELECT cast(count(subject) AS BOOLEAN) AS hasUser
|
SELECT CAST(count(subject) AS BOOLEAN) AS hasUser
|
||||||
FROM users;
|
FROM users;
|
||||||
|
|
||||||
-- name: addUser :exec
|
-- name: addUser :exec
|
||||||
@ -7,7 +7,7 @@ INSERT INTO users (subject, name, username, password, email, email_verified, rol
|
|||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
|
||||||
|
|
||||||
-- name: checkLogin :one
|
-- name: checkLogin :one
|
||||||
SELECT subject, password, cast(EXISTS(SELECT 1 FROM otp WHERE otp.subject = users.subject) AS BOOLEAN) as has_otp, email, email_verified
|
SELECT subject, name, password, CAST(EXISTS(SELECT 1 FROM otp WHERE otp.subject = users.subject) AS BOOLEAN) AS has_otp, email, email_verified
|
||||||
FROM users
|
FROM users
|
||||||
WHERE username = ?
|
WHERE username = ?
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
@ -33,14 +33,14 @@ SELECT password
|
|||||||
FROM users
|
FROM users
|
||||||
WHERE subject = ?;
|
WHERE subject = ?;
|
||||||
|
|
||||||
-- name: changeUserPassword :execrows
|
-- name: changeUserPassword :exec
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET password = ?,
|
SET password = ?,
|
||||||
updated_at = ?
|
updated_at = ?
|
||||||
WHERE subject = ?
|
WHERE subject = ?
|
||||||
AND password = ?;
|
AND password = ?;
|
||||||
|
|
||||||
-- name: ModifyUser :execrows
|
-- name: ModifyUser :exec
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET name = ?,
|
SET name = ?,
|
||||||
picture = ?,
|
picture = ?,
|
||||||
@ -52,21 +52,26 @@ SET name = ?,
|
|||||||
updated_at=?
|
updated_at=?
|
||||||
WHERE subject = ?;
|
WHERE subject = ?;
|
||||||
|
|
||||||
-- name: SetTwoFactor :exec
|
-- name: SetOtp :exec
|
||||||
INSERT OR
|
INSERT OR
|
||||||
REPLACE
|
REPLACE
|
||||||
INTO otp (subject, secret, digits)
|
INTO otp (subject, secret, digits)
|
||||||
VALUES (?, ?, ?);
|
VALUES (?, ?, ?);
|
||||||
|
|
||||||
-- name: DeleteTwoFactor :exec
|
-- name: DeleteOtp :exec
|
||||||
DELETE
|
DELETE
|
||||||
FROM otp
|
FROM otp
|
||||||
WHERE otp.subject = ?;
|
WHERE otp.subject = ?;
|
||||||
|
|
||||||
-- name: GetTwoFactor :one
|
-- name: GetOtp :one
|
||||||
SELECT secret, digits
|
SELECT secret, digits
|
||||||
FROM otp
|
FROM otp
|
||||||
WHERE subject = ?;
|
WHERE subject = ?;
|
||||||
|
|
||||||
-- name: HasTwoFactor :one
|
-- name: HasOtp :one
|
||||||
SELECT cast(EXISTS(SELECT 1 FROM otp WHERE subject = ?) AS BOOLEAN);
|
SELECT CAST(EXISTS(SELECT 1 FROM otp WHERE subject = ?) AS BOOLEAN);
|
||||||
|
|
||||||
|
-- name: GetUserEmail :one
|
||||||
|
SELECT email
|
||||||
|
FROM users
|
||||||
|
WHERE subject = ?;
|
||||||
|
@ -14,31 +14,31 @@ import (
|
|||||||
"github.com/1f349/tulip/password"
|
"github.com/1f349/tulip/password"
|
||||||
)
|
)
|
||||||
|
|
||||||
const deleteTwoFactor = `-- name: DeleteTwoFactor :exec
|
const deleteOtp = `-- name: DeleteOtp :exec
|
||||||
DELETE
|
DELETE
|
||||||
FROM otp
|
FROM otp
|
||||||
WHERE otp.subject = ?
|
WHERE otp.subject = ?
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) DeleteTwoFactor(ctx context.Context, subject string) error {
|
func (q *Queries) DeleteOtp(ctx context.Context, subject string) error {
|
||||||
_, err := q.db.ExecContext(ctx, deleteTwoFactor, subject)
|
_, err := q.db.ExecContext(ctx, deleteOtp, subject)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTwoFactor = `-- name: GetTwoFactor :one
|
const getOtp = `-- name: GetOtp :one
|
||||||
SELECT secret, digits
|
SELECT secret, digits
|
||||||
FROM otp
|
FROM otp
|
||||||
WHERE subject = ?
|
WHERE subject = ?
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetTwoFactorRow struct {
|
type GetOtpRow struct {
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
Digits int64 `json:"digits"`
|
Digits int64 `json:"digits"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetTwoFactor(ctx context.Context, subject string) (GetTwoFactorRow, error) {
|
func (q *Queries) GetOtp(ctx context.Context, subject string) (GetOtpRow, error) {
|
||||||
row := q.db.QueryRowContext(ctx, getTwoFactor, subject)
|
row := q.db.QueryRowContext(ctx, getOtp, subject)
|
||||||
var i GetTwoFactorRow
|
var i GetOtpRow
|
||||||
err := row.Scan(&i.Secret, &i.Digits)
|
err := row.Scan(&i.Secret, &i.Digits)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
@ -87,6 +87,19 @@ func (q *Queries) GetUserDisplayName(ctx context.Context, subject string) (strin
|
|||||||
return name, err
|
return name, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getUserEmail = `-- name: GetUserEmail :one
|
||||||
|
SELECT email
|
||||||
|
FROM users
|
||||||
|
WHERE subject = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetUserEmail(ctx context.Context, subject string) (string, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getUserEmail, subject)
|
||||||
|
var email string
|
||||||
|
err := row.Scan(&email)
|
||||||
|
return email, err
|
||||||
|
}
|
||||||
|
|
||||||
const getUserRole = `-- name: GetUserRole :one
|
const getUserRole = `-- name: GetUserRole :one
|
||||||
SELECT role
|
SELECT role
|
||||||
FROM users
|
FROM users
|
||||||
@ -100,19 +113,19 @@ func (q *Queries) GetUserRole(ctx context.Context, subject string) (types.UserRo
|
|||||||
return role, err
|
return role, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasTwoFactor = `-- name: HasTwoFactor :one
|
const hasOtp = `-- name: HasOtp :one
|
||||||
SELECT cast(EXISTS(SELECT 1 FROM otp WHERE subject = ?) AS BOOLEAN)
|
SELECT CAST(EXISTS(SELECT 1 FROM otp WHERE subject = ?) AS BOOLEAN)
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) HasTwoFactor(ctx context.Context, subject string) (bool, error) {
|
func (q *Queries) HasOtp(ctx context.Context, subject string) (bool, error) {
|
||||||
row := q.db.QueryRowContext(ctx, hasTwoFactor, subject)
|
row := q.db.QueryRowContext(ctx, hasOtp, subject)
|
||||||
var column_1 bool
|
var column_1 bool
|
||||||
err := row.Scan(&column_1)
|
err := row.Scan(&column_1)
|
||||||
return column_1, err
|
return column_1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasUser = `-- name: HasUser :one
|
const hasUser = `-- name: HasUser :one
|
||||||
SELECT cast(count(subject) AS BOOLEAN) AS hasUser
|
SELECT CAST(count(subject) AS BOOLEAN) AS hasUser
|
||||||
FROM users
|
FROM users
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -123,7 +136,7 @@ func (q *Queries) HasUser(ctx context.Context) (bool, error) {
|
|||||||
return hasuser, err
|
return hasuser, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const modifyUser = `-- name: ModifyUser :execrows
|
const modifyUser = `-- name: ModifyUser :exec
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET name = ?,
|
SET name = ?,
|
||||||
picture = ?,
|
picture = ?,
|
||||||
@ -148,8 +161,8 @@ type ModifyUserParams struct {
|
|||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) ModifyUser(ctx context.Context, arg ModifyUserParams) (int64, error) {
|
func (q *Queries) ModifyUser(ctx context.Context, arg ModifyUserParams) error {
|
||||||
result, err := q.db.ExecContext(ctx, modifyUser,
|
_, err := q.db.ExecContext(ctx, modifyUser,
|
||||||
arg.Name,
|
arg.Name,
|
||||||
arg.Picture,
|
arg.Picture,
|
||||||
arg.Website,
|
arg.Website,
|
||||||
@ -160,27 +173,24 @@ func (q *Queries) ModifyUser(ctx context.Context, arg ModifyUserParams) (int64,
|
|||||||
arg.UpdatedAt,
|
arg.UpdatedAt,
|
||||||
arg.Subject,
|
arg.Subject,
|
||||||
)
|
)
|
||||||
if err != nil {
|
return err
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return result.RowsAffected()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const setTwoFactor = `-- name: SetTwoFactor :exec
|
const setOtp = `-- name: SetOtp :exec
|
||||||
INSERT OR
|
INSERT OR
|
||||||
REPLACE
|
REPLACE
|
||||||
INTO otp (subject, secret, digits)
|
INTO otp (subject, secret, digits)
|
||||||
VALUES (?, ?, ?)
|
VALUES (?, ?, ?)
|
||||||
`
|
`
|
||||||
|
|
||||||
type SetTwoFactorParams struct {
|
type SetOtpParams struct {
|
||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
Digits int64 `json:"digits"`
|
Digits int64 `json:"digits"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) SetTwoFactor(ctx context.Context, arg SetTwoFactorParams) error {
|
func (q *Queries) SetOtp(ctx context.Context, arg SetOtpParams) error {
|
||||||
_, err := q.db.ExecContext(ctx, setTwoFactor, arg.Subject, arg.Secret, arg.Digits)
|
_, err := q.db.ExecContext(ctx, setOtp, arg.Subject, arg.Secret, arg.Digits)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +226,7 @@ func (q *Queries) addUser(ctx context.Context, arg addUserParams) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
const changeUserPassword = `-- name: changeUserPassword :execrows
|
const changeUserPassword = `-- name: changeUserPassword :exec
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET password = ?,
|
SET password = ?,
|
||||||
updated_at = ?
|
updated_at = ?
|
||||||
@ -231,21 +241,18 @@ type changeUserPasswordParams struct {
|
|||||||
Password_2 password.HashString `json:"password_2"`
|
Password_2 password.HashString `json:"password_2"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) changeUserPassword(ctx context.Context, arg changeUserPasswordParams) (int64, error) {
|
func (q *Queries) changeUserPassword(ctx context.Context, arg changeUserPasswordParams) error {
|
||||||
result, err := q.db.ExecContext(ctx, changeUserPassword,
|
_, err := q.db.ExecContext(ctx, changeUserPassword,
|
||||||
arg.Password,
|
arg.Password,
|
||||||
arg.UpdatedAt,
|
arg.UpdatedAt,
|
||||||
arg.Subject,
|
arg.Subject,
|
||||||
arg.Password_2,
|
arg.Password_2,
|
||||||
)
|
)
|
||||||
if err != nil {
|
return err
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return result.RowsAffected()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkLogin = `-- name: checkLogin :one
|
const checkLogin = `-- name: checkLogin :one
|
||||||
SELECT subject, password, cast(EXISTS(SELECT 1 FROM otp WHERE otp.subject = users.subject) AS BOOLEAN) as has_otp, email, email_verified
|
SELECT subject, name, password, CAST(EXISTS(SELECT 1 FROM otp WHERE otp.subject = users.subject) AS BOOLEAN) AS has_otp, email, email_verified
|
||||||
FROM users
|
FROM users
|
||||||
WHERE username = ?
|
WHERE username = ?
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
@ -253,6 +260,7 @@ LIMIT 1
|
|||||||
|
|
||||||
type checkLoginRow struct {
|
type checkLoginRow struct {
|
||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
|
Name string `json:"name"`
|
||||||
Password password.HashString `json:"password"`
|
Password password.HashString `json:"password"`
|
||||||
HasOtp bool `json:"has_otp"`
|
HasOtp bool `json:"has_otp"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
@ -264,6 +272,7 @@ func (q *Queries) checkLogin(ctx context.Context, username string) (checkLoginRo
|
|||||||
var i checkLoginRow
|
var i checkLoginRow
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.Subject,
|
&i.Subject,
|
||||||
|
&i.Name,
|
||||||
&i.Password,
|
&i.Password,
|
||||||
&i.HasOtp,
|
&i.HasOtp,
|
||||||
&i.Email,
|
&i.Email,
|
||||||
|
14
go.mod
14
go.mod
@ -4,7 +4,7 @@ go 1.22
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/1f349/cache v0.0.2
|
github.com/1f349/cache v0.0.2
|
||||||
github.com/1f349/mjwt v0.2.2
|
github.com/1f349/mjwt v0.2.5
|
||||||
github.com/1f349/overlapfs v0.0.1
|
github.com/1f349/overlapfs v0.0.1
|
||||||
github.com/1f349/violet v0.0.13
|
github.com/1f349/violet v0.0.13
|
||||||
github.com/MrMelon54/exit-reload v0.0.1
|
github.com/MrMelon54/exit-reload v0.0.1
|
||||||
@ -14,14 +14,15 @@ require (
|
|||||||
github.com/emersion/go-smtp v0.20.2
|
github.com/emersion/go-smtp v0.20.2
|
||||||
github.com/go-oauth2/oauth2/v4 v4.5.2
|
github.com/go-oauth2/oauth2/v4 v4.5.2
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.17.0
|
||||||
github.com/google/subcommands v1.2.0
|
github.com/google/subcommands v1.2.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/julienschmidt/httprouter v1.3.0
|
github.com/julienschmidt/httprouter v1.3.0
|
||||||
github.com/mattn/go-sqlite3 v1.14.22
|
github.com/mattn/go-sqlite3 v1.14.22
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/xlzd/gotp v0.1.0
|
github.com/xlzd/gotp v0.1.0
|
||||||
golang.org/x/crypto v0.19.0
|
golang.org/x/crypto v0.21.0
|
||||||
golang.org/x/text v0.14.0
|
golang.org/x/text v0.14.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,16 +32,19 @@ require (
|
|||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect
|
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/tidwall/btree v1.7.0 // indirect
|
github.com/tidwall/btree v1.7.0 // indirect
|
||||||
github.com/tidwall/buntdb v1.3.0 // indirect
|
github.com/tidwall/buntdb v1.3.0 // indirect
|
||||||
github.com/tidwall/gjson v1.17.0 // indirect
|
github.com/tidwall/gjson v1.17.1 // indirect
|
||||||
github.com/tidwall/grect v0.1.4 // indirect
|
github.com/tidwall/grect v0.1.4 // indirect
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
github.com/tidwall/rtred v0.1.2 // indirect
|
github.com/tidwall/rtred v0.1.2 // indirect
|
||||||
github.com/tidwall/tinyqueue v0.1.1 // indirect
|
github.com/tidwall/tinyqueue v0.1.1 // indirect
|
||||||
golang.org/x/net v0.21.0 // indirect
|
go.uber.org/atomic v1.11.0 // indirect
|
||||||
|
golang.org/x/net v0.22.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
37
go.sum
37
go.sum
@ -1,8 +1,8 @@
|
|||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/1f349/cache v0.0.2 h1:27QD6zPd9xYyvh9V1qqWq+EAt5+N+qvyGWKfnjMrhP8=
|
github.com/1f349/cache v0.0.2 h1:27QD6zPd9xYyvh9V1qqWq+EAt5+N+qvyGWKfnjMrhP8=
|
||||||
github.com/1f349/cache v0.0.2/go.mod h1:LibAMy13dF0KO1fQA9aEjZPBCB6Y4b5kKYEQJUqc2rQ=
|
github.com/1f349/cache v0.0.2/go.mod h1:LibAMy13dF0KO1fQA9aEjZPBCB6Y4b5kKYEQJUqc2rQ=
|
||||||
github.com/1f349/mjwt v0.2.2 h1:mVw71zcf0D7dWgZXMvjXMq8oNn41V1DFyLY0Ywkq1VQ=
|
github.com/1f349/mjwt v0.2.5 h1:IxjLaali22ayTzZ628lH7j0JDdYJoj6+CJ/VktCqtXQ=
|
||||||
github.com/1f349/mjwt v0.2.2/go.mod h1:KEs6jd9JjWrQW+8feP2pGAU7pdA3aYTqjkT/YQr73PU=
|
github.com/1f349/mjwt v0.2.5/go.mod h1:KEs6jd9JjWrQW+8feP2pGAU7pdA3aYTqjkT/YQr73PU=
|
||||||
github.com/1f349/overlapfs v0.0.1 h1:LAxBolrXFAgU0yqZtXg/C/aaPq3eoQSPpBc49BHuTp0=
|
github.com/1f349/overlapfs v0.0.1 h1:LAxBolrXFAgU0yqZtXg/C/aaPq3eoQSPpBc49BHuTp0=
|
||||||
github.com/1f349/overlapfs v0.0.1/go.mod h1:I6aItQycr7nrzplmfNXp/QF9tTmKRSgY3fXmu/7Ky2o=
|
github.com/1f349/overlapfs v0.0.1/go.mod h1:I6aItQycr7nrzplmfNXp/QF9tTmKRSgY3fXmu/7Ky2o=
|
||||||
github.com/1f349/violet v0.0.13 h1:lJpTz15Ea83Uc1VAISXTjtKuzr8Pe8NM4cMGp3Aiyhk=
|
github.com/1f349/violet v0.0.13 h1:lJpTz15Ea83Uc1VAISXTjtKuzr8Pe8NM4cMGp3Aiyhk=
|
||||||
@ -46,6 +46,8 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL
|
|||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.17.0 h1:rd40H3QXU0AA4IoLllFcEAEo9dYKRHYND2gB4p7xcaU=
|
||||||
|
github.com/golang-migrate/migrate/v4 v4.17.0/go.mod h1:+Cp2mtLP4/aXDTKb9wmXYitdrNx2HGs45rbWAo6OsKM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
@ -57,8 +59,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
|||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
|
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
|
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
|
||||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@ -69,6 +72,11 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR
|
|||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
||||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||||
@ -77,14 +85,17 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
|||||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
|
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
|
||||||
github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U=
|
|
||||||
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
|
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
|
||||||
|
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
@ -116,8 +127,8 @@ 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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI=
|
github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI=
|
||||||
github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8=
|
github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8=
|
||||||
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8=
|
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8=
|
||||||
@ -128,8 +139,8 @@ github.com/tidwall/buntdb v1.3.0 h1:gdhWO+/YwoB2qZMeAU9JcWWsHSYU3OvcieYgFRS0zwA=
|
|||||||
github.com/tidwall/buntdb v1.3.0/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU=
|
github.com/tidwall/buntdb v1.3.0/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU=
|
||||||
github.com/tidwall/gjson v1.3.4/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
|
github.com/tidwall/gjson v1.3.4/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
|
||||||
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
|
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
|
||||||
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb/go.mod h1:lKYYLFIr9OIgdgrtgkZ9zgRxRdvPYsExnYBsEAd8W5M=
|
github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb/go.mod h1:lKYYLFIr9OIgdgrtgkZ9zgRxRdvPYsExnYBsEAd8W5M=
|
||||||
github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg=
|
github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg=
|
||||||
github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q=
|
github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q=
|
||||||
@ -169,11 +180,13 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3Ifn
|
|||||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@ -188,8 +201,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
|
|||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
38
initdb.go
Normal file
38
initdb.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package tulip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"embed"
|
||||||
|
"errors"
|
||||||
|
"github.com/1f349/tulip/database"
|
||||||
|
"github.com/golang-migrate/migrate/v4"
|
||||||
|
"github.com/golang-migrate/migrate/v4/database/sqlite3"
|
||||||
|
"github.com/golang-migrate/migrate/v4/source/iofs"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed database/migrations/*.sql
|
||||||
|
var migrations embed.FS
|
||||||
|
|
||||||
|
func InitDB(p string) (*database.Queries, error) {
|
||||||
|
migDrv, err := iofs.New(migrations, "database/migrations")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dbOpen, err := sql.Open("sqlite3", p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dbDrv, err := sqlite3.WithInstance(dbOpen, &sqlite3.Config{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mig, err := migrate.NewWithInstance("iofs", migDrv, "sqlite3", dbDrv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = mig.Up()
|
||||||
|
if err != nil && !errors.Is(err, migrate.ErrNoChange) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return database.New(dbOpen), nil
|
||||||
|
}
|
@ -76,7 +76,7 @@ func (h *HttpServer) EditPost(rw http.ResponseWriter, req *http.Request, _ httpr
|
|||||||
Subject: auth.ID,
|
Subject: auth.ID,
|
||||||
}
|
}
|
||||||
if h.DbTx(rw, func(tx *database.Queries) error {
|
if h.DbTx(rw, func(tx *database.Queries) error {
|
||||||
if _, err := tx.ModifyUser(req.Context(), m); err != nil {
|
if err := tx.ModifyUser(req.Context(), m); err != nil {
|
||||||
return fmt.Errorf("failed to modify user info: %w", err)
|
return fmt.Errorf("failed to modify user info: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -38,7 +38,7 @@ func (h *HttpServer) Home(rw http.ResponseWriter, req *http.Request, _ httproute
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get user display name: %w", err)
|
return fmt.Errorf("failed to get user display name: %w", err)
|
||||||
}
|
}
|
||||||
hasTwoFactor, err = tx.HasTwoFactor(req.Context(), auth.ID)
|
hasTwoFactor, err = tx.HasOtp(req.Context(), auth.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get user two factor state: %w", err)
|
return fmt.Errorf("failed to get user two factor state: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ func (h *HttpServer) LoginPost(rw http.ResponseWriter, req *http.Request, _ http
|
|||||||
pw := req.FormValue("password")
|
pw := req.FormValue("password")
|
||||||
|
|
||||||
// flags returned from database call
|
// flags returned from database call
|
||||||
var userInfo *database.User
|
var userInfo database.CheckLoginResult
|
||||||
var loginMismatch byte
|
var loginMismatch byte
|
||||||
var hasOtp bool
|
var hasOtp bool
|
||||||
|
|
||||||
@ -74,8 +74,8 @@ func (h *HttpServer) LoginPost(rw http.ResponseWriter, req *http.Request, _ http
|
|||||||
}
|
}
|
||||||
|
|
||||||
userInfo = loginUser
|
userInfo = loginUser
|
||||||
hasOtp = hasOtpRaw
|
hasOtp = loginUser.HasOtp
|
||||||
if !hasVerifiedEmail {
|
if !loginUser.EmailVerified {
|
||||||
loginMismatch = 2
|
loginMismatch = 2
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -100,7 +100,7 @@ func (h *HttpServer) LoginPost(rw http.ResponseWriter, req *http.Request, _ http
|
|||||||
}
|
}
|
||||||
|
|
||||||
u := uuid.NewString()
|
u := uuid.NewString()
|
||||||
h.mailLinkCache.Set(mailLinkKey{mailLinkVerifyEmail, u}, userInfo.Sub, time.Now().Add(10*time.Minute))
|
h.mailLinkCache.Set(mailLinkKey{mailLinkVerifyEmail, u}, userInfo.Subject, time.Now().Add(10*time.Minute))
|
||||||
|
|
||||||
// try to send email
|
// try to send email
|
||||||
err = h.conf.Mail.SendEmailTemplate("mail-verify", "Verify Email", userInfo.Name, address, map[string]any{
|
err = h.conf.Mail.SendEmailTemplate("mail-verify", "Verify Email", userInfo.Name, address, map[string]any{
|
||||||
@ -122,7 +122,7 @@ func (h *HttpServer) LoginPost(rw http.ResponseWriter, req *http.Request, _ http
|
|||||||
|
|
||||||
// only continues if the above tx succeeds
|
// only continues if the above tx succeeds
|
||||||
auth = UserAuth{
|
auth = UserAuth{
|
||||||
ID: userInfo.Sub,
|
ID: userInfo.Subject,
|
||||||
NeedOtp: hasOtp,
|
NeedOtp: hasOtp,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ func (h *HttpServer) LoginResetPasswordPost(rw http.ResponseWriter, req *http.Re
|
|||||||
|
|
||||||
var emailExists bool
|
var emailExists bool
|
||||||
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
||||||
emailExists, err = tx.UserEmailExists(email)
|
emailExists, err = tx.UserEmailExists(req.Context(), email)
|
||||||
return err
|
return err
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
@ -190,4 +190,6 @@ func (h *HttpServer) LoginResetPasswordPost(rw http.ResponseWriter, req *http.Re
|
|||||||
|
|
||||||
func (h *HttpServer) possiblySendPasswordResetEmail(email string, exists bool) {
|
func (h *HttpServer) possiblySendPasswordResetEmail(email string, exists bool) {
|
||||||
// TODO(Melon): Send reset password email template
|
// TODO(Melon): Send reset password email template
|
||||||
|
_ = email
|
||||||
|
_ = exists
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,14 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/1f349/tulip/database"
|
"github.com/1f349/tulip/database"
|
||||||
|
"github.com/1f349/tulip/database/types"
|
||||||
"github.com/1f349/tulip/pages"
|
"github.com/1f349/tulip/pages"
|
||||||
"github.com/emersion/go-message/mail"
|
"github.com/emersion/go-message/mail"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *HttpServer) MailVerify(rw http.ResponseWriter, _ *http.Request, params httprouter.Params) {
|
func (h *HttpServer) MailVerify(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
|
||||||
code := params.ByName("code")
|
code := params.ByName("code")
|
||||||
|
|
||||||
k := mailLinkKey{mailLinkVerifyEmail, code}
|
k := mailLinkKey{mailLinkVerifyEmail, code}
|
||||||
@ -19,7 +20,7 @@ func (h *HttpServer) MailVerify(rw http.ResponseWriter, _ *http.Request, params
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if h.DbTx(rw, func(tx *database.Queries) error {
|
if h.DbTx(rw, func(tx *database.Queries) error {
|
||||||
return tx.VerifyUserEmail(userSub)
|
return tx.VerifyUserEmail(req.Context(), userSub)
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -76,7 +77,7 @@ func (h *HttpServer) MailPasswordPost(rw http.ResponseWriter, req *http.Request,
|
|||||||
|
|
||||||
// reset password database call
|
// reset password database call
|
||||||
if h.DbTx(rw, func(tx *database.Queries) error {
|
if h.DbTx(rw, func(tx *database.Queries) error {
|
||||||
return tx.UserResetPassword(userSub, pw)
|
return tx.ChangePassword(req.Context(), userSub, pw)
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -84,7 +85,7 @@ func (h *HttpServer) MailPasswordPost(rw http.ResponseWriter, req *http.Request,
|
|||||||
http.Error(rw, "Reset password successfully, you can login now.", http.StatusOK)
|
http.Error(rw, "Reset password successfully, you can login now.", http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HttpServer) MailDelete(rw http.ResponseWriter, _ *http.Request, params httprouter.Params) {
|
func (h *HttpServer) MailDelete(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
|
||||||
code := params.ByName("code")
|
code := params.ByName("code")
|
||||||
|
|
||||||
k := mailLinkKey{mailLinkDelete, code}
|
k := mailLinkKey{mailLinkDelete, code}
|
||||||
@ -93,13 +94,17 @@ func (h *HttpServer) MailDelete(rw http.ResponseWriter, _ *http.Request, params
|
|||||||
http.Error(rw, "Invalid email delete code", http.StatusBadRequest)
|
http.Error(rw, "Invalid email delete code", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var userInfo *database.User
|
var userInfo database.User
|
||||||
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
||||||
userInfo, err = tx.GetUser(userSub)
|
userInfo, err = tx.GetUser(req.Context(), userSub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return tx.UpdateUser(userSub, types.RoleToDelete, false)
|
return tx.UpdateUserRole(req.Context(), database.UpdateUserRoleParams{
|
||||||
|
Active: false,
|
||||||
|
Role: types.RoleToDelete,
|
||||||
|
Subject: userSub,
|
||||||
|
})
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,10 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/1f349/tulip/database"
|
"github.com/1f349/tulip/database"
|
||||||
|
"github.com/1f349/tulip/database/types"
|
||||||
"github.com/1f349/tulip/pages"
|
"github.com/1f349/tulip/pages"
|
||||||
"github.com/go-oauth2/oauth2/v4"
|
"github.com/1f349/tulip/password"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -23,13 +25,17 @@ func (h *HttpServer) ManageAppsGet(rw http.ResponseWriter, req *http.Request, _
|
|||||||
}
|
}
|
||||||
|
|
||||||
var role types.UserRole
|
var role types.UserRole
|
||||||
var appList []database.ClientStore
|
var appList []database.GetAppListRow
|
||||||
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
||||||
role, err = tx.GetUserRole(auth.ID)
|
role, err = tx.GetUserRole(req.Context(), auth.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
appList, err = tx.GetAppList(auth.ID, role == types.RoleAdmin, offset)
|
appList, err = tx.GetAppList(req.Context(), database.GetAppListParams{
|
||||||
|
Owner: auth.ID,
|
||||||
|
Column2: role == types.RoleAdmin,
|
||||||
|
Offset: int64(offset),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
@ -45,7 +51,7 @@ func (h *HttpServer) ManageAppsGet(rw http.ResponseWriter, req *http.Request, _
|
|||||||
}
|
}
|
||||||
if q.Has("edit") {
|
if q.Has("edit") {
|
||||||
for _, i := range appList {
|
for _, i := range appList {
|
||||||
if i.Sub == q.Get("edit") {
|
if i.Subject == q.Get("edit") {
|
||||||
m["Edit"] = i
|
m["Edit"] = i
|
||||||
goto validEdit
|
goto validEdit
|
||||||
}
|
}
|
||||||
@ -78,7 +84,7 @@ func (h *HttpServer) ManageAppsPost(rw http.ResponseWriter, req *http.Request, _
|
|||||||
if sso {
|
if sso {
|
||||||
var role types.UserRole
|
var role types.UserRole
|
||||||
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
||||||
role, err = tx.GetUserRole(auth.ID)
|
role, err = tx.GetUserRole(req.Context(), auth.ID)
|
||||||
return
|
return
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
@ -92,35 +98,61 @@ func (h *HttpServer) ManageAppsPost(rw http.ResponseWriter, req *http.Request, _
|
|||||||
switch action {
|
switch action {
|
||||||
case "create":
|
case "create":
|
||||||
if h.DbTx(rw, func(tx *database.Queries) error {
|
if h.DbTx(rw, func(tx *database.Queries) error {
|
||||||
return tx.InsertClientApp(name, domain, public, sso, active, auth.ID)
|
secret, err := password.GenerateApiSecret(70)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return tx.InsertClientApp(req.Context(), database.InsertClientAppParams{
|
||||||
|
Subject: uuid.NewString(),
|
||||||
|
Name: name,
|
||||||
|
Secret: secret,
|
||||||
|
Domain: domain,
|
||||||
|
Owner: auth.ID,
|
||||||
|
Public: public,
|
||||||
|
Sso: sso,
|
||||||
|
Active: active,
|
||||||
|
})
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case "edit":
|
case "edit":
|
||||||
if h.DbTx(rw, func(tx *database.Queries) error {
|
if h.DbTx(rw, func(tx *database.Queries) error {
|
||||||
return tx.UpdateClientApp(req.Form.Get("subject"), auth.ID, name, domain, public, sso, active)
|
return tx.UpdateClientApp(req.Context(), database.UpdateClientAppParams{
|
||||||
|
Name: name,
|
||||||
|
Domain: domain,
|
||||||
|
Public: public,
|
||||||
|
Sso: sso,
|
||||||
|
Active: active,
|
||||||
|
Subject: req.FormValue("subject"),
|
||||||
|
Owner: auth.ID,
|
||||||
|
})
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case "secret":
|
case "secret":
|
||||||
var info oauth2.ClientInfo
|
var info database.ClientStore
|
||||||
var secret string
|
var secret string
|
||||||
if h.DbTx(rw, func(tx *database.Queries) error {
|
if h.DbTx(rw, func(tx *database.Queries) error {
|
||||||
sub := req.Form.Get("subject")
|
sub := req.Form.Get("subject")
|
||||||
info, err = tx.GetClientInfo(sub)
|
info, err = tx.GetClientInfo(req.Context(), sub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
secret, err = tx.ResetClientAppSecret(sub, auth.ID)
|
secret, err := password.GenerateApiSecret(70)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = tx.ResetClientAppSecret(req.Context(), database.ResetClientAppSecretParams{
|
||||||
|
Secret: secret,
|
||||||
|
Subject: sub,
|
||||||
|
Owner: auth.ID,
|
||||||
|
})
|
||||||
return err
|
return err
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
appName := "Unknown..."
|
appName := info.GetName()
|
||||||
if getName, ok := info.(interface{ GetName() string }); ok {
|
|
||||||
appName = getName.GetName()
|
|
||||||
}
|
|
||||||
|
|
||||||
h.ManageAppsGet(rw, &http.Request{
|
h.ManageAppsGet(rw, &http.Request{
|
||||||
URL: &url.URL{
|
URL: &url.URL{
|
||||||
|
@ -29,17 +29,18 @@ func (h *HttpServer) ManageUsersGet(rw http.ResponseWriter, req *http.Request, _
|
|||||||
}
|
}
|
||||||
|
|
||||||
var role types.UserRole
|
var role types.UserRole
|
||||||
var userList []database.User
|
var userList []database.GetUserListRow
|
||||||
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
||||||
role, err = tx.GetUserRole(auth.ID)
|
role, err = tx.GetUserRole(req.Context(), auth.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userList, err = tx.GetUserList(offset)
|
userList, err = tx.GetUserList(req.Context(), int64(offset))
|
||||||
return
|
return
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if role != types.RoleAdmin {
|
if role != types.RoleAdmin {
|
||||||
http.Error(rw, "403 Forbidden", http.StatusForbidden)
|
http.Error(rw, "403 Forbidden", http.StatusForbidden)
|
||||||
return
|
return
|
||||||
@ -55,7 +56,7 @@ func (h *HttpServer) ManageUsersGet(rw http.ResponseWriter, req *http.Request, _
|
|||||||
}
|
}
|
||||||
if q.Has("edit") {
|
if q.Has("edit") {
|
||||||
for _, i := range userList {
|
for _, i := range userList {
|
||||||
if i.Sub == q.Get("edit") {
|
if i.Subject == q.Get("edit") {
|
||||||
m["Edit"] = i
|
m["Edit"] = i
|
||||||
goto validEdit
|
goto validEdit
|
||||||
}
|
}
|
||||||
@ -79,7 +80,7 @@ func (h *HttpServer) ManageUsersPost(rw http.ResponseWriter, req *http.Request,
|
|||||||
|
|
||||||
var role types.UserRole
|
var role types.UserRole
|
||||||
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
||||||
role, err = tx.GetUserRole(auth.ID)
|
role, err = tx.GetUserRole(req.Context(), auth.ID)
|
||||||
return
|
return
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
@ -116,17 +117,26 @@ func (h *HttpServer) ManageUsersPost(rw http.ResponseWriter, req *http.Request,
|
|||||||
}
|
}
|
||||||
addrDomain := address.Address[n+1:]
|
addrDomain := address.Address[n+1:]
|
||||||
|
|
||||||
var userSub uuid.UUID
|
var userSub string
|
||||||
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
||||||
userSub, err = tx.InsertUser(name, username, "", email, addrDomain == h.conf.Namespace, newRole, active)
|
userSub, err = tx.AddUser(req.Context(), database.AddUserParams{
|
||||||
|
Name: name,
|
||||||
|
Username: username,
|
||||||
|
Password: "",
|
||||||
|
Email: email,
|
||||||
|
EmailVerified: addrDomain == h.conf.Namespace,
|
||||||
|
Role: newRole,
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
Active: active,
|
||||||
|
})
|
||||||
return err
|
return err
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
u, u2 := uuid.NewString(), uuid.NewString()
|
u, u2 := uuid.NewString(), uuid.NewString()
|
||||||
h.mailLinkCache.Set(mailLinkKey{mailLinkResetPassword, u}, userSub.String(), time.Now().Add(10*time.Minute))
|
h.mailLinkCache.Set(mailLinkKey{mailLinkResetPassword, u}, userSub, time.Now().Add(10*time.Minute))
|
||||||
h.mailLinkCache.Set(mailLinkKey{mailLinkDelete, u2}, userSub.String(), time.Now().Add(10*time.Minute))
|
h.mailLinkCache.Set(mailLinkKey{mailLinkDelete, u2}, userSub, time.Now().Add(10*time.Minute))
|
||||||
|
|
||||||
err = h.conf.Mail.SendEmailTemplate("mail-register-admin", "Register", name, address, map[string]any{
|
err = h.conf.Mail.SendEmailTemplate("mail-register-admin", "Register", name, address, map[string]any{
|
||||||
"RegisterUrl": h.conf.BaseUrl + "/mail/password/" + u,
|
"RegisterUrl": h.conf.BaseUrl + "/mail/password/" + u,
|
||||||
@ -139,7 +149,11 @@ func (h *HttpServer) ManageUsersPost(rw http.ResponseWriter, req *http.Request,
|
|||||||
case "edit":
|
case "edit":
|
||||||
if h.DbTx(rw, func(tx *database.Queries) error {
|
if h.DbTx(rw, func(tx *database.Queries) error {
|
||||||
sub := req.Form.Get("subject")
|
sub := req.Form.Get("subject")
|
||||||
return tx.UpdateUser(sub, newRole, active)
|
return tx.UpdateUserRole(req.Context(), database.UpdateUserRoleParams{
|
||||||
|
Active: active,
|
||||||
|
Role: newRole,
|
||||||
|
Subject: sub,
|
||||||
|
})
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -77,17 +77,14 @@ func (h *HttpServer) authorizeEndpoint(rw http.ResponseWriter, req *http.Request
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var user *database.User
|
var user string
|
||||||
var hasOtp bool
|
var hasOtp bool
|
||||||
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
||||||
user, err = tx.GetUserDisplayName(auth.ID)
|
user, err = tx.GetUserDisplayName(req.Context(), auth.ID)
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
hasOtp, err = tx.HasTwoFactor(auth.ID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
hasOtp, err = tx.HasOtp(req.Context(), auth.ID)
|
||||||
return
|
return
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
|
@ -2,6 +2,7 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"github.com/1f349/tulip/database"
|
"github.com/1f349/tulip/database"
|
||||||
"github.com/1f349/tulip/pages"
|
"github.com/1f349/tulip/pages"
|
||||||
@ -45,15 +46,18 @@ func (h *HttpServer) LoginOtpPost(rw http.ResponseWriter, req *http.Request, _ h
|
|||||||
|
|
||||||
func (h *HttpServer) fetchAndValidateOtp(rw http.ResponseWriter, sub, code string) bool {
|
func (h *HttpServer) fetchAndValidateOtp(rw http.ResponseWriter, sub, code string) bool {
|
||||||
var hasOtp bool
|
var hasOtp bool
|
||||||
|
var otpRow database.GetOtpRow
|
||||||
var secret string
|
var secret string
|
||||||
var digits int
|
var digits int64
|
||||||
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
if h.DbTx(rw, func(tx *database.Queries) (err error) {
|
||||||
hasOtp, err = tx.HasTwoFactor(sub)
|
hasOtp, err = tx.HasOtp(context.Background(), sub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if hasOtp {
|
if hasOtp {
|
||||||
secret, digits, err = tx.GetTwoFactor(sub)
|
otpRow, err = tx.GetOtp(context.Background(), sub)
|
||||||
|
secret = otpRow.Secret
|
||||||
|
digits = otpRow.Digits
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}) {
|
}) {
|
||||||
@ -61,7 +65,7 @@ func (h *HttpServer) fetchAndValidateOtp(rw http.ResponseWriter, sub, code strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
if hasOtp {
|
if hasOtp {
|
||||||
totp := gotp.NewTOTP(secret, digits, 30, nil)
|
totp := gotp.NewTOTP(secret, int(digits), 30, nil)
|
||||||
if !verifyTotp(totp, code) {
|
if !verifyTotp(totp, code) {
|
||||||
http.Error(rw, "400 Bad Request: Invalid OTP code", http.StatusBadRequest)
|
http.Error(rw, "400 Bad Request: Invalid OTP code", http.StatusBadRequest)
|
||||||
return true
|
return true
|
||||||
@ -87,7 +91,11 @@ func (h *HttpServer) EditOtpPost(rw http.ResponseWriter, req *http.Request, _ ht
|
|||||||
}
|
}
|
||||||
|
|
||||||
if h.DbTx(rw, func(tx *database.Queries) error {
|
if h.DbTx(rw, func(tx *database.Queries) error {
|
||||||
return tx.SetTwoFactor(auth.ID, "", 0)
|
return tx.SetOtp(req.Context(), database.SetOtpParams{
|
||||||
|
Subject: auth.ID,
|
||||||
|
Secret: "",
|
||||||
|
Digits: 0,
|
||||||
|
})
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -120,7 +128,7 @@ func (h *HttpServer) EditOtpPost(rw http.ResponseWriter, req *http.Request, _ ht
|
|||||||
var email string
|
var email string
|
||||||
if h.DbTx(rw, func(tx *database.Queries) error {
|
if h.DbTx(rw, func(tx *database.Queries) error {
|
||||||
var err error
|
var err error
|
||||||
email, err = tx.GetUserEmail(auth.ID)
|
email, err = tx.GetUserEmail(req.Context(), auth.ID)
|
||||||
return err
|
return err
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
@ -168,7 +176,11 @@ func (h *HttpServer) EditOtpPost(rw http.ResponseWriter, req *http.Request, _ ht
|
|||||||
}
|
}
|
||||||
|
|
||||||
if h.DbTx(rw, func(tx *database.Queries) error {
|
if h.DbTx(rw, func(tx *database.Queries) error {
|
||||||
return tx.SetTwoFactor(auth.ID, secret, digits)
|
return tx.SetOtp(req.Context(), database.SetOtpParams{
|
||||||
|
Subject: auth.ID,
|
||||||
|
Secret: secret,
|
||||||
|
Digits: int64(digits),
|
||||||
|
})
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,7 @@ func NewHttpServer(conf Conf, db *database.Queries, signingKey mjwt.Signer) *htt
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var userData database.GetUserRow
|
var userData database.User
|
||||||
|
|
||||||
if hs.DbTx(rw, func(tx *database.Queries) (err error) {
|
if hs.DbTx(rw, func(tx *database.Queries) (err error) {
|
||||||
userData, err = tx.GetUser(req.Context(), userId)
|
userData, err = tx.GetUser(req.Context(), userId)
|
||||||
@ -212,21 +212,21 @@ func NewHttpServer(conf Conf, db *database.Queries, signingKey mjwt.Signer) *htt
|
|||||||
}
|
}
|
||||||
if claims["profile"] {
|
if claims["profile"] {
|
||||||
m["profile"] = conf.BaseUrl + "/user/" + userData.Username
|
m["profile"] = conf.BaseUrl + "/user/" + userData.Username
|
||||||
m["picture"] = userData.Picture.String()
|
m["picture"] = userData.Picture
|
||||||
m["website"] = userData.Website.String()
|
m["website"] = userData.Website
|
||||||
}
|
}
|
||||||
if claims["email"] {
|
if claims["email"] {
|
||||||
m["email"] = userData.Email
|
m["email"] = userData.Email
|
||||||
m["email_verified"] = userData.EmailVerified
|
m["email_verified"] = userData.EmailVerified
|
||||||
}
|
}
|
||||||
if claims["birthdate"] {
|
if claims["birthdate"] && userData.Birthdate.Valid {
|
||||||
m["birthdate"] = userData.Birthdate.String()
|
m["birthdate"] = userData.Birthdate.Time.String()
|
||||||
}
|
}
|
||||||
if claims["age"] {
|
if claims["age"] {
|
||||||
m["age"] = CalculateAge(userData.Birthdate.Time.In(userData.ZoneInfo.Location))
|
m["age"] = CalculateAge(userData.Birthdate.Time.In(userData.Zoneinfo.Location))
|
||||||
}
|
}
|
||||||
if claims["zoneinfo"] {
|
if claims["zoneinfo"] {
|
||||||
m["zoneinfo"] = userData.ZoneInfo.Location.String()
|
m["zoneinfo"] = userData.Zoneinfo.Location.String()
|
||||||
}
|
}
|
||||||
if claims["locale"] {
|
if claims["locale"] {
|
||||||
m["locale"] = userData.Locale.Tag.String()
|
m["locale"] = userData.Locale.Tag.String()
|
||||||
|
Loading…
Reference in New Issue
Block a user