287 lines
7.3 KiB
Go
287 lines
7.3 KiB
Go
package mysql
|
|
|
|
import (
|
|
"context"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"wucher/internal/domain/auth"
|
|
"wucher/internal/domain/user"
|
|
)
|
|
|
|
type userRoleJoinRow struct {
|
|
UserID []byte `gorm:"column:user_id"`
|
|
RoleID []byte `gorm:"column:role_id"`
|
|
RoleName string `gorm:"column:role_name"`
|
|
RoleDescription string `gorm:"column:role_description"`
|
|
}
|
|
|
|
func normalizeAssignedRoleIDs(primaryRoleID []byte, roleIDs [][]byte) [][]byte {
|
|
out := make([][]byte, 0, len(roleIDs)+1)
|
|
seen := make(map[string]struct{}, len(roleIDs)+1)
|
|
|
|
appendRole := func(roleID []byte) {
|
|
if len(roleID) != 16 {
|
|
return
|
|
}
|
|
key := string(roleID)
|
|
if _, exists := seen[key]; exists {
|
|
return
|
|
}
|
|
seen[key] = struct{}{}
|
|
out = append(out, append([]byte(nil), roleID...))
|
|
}
|
|
|
|
appendRole(primaryRoleID)
|
|
for i := range roleIDs {
|
|
appendRole(roleIDs[i])
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func replaceUserRolesTx(ctx context.Context, tx *gorm.DB, userID, primaryRoleID []byte, roleIDs [][]byte) error {
|
|
normalized := normalizeAssignedRoleIDs(primaryRoleID, roleIDs)
|
|
if len(userID) != 16 {
|
|
return nil
|
|
}
|
|
|
|
if err := tx.Where("user_id = ?", userID).Delete(&auth.UserRole{}).Error; err != nil {
|
|
return err
|
|
}
|
|
if len(normalized) == 0 {
|
|
return nil
|
|
}
|
|
|
|
actor := actorUserIDFromContext(ctx)
|
|
items := make([]auth.UserRole, 0, len(normalized))
|
|
for i := range normalized {
|
|
item := auth.UserRole{
|
|
UserID: userID,
|
|
RoleID: normalized[i],
|
|
}
|
|
if len(actor) > 0 {
|
|
item.CreatedBy = actor
|
|
item.UpdatedBy = actor
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
|
|
return tx.Create(&items).Error
|
|
}
|
|
|
|
func listUserRoleIDsTx(tx *gorm.DB, userID []byte) ([][]byte, error) {
|
|
var rows []struct {
|
|
RoleID []byte `gorm:"column:role_id"`
|
|
}
|
|
if err := tx.Table("user_roles").
|
|
Select("role_id").
|
|
Where("user_id = ?", userID).
|
|
Find(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
roleIDs := make([][]byte, 0, len(rows))
|
|
for i := range rows {
|
|
if len(rows[i].RoleID) != 16 {
|
|
continue
|
|
}
|
|
roleIDs = append(roleIDs, append([]byte(nil), rows[i].RoleID...))
|
|
}
|
|
return roleIDs, nil
|
|
}
|
|
|
|
func replacePrimaryUserRoleTx(ctx context.Context, tx *gorm.DB, userID, currentPrimaryRoleID, newPrimaryRoleID []byte) error {
|
|
roleIDs, err := listUserRoleIDsTx(tx, userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
filtered := make([][]byte, 0, len(roleIDs))
|
|
currentPrimaryKey := string(currentPrimaryRoleID)
|
|
newPrimaryKey := string(newPrimaryRoleID)
|
|
for i := range roleIDs {
|
|
key := string(roleIDs[i])
|
|
if key == currentPrimaryKey || key == newPrimaryKey {
|
|
continue
|
|
}
|
|
filtered = append(filtered, roleIDs[i])
|
|
}
|
|
|
|
return replaceUserRolesTx(ctx, tx, userID, newPrimaryRoleID, filtered)
|
|
}
|
|
|
|
func loadUserRoleRows(ctx context.Context, db *gorm.DB, userIDs [][]byte) (map[string][]userRoleJoinRow, error) {
|
|
rowsByUserID := make(map[string][]userRoleJoinRow, len(userIDs))
|
|
if len(userIDs) == 0 {
|
|
return rowsByUserID, nil
|
|
}
|
|
|
|
var rows []userRoleJoinRow
|
|
if err := db.WithContext(ctx).
|
|
Table("user_roles ur").
|
|
Select("ur.user_id, roles.id AS role_id, roles.name AS role_name, roles.description AS role_description").
|
|
Joins("JOIN roles ON roles.id = ur.role_id").
|
|
Where("ur.user_id IN ?", userIDs).
|
|
Order("roles.name ASC").
|
|
Find(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i := range rows {
|
|
key := string(rows[i].UserID)
|
|
rowsByUserID[key] = append(rowsByUserID[key], userRoleJoinRow{
|
|
UserID: append([]byte(nil), rows[i].UserID...),
|
|
RoleID: append([]byte(nil), rows[i].RoleID...),
|
|
RoleName: rows[i].RoleName,
|
|
RoleDescription: rows[i].RoleDescription,
|
|
})
|
|
}
|
|
|
|
return rowsByUserID, nil
|
|
}
|
|
|
|
func orderUserRoleRows(primaryRoleID []byte, rows []userRoleJoinRow) []userRoleJoinRow {
|
|
if len(rows) < 2 || len(primaryRoleID) != 16 {
|
|
return rows
|
|
}
|
|
|
|
primaryKey := string(primaryRoleID)
|
|
ordered := make([]userRoleJoinRow, 0, len(rows))
|
|
for i := range rows {
|
|
if string(rows[i].RoleID) == primaryKey {
|
|
ordered = append(ordered, rows[i])
|
|
break
|
|
}
|
|
}
|
|
for i := range rows {
|
|
if string(rows[i].RoleID) == primaryKey {
|
|
continue
|
|
}
|
|
ordered = append(ordered, rows[i])
|
|
}
|
|
return ordered
|
|
}
|
|
|
|
func loadRoleRowsByID(ctx context.Context, db *gorm.DB, roleIDs [][]byte) (map[string]userRoleJoinRow, error) {
|
|
out := make(map[string]userRoleJoinRow, len(roleIDs))
|
|
if len(roleIDs) == 0 {
|
|
return out, nil
|
|
}
|
|
|
|
var rows []struct {
|
|
ID []byte `gorm:"column:id"`
|
|
Name string `gorm:"column:name"`
|
|
Description string `gorm:"column:description"`
|
|
}
|
|
if err := db.WithContext(ctx).
|
|
Table("roles").
|
|
Select("id, name, description").
|
|
Where("id IN ?", roleIDs).
|
|
Find(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i := range rows {
|
|
out[string(rows[i].ID)] = userRoleJoinRow{
|
|
RoleID: append([]byte(nil), rows[i].ID...),
|
|
RoleName: rows[i].Name,
|
|
RoleDescription: rows[i].Description,
|
|
}
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func attachAuthUserRoles(ctx context.Context, db *gorm.DB, users ...*auth.User) error {
|
|
userIDs := make([][]byte, 0, len(users))
|
|
missingPrimaryRoleIDs := make([][]byte, 0, len(users))
|
|
for i := range users {
|
|
if users[i] == nil || len(users[i].ID) != 16 {
|
|
continue
|
|
}
|
|
userIDs = append(userIDs, users[i].ID)
|
|
if len(users[i].RoleID) == 16 {
|
|
missingPrimaryRoleIDs = append(missingPrimaryRoleIDs, users[i].RoleID)
|
|
}
|
|
}
|
|
|
|
rowsByUserID, err := loadUserRoleRows(ctx, db, userIDs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
roleRowsByID, err := loadRoleRowsByID(ctx, db, missingPrimaryRoleIDs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for i := range users {
|
|
if users[i] == nil {
|
|
continue
|
|
}
|
|
|
|
rows := orderUserRoleRows(users[i].RoleID, rowsByUserID[string(users[i].ID)])
|
|
if len(rows) == 0 && len(users[i].RoleID) == 16 {
|
|
if roleRow, ok := roleRowsByID[string(users[i].RoleID)]; ok {
|
|
rows = []userRoleJoinRow{roleRow}
|
|
}
|
|
}
|
|
|
|
users[i].RoleIDs = make([][]byte, 0, len(rows))
|
|
users[i].Roles = make([]auth.Role, 0, len(rows))
|
|
for j := range rows {
|
|
users[i].RoleIDs = append(users[i].RoleIDs, append([]byte(nil), rows[j].RoleID...))
|
|
users[i].Roles = append(users[i].Roles, auth.Role{
|
|
ID: append([]byte(nil), rows[j].RoleID...),
|
|
Name: rows[j].RoleName,
|
|
Description: rows[j].RoleDescription,
|
|
})
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func attachUserDomainRoles(ctx context.Context, db *gorm.DB, users ...*user.User) error {
|
|
userIDs := make([][]byte, 0, len(users))
|
|
for i := range users {
|
|
if users[i] == nil || len(users[i].ID) != 16 {
|
|
continue
|
|
}
|
|
userIDs = append(userIDs, users[i].ID)
|
|
}
|
|
|
|
rowsByUserID, err := loadUserRoleRows(ctx, db, userIDs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for i := range users {
|
|
if users[i] == nil {
|
|
continue
|
|
}
|
|
|
|
rows := orderUserRoleRows(users[i].RoleID, rowsByUserID[string(users[i].ID)])
|
|
if len(rows) == 0 && len(users[i].RoleID) == 16 {
|
|
rows = []userRoleJoinRow{{
|
|
RoleID: append([]byte(nil), users[i].RoleID...),
|
|
RoleName: users[i].RoleName,
|
|
RoleDescription: users[i].RoleDescription,
|
|
}}
|
|
}
|
|
|
|
users[i].RoleIDs = make([][]byte, 0, len(rows))
|
|
users[i].Roles = make([]user.RoleSummary, 0, len(rows))
|
|
for j := range rows {
|
|
users[i].RoleIDs = append(users[i].RoleIDs, append([]byte(nil), rows[j].RoleID...))
|
|
users[i].Roles = append(users[i].Roles, user.RoleSummary{
|
|
ID: append([]byte(nil), rows[j].RoleID...),
|
|
Name: rows[j].RoleName,
|
|
Description: rows[j].RoleDescription,
|
|
})
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|