Merge branch 'main' into v2.0.1_unshare_ownership

This commit is contained in:
Michael Quigley
2026-03-30 16:33:42 -04:00
4 changed files with 75 additions and 17 deletions
+2
View File
@@ -18,6 +18,8 @@ FEATURE: New `zrok2 delete access` subcommand that allows end users to clean up
FIX: Security hardening for the `/unaccess` endpoint.
FIX: Always return success on reset password request, even if account not found... unless there was actually an error.
## v2.0.0
FEATURE: Major changes to how "unique names" and "reserved sharing" work. See the [zrok v2 Migration Guide](https://docs.zrok.io) for details. Reserved sharing, including the `zrok reserve`, `zrok release` and `zrok share reserved` commands have been removed. Namespaces and reserved names replace these concepts in a much more powerful, flexible way which can accomplish what reserved sharing did in a much better way. (https://github.com/openziti/zrok/issues/726)
+10 -6
View File
@@ -1,6 +1,8 @@
package controller
import (
stderrors "errors"
"github.com/go-openapi/runtime/middleware"
"github.com/michaelquigley/df/dl"
"github.com/openziti/zrok/v2/controller/store"
@@ -25,8 +27,6 @@ func (handler *resetPasswordRequestHandler) Handle(params account.ResetPasswordR
}
dl.Infof("received reset password request for email '%v'", params.Body.EmailAddress)
var token string
trx, err := str.Begin()
if err != nil {
dl.Errorf("error starting transaction for request '%v': %v", params.Body.EmailAddress, err)
@@ -34,15 +34,19 @@ func (handler *resetPasswordRequestHandler) Handle(params account.ResetPasswordR
}
defer func() { _ = trx.Rollback() }()
token, err = CreateToken()
a, err := str.FindAccountWithEmail(params.Body.EmailAddress, trx)
if err != nil {
dl.Errorf("error creating token for '%v': %v", params.Body.EmailAddress, err)
if stderrors.Is(err, store.ErrAccountNotFound) {
dl.Infof("received reset password request for unknown email '%v'", params.Body.EmailAddress)
return account.NewResetPasswordRequestCreated()
}
dl.Errorf("error looking up account for '%v': %v", params.Body.EmailAddress, err)
return account.NewResetPasswordRequestInternalServerError()
}
a, err := str.FindAccountWithEmail(params.Body.EmailAddress, trx)
token, err := CreateToken()
if err != nil {
dl.Errorf("no account found for '%v': %v", params.Body.EmailAddress, err)
dl.Errorf("error creating token for '%v': %v", params.Body.EmailAddress, err)
return account.NewResetPasswordRequestInternalServerError()
}
+19 -11
View File
@@ -1,8 +1,11 @@
package store
import (
"database/sql"
stderrors "errors"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
pkgerrors "github.com/pkg/errors"
)
type Account struct {
@@ -15,14 +18,16 @@ type Account struct {
Deleted bool
}
var ErrAccountNotFound = stderrors.New("account not found")
func (str *Store) CreateAccount(a *Account, trx *sqlx.Tx) (int, error) {
stmt, err := trx.Prepare("insert into accounts (email, salt, password, token, limitless) values (lower($1), $2, $3, $4, $5) returning id")
if err != nil {
return 0, errors.Wrap(err, "error preparing accounts insert statement")
return 0, pkgerrors.Wrap(err, "error preparing accounts insert statement")
}
var id int
if err := stmt.QueryRow(a.Email, a.Salt, a.Password, a.Token, a.Limitless).Scan(&id); err != nil {
return 0, errors.Wrap(err, "error executing accounts insert statement")
return 0, pkgerrors.Wrap(err, "error executing accounts insert statement")
}
return id, nil
}
@@ -30,7 +35,7 @@ func (str *Store) CreateAccount(a *Account, trx *sqlx.Tx) (int, error) {
func (str *Store) GetAccount(id int, trx *sqlx.Tx) (*Account, error) {
a := &Account{}
if err := trx.QueryRowx("select * from accounts where id = $1", id).StructScan(a); err != nil {
return nil, errors.Wrap(err, "error selecting account by id")
return nil, pkgerrors.Wrap(err, "error selecting account by id")
}
return a, nil
}
@@ -38,7 +43,10 @@ func (str *Store) GetAccount(id int, trx *sqlx.Tx) (*Account, error) {
func (str *Store) FindAccountWithEmail(email string, trx *sqlx.Tx) (*Account, error) {
a := &Account{}
if err := trx.QueryRowx("select * from accounts where email = lower($1) and not deleted", email).StructScan(a); err != nil {
return nil, errors.Wrap(err, "error selecting account by email")
if stderrors.Is(err, sql.ErrNoRows) {
return nil, ErrAccountNotFound
}
return nil, pkgerrors.Wrap(err, "error selecting account by email")
}
return a, nil
}
@@ -46,7 +54,7 @@ func (str *Store) FindAccountWithEmail(email string, trx *sqlx.Tx) (*Account, er
func (str *Store) FindAccountWithEmailAndDeleted(email string, trx *sqlx.Tx) (*Account, error) {
a := &Account{}
if err := trx.QueryRowx("select * from accounts where email = lower($1)", email).StructScan(a); err != nil {
return nil, errors.Wrap(err, "error selecting acount by email")
return nil, pkgerrors.Wrap(err, "error selecting acount by email")
}
return a, nil
}
@@ -54,7 +62,7 @@ func (str *Store) FindAccountWithEmailAndDeleted(email string, trx *sqlx.Tx) (*A
func (str *Store) FindAccountWithToken(token string, trx *sqlx.Tx) (*Account, error) {
a := &Account{}
if err := trx.QueryRowx("select * from accounts where token = $1 and not deleted", token).StructScan(a); err != nil {
return nil, errors.Wrap(err, "error selecting account by token")
return nil, pkgerrors.Wrap(err, "error selecting account by token")
}
return a, nil
}
@@ -62,11 +70,11 @@ func (str *Store) FindAccountWithToken(token string, trx *sqlx.Tx) (*Account, er
func (str *Store) UpdateAccount(a *Account, trx *sqlx.Tx) (int, error) {
stmt, err := trx.Prepare("update accounts set email=lower($1), salt=$2, password=$3, token=$4, limitless=$5 where id = $6")
if err != nil {
return 0, errors.Wrap(err, "error preparing accounts update statement")
return 0, pkgerrors.Wrap(err, "error preparing accounts update statement")
}
var id int
if _, err := stmt.Exec(a.Email, a.Salt, a.Password, a.Token, a.Limitless, a.Id); err != nil {
return 0, errors.Wrap(err, "error executing accounts update statement")
return 0, pkgerrors.Wrap(err, "error executing accounts update statement")
}
return id, nil
}
@@ -74,10 +82,10 @@ func (str *Store) UpdateAccount(a *Account, trx *sqlx.Tx) (int, error) {
func (str *Store) DeleteAccount(id int, trx *sqlx.Tx) error {
stmt, err := trx.Prepare("update accounts set deleted = true where id = $1")
if err != nil {
return errors.Wrap(err, "error preparing accounts delete statement")
return pkgerrors.Wrap(err, "error preparing accounts delete statement")
}
if _, err := stmt.Exec(id); err != nil {
return errors.Wrap(err, "error executing accounts delete statement")
return pkgerrors.Wrap(err, "error executing accounts delete statement")
}
return nil
}
+44
View File
@@ -0,0 +1,44 @@
package store
import (
stderrors "errors"
"testing"
"github.com/stretchr/testify/require"
)
func TestFindAccountWithEmailReturnsNotFoundSentinel(t *testing.T) {
str, err := Open(&Config{Path: ":memory:", Type: "sqlite3"})
require.NoError(t, err)
trx, err := str.Begin()
require.NoError(t, err)
defer func() { _ = trx.Rollback() }()
account, err := str.FindAccountWithEmail("missing@test.com", trx)
require.Nil(t, account)
require.ErrorIs(t, err, ErrAccountNotFound)
require.True(t, stderrors.Is(err, ErrAccountNotFound))
}
func TestFindAccountWithEmailFindsExistingAccount(t *testing.T) {
str, err := Open(&Config{Path: ":memory:", Type: "sqlite3"})
require.NoError(t, err)
trx, err := str.Begin()
require.NoError(t, err)
defer func() { _ = trx.Rollback() }()
_, err = str.CreateAccount(&Account{
Email: "test@test.com",
Password: "password",
Token: "token",
Salt: "salt",
}, trx)
require.NoError(t, err)
account, err := str.FindAccountWithEmail("TEST@test.com", trx)
require.NoError(t, err)
require.NotNil(t, account)
require.Equal(t, "test@test.com", account.Email)
}