fix(login): improve auth handlers (#7969)

# Which Problems Are Solved

During the implementation of #7486 it was noticed, that projections in
the `auth` database schema could be blocked.
Investigations suggested, that this is due to the use of
[GORM](https://gorm.io/index.html) and it's inability to use an existing
(sql) transaction.
With the improved / simplified handling (see below) there should also be
a minimal improvement in performance, resp. reduced database update
statements.

# How the Problems Are Solved

The handlers in `auth` are exchanged to proper (sql) statements and gorm
usage is removed for any writing part.
To further improve / simplify the handling of the users, a new
`auth.users3` table is created, where only attributes are handled, which
are not yet available from the `projections.users`,
`projections.login_name` and `projections.user_auth_methods` do not
provide. This reduces the events handled in that specific handler by a
lot.

# Additional Changes

None

# Additional Context

relates to #7486
This commit is contained in:
Livio Spring
2024-05-22 17:26:02 +02:00
committed by GitHub
parent cca342187b
commit fb162a7d75
25 changed files with 987 additions and 1279 deletions

View File

@@ -1,98 +1,42 @@
package view
import (
"github.com/jinzhu/gorm"
"context"
"database/sql"
_ "embed"
"errors"
"github.com/jinzhu/gorm"
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/domain"
usr_model "github.com/zitadel/zitadel/internal/user/model"
"github.com/zitadel/zitadel/internal/user/repository/view/model"
"github.com/zitadel/zitadel/internal/view/repository"
"github.com/zitadel/zitadel/internal/zerrors"
)
//go:embed user_by_id.sql
var userByIDQuery string
func UserByID(db *gorm.DB, table, userID, instanceID string) (*model.UserView, error) {
user := new(model.UserView)
userIDQuery := &model.UserSearchQuery{
Key: usr_model.UserSearchKeyUserID,
Method: domain.SearchMethodEquals,
Value: userID,
}
instanceIDQuery := &model.UserSearchQuery{
Key: usr_model.UserSearchKeyInstanceID,
Method: domain.SearchMethodEquals,
Value: instanceID,
}
ownerRemovedQuery := &model.UserSearchQuery{
Key: usr_model.UserSearchOwnerRemoved,
Method: domain.SearchMethodEquals,
Value: false,
}
query := repository.PrepareGetByQuery(table, userIDQuery, instanceIDQuery, ownerRemovedQuery)
err := query(db, user)
if zerrors.IsNotFound(err) {
return nil, zerrors.ThrowNotFound(nil, "VIEW-sj8Sw", "Errors.User.NotFound")
}
user.SetEmptyUserType()
return user, err
}
func UsersByOrgID(db *gorm.DB, table, orgID, instanceID string) ([]*model.UserView, error) {
users := make([]*model.UserView, 0)
orgIDQuery := &usr_model.UserSearchQuery{
Key: usr_model.UserSearchKeyResourceOwner,
Method: domain.SearchMethodEquals,
Value: orgID,
query := db.Raw(userByIDQuery, instanceID, userID)
tx := query.BeginTx(context.Background(), &sql.TxOptions{ReadOnly: true})
defer func() {
if err := tx.Commit().Error; err != nil {
logging.OnError(err).Info("commit failed")
}
tx.RollbackUnlessCommitted()
}()
err := tx.Scan(user).Error
if err == nil {
user.SetEmptyUserType()
return user, nil
}
instanceIDQuery := &usr_model.UserSearchQuery{
Key: usr_model.UserSearchKeyInstanceID,
Method: domain.SearchMethodEquals,
Value: instanceID,
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, zerrors.ThrowNotFound(err, "VIEW-hodc6", "object not found")
}
ownerRemovedQuery := &usr_model.UserSearchQuery{
Key: usr_model.UserSearchOwnerRemoved,
Method: domain.SearchMethodEquals,
Value: false,
}
query := repository.PrepareSearchQuery(table, model.UserSearchRequest{
Queries: []*usr_model.UserSearchQuery{orgIDQuery, instanceIDQuery, ownerRemovedQuery},
})
_, err := query(db, &users)
return users, err
}
func PutUsers(db *gorm.DB, table string, users ...*model.UserView) error {
save := repository.PrepareBulkSave(table)
u := make([]interface{}, len(users))
for i, user := range users {
u[i] = user
}
return save(db, u...)
}
func PutUser(db *gorm.DB, table string, user *model.UserView) error {
save := repository.PrepareSave(table)
return save(db, user)
}
func DeleteUser(db *gorm.DB, table, userID, instanceID string) error {
delete := repository.PrepareDeleteByKeys(table,
repository.Key{model.UserSearchKey(usr_model.UserSearchKeyUserID), userID},
repository.Key{model.UserSearchKey(usr_model.UserSearchKeyInstanceID), instanceID},
)
return delete(db)
}
func DeleteInstanceUsers(db *gorm.DB, table, instanceID string) error {
delete := repository.PrepareDeleteByKey(table, model.UserSearchKey(usr_model.UserSearchKeyInstanceID), instanceID)
return delete(db)
}
func UpdateOrgOwnerRemovedUsers(db *gorm.DB, table, instanceID, aggID string) error {
update := repository.PrepareUpdateByKeys(table,
model.UserSearchKey(usr_model.UserSearchOwnerRemoved),
true,
repository.Key{Key: model.UserSearchKey(usr_model.UserSearchKeyInstanceID), Value: instanceID},
repository.Key{Key: model.UserSearchKey(usr_model.UserSearchKeyResourceOwner), Value: aggID},
)
return update(db)
logging.WithFields("table ", table).WithError(err).Warn("get from cache error")
return nil, zerrors.ThrowInternal(err, "VIEW-qJBg9", "cache error")
}