- Updated TestMemberInvitationExpiry to use a consistent expired time for invitations. - Simplified invitation expiration check in AcceptInvitation by utilizing a new checkTimeExpired function. - Enhanced member ID parsing in AcceptInvitation to handle potential errors more gracefully. - Refactored total extraction logic in CountOAuthAccounts, CountRoles, CountTeams, CountTypes, and CountUsers to use a new utility function for improved clarity and consistency. - Introduced parseTimeFromDB and parseIntFromDB functions to streamline data parsing from database fields.
339 lines
9.1 KiB
Go
339 lines
9.1 KiB
Go
package user
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/yaoapp/gou/model"
|
|
"github.com/yaoapp/kun/maps"
|
|
)
|
|
|
|
// Role Resource
|
|
|
|
// GetRole retrieves role information by role_id
|
|
func (u *DefaultUser) GetRole(ctx context.Context, roleID string) (maps.MapStrAny, error) {
|
|
m := model.Select(u.roleModel)
|
|
roles, err := m.Get(model.QueryParam{
|
|
Select: u.roleFields,
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "role_id", Value: roleID},
|
|
},
|
|
Limit: 1,
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf(ErrFailedToGetRole, err)
|
|
}
|
|
|
|
if len(roles) == 0 {
|
|
return nil, fmt.Errorf(ErrRoleNotFound)
|
|
}
|
|
|
|
return roles[0], nil
|
|
}
|
|
|
|
// RoleExists checks if a role exists by role_id (lightweight query)
|
|
func (u *DefaultUser) RoleExists(ctx context.Context, roleID string) (bool, error) {
|
|
m := model.Select(u.roleModel)
|
|
roles, err := m.Get(model.QueryParam{
|
|
Select: []interface{}{"id"}, // Only select ID for existence check
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "role_id", Value: roleID},
|
|
},
|
|
Limit: 1, // Only need to know if at least one exists
|
|
})
|
|
|
|
if err != nil {
|
|
return false, fmt.Errorf(ErrFailedToGetRole, err)
|
|
}
|
|
|
|
return len(roles) > 0, nil
|
|
}
|
|
|
|
// CreateRole creates a new user role
|
|
func (u *DefaultUser) CreateRole(ctx context.Context, roleData maps.MapStrAny) (string, error) {
|
|
// Validate required role_id field
|
|
if _, exists := roleData["role_id"]; !exists {
|
|
return "", fmt.Errorf("role_id is required in roleData")
|
|
}
|
|
|
|
// Set default values if not provided
|
|
if _, exists := roleData["is_active"]; !exists {
|
|
roleData["is_active"] = true
|
|
}
|
|
if _, exists := roleData["is_default"]; !exists {
|
|
roleData["is_default"] = false
|
|
}
|
|
if _, exists := roleData["is_system"]; !exists {
|
|
roleData["is_system"] = false
|
|
}
|
|
if _, exists := roleData["level"]; !exists {
|
|
roleData["level"] = 0
|
|
}
|
|
if _, exists := roleData["sort_order"]; !exists {
|
|
roleData["sort_order"] = 0
|
|
}
|
|
|
|
m := model.Select(u.roleModel)
|
|
id, err := m.Create(roleData)
|
|
if err != nil {
|
|
return "", fmt.Errorf(ErrFailedToCreateRole, err)
|
|
}
|
|
|
|
// Return the role_id as string (preferred approach)
|
|
if roleID, ok := roleData["role_id"].(string); ok {
|
|
return roleID, nil
|
|
}
|
|
|
|
// Fallback: convert the returned int id to string
|
|
return fmt.Sprintf("%d", id), nil
|
|
}
|
|
|
|
// UpdateRole updates an existing role
|
|
func (u *DefaultUser) UpdateRole(ctx context.Context, roleID string, roleData maps.MapStrAny) error {
|
|
// Remove sensitive fields that should not be updated directly
|
|
sensitiveFields := []string{"id", "role_id", "created_at"}
|
|
for _, field := range sensitiveFields {
|
|
delete(roleData, field)
|
|
}
|
|
|
|
// Skip update if no valid fields remain
|
|
if len(roleData) == 0 {
|
|
return nil
|
|
}
|
|
|
|
m := model.Select(u.roleModel)
|
|
affected, err := m.UpdateWhere(model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "role_id", Value: roleID},
|
|
},
|
|
Limit: 1, // Safety: ensure only one record is updated
|
|
}, roleData)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf(ErrFailedToUpdateRole, err)
|
|
}
|
|
|
|
if affected == 0 {
|
|
return fmt.Errorf(ErrRoleNotFound)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeleteRole soft deletes a role (if not system role)
|
|
func (u *DefaultUser) DeleteRole(ctx context.Context, roleID string) error {
|
|
// First check if role exists and is not a system role
|
|
m := model.Select(u.roleModel)
|
|
roles, err := m.Get(model.QueryParam{
|
|
Select: []interface{}{"id", "role_id", "is_system"},
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "role_id", Value: roleID},
|
|
},
|
|
Limit: 1,
|
|
})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf(ErrFailedToGetRole, err)
|
|
}
|
|
|
|
if len(roles) == 0 {
|
|
return fmt.Errorf(ErrRoleNotFound)
|
|
}
|
|
|
|
role := roles[0]
|
|
// Check if this is a system role
|
|
if isSystem, ok := role["is_system"].(bool); ok && isSystem {
|
|
return fmt.Errorf("cannot delete system role: %s", roleID)
|
|
}
|
|
// Handle different boolean types from database
|
|
if isSystemInt, ok := role["is_system"].(int64); ok && isSystemInt != 0 {
|
|
return fmt.Errorf("cannot delete system role: %s", roleID)
|
|
}
|
|
|
|
// Proceed with soft delete
|
|
affected, err := m.DeleteWhere(model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "role_id", Value: roleID},
|
|
},
|
|
Limit: 1, // Safety: ensure only one record is deleted
|
|
})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf(ErrFailedToDeleteRole, err)
|
|
}
|
|
|
|
if affected == 0 {
|
|
return fmt.Errorf(ErrRoleNotFound)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetRoles retrieves roles by query parameters
|
|
func (u *DefaultUser) GetRoles(ctx context.Context, param model.QueryParam) ([]maps.MapStr, error) {
|
|
// Set default select fields if not provided
|
|
if param.Select == nil {
|
|
param.Select = u.roleFields
|
|
}
|
|
|
|
m := model.Select(u.roleModel)
|
|
roles, err := m.Get(param)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(ErrFailedToGetRole, err)
|
|
}
|
|
|
|
return roles, nil
|
|
}
|
|
|
|
// PaginateRoles retrieves paginated list of roles
|
|
func (u *DefaultUser) PaginateRoles(ctx context.Context, param model.QueryParam, page int, pagesize int) (maps.MapStr, error) {
|
|
// Set default select fields if not provided
|
|
if param.Select == nil {
|
|
param.Select = u.roleFields
|
|
}
|
|
|
|
m := model.Select(u.roleModel)
|
|
result, err := m.Paginate(param, page, pagesize)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(ErrFailedToGetRole, err)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// CountRoles returns total count of roles with optional filters
|
|
func (u *DefaultUser) CountRoles(ctx context.Context, param model.QueryParam) (int64, error) {
|
|
// Use Paginate with a small page size to get the total count
|
|
// This is more reliable than manual COUNT(*) queries
|
|
m := model.Select(u.roleModel)
|
|
result, err := m.Paginate(param, 1, 1) // Get first page with 1 item to get total
|
|
if err != nil {
|
|
return 0, fmt.Errorf(ErrFailedToGetRole, err)
|
|
}
|
|
|
|
// Extract total from pagination result using utility function
|
|
if totalInterface, ok := result["total"]; ok {
|
|
return parseIntFromDB(totalInterface)
|
|
}
|
|
|
|
return 0, fmt.Errorf("total not found in pagination result")
|
|
}
|
|
|
|
// GetRolePermissions retrieves permissions for a role
|
|
func (u *DefaultUser) GetRolePermissions(ctx context.Context, roleID string) (maps.MapStrAny, error) {
|
|
m := model.Select(u.roleModel)
|
|
roles, err := m.Get(model.QueryParam{
|
|
Select: []interface{}{"role_id", "permissions", "restricted_permissions"},
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "role_id", Value: roleID},
|
|
},
|
|
Limit: 1,
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf(ErrFailedToGetRole, err)
|
|
}
|
|
|
|
if len(roles) == 0 {
|
|
return nil, fmt.Errorf(ErrRoleNotFound)
|
|
}
|
|
|
|
role := roles[0]
|
|
permissions := maps.MapStrAny{
|
|
"role_id": roleID,
|
|
"permissions": role["permissions"],
|
|
"restricted_permissions": role["restricted_permissions"],
|
|
}
|
|
|
|
return permissions, nil
|
|
}
|
|
|
|
// SetRolePermissions sets permissions for a role
|
|
func (u *DefaultUser) SetRolePermissions(ctx context.Context, roleID string, permissions maps.MapStrAny) error {
|
|
// Prepare update data - only allow permission-related fields
|
|
updateData := maps.MapStrAny{}
|
|
|
|
if perms, ok := permissions["permissions"]; ok {
|
|
updateData["permissions"] = perms
|
|
}
|
|
|
|
if restrictedPerms, ok := permissions["restricted_permissions"]; ok {
|
|
updateData["restricted_permissions"] = restrictedPerms
|
|
}
|
|
|
|
// Skip update if no permission fields provided
|
|
if len(updateData) == 0 {
|
|
return nil
|
|
}
|
|
|
|
m := model.Select(u.roleModel)
|
|
affected, err := m.UpdateWhere(model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "role_id", Value: roleID},
|
|
},
|
|
Limit: 1, // Safety: ensure only one record is updated
|
|
}, updateData)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf(ErrFailedToUpdateRole, err)
|
|
}
|
|
|
|
if affected == 0 {
|
|
return fmt.Errorf(ErrRoleNotFound)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ValidateRolePermissions validates if role has specific permissions
|
|
func (u *DefaultUser) ValidateRolePermissions(ctx context.Context, roleID string, requiredPermissions []string) (bool, error) {
|
|
if len(requiredPermissions) == 0 {
|
|
return true, nil // No permissions required
|
|
}
|
|
|
|
// Get role permissions
|
|
rolePermissions, err := u.GetRolePermissions(ctx, roleID)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
// Extract permissions and restricted permissions
|
|
permissions, _ := rolePermissions["permissions"].(map[string]interface{})
|
|
restrictedPermissions, _ := rolePermissions["restricted_permissions"].([]interface{})
|
|
|
|
// Convert restricted permissions to map for faster lookup
|
|
restrictedMap := make(map[string]bool)
|
|
for _, perm := range restrictedPermissions {
|
|
if permStr, ok := perm.(string); ok {
|
|
restrictedMap[permStr] = true
|
|
}
|
|
}
|
|
|
|
// Check each required permission
|
|
for _, requiredPerm := range requiredPermissions {
|
|
// First check if permission is explicitly restricted
|
|
if restrictedMap[requiredPerm] {
|
|
return false, nil // Permission is explicitly denied
|
|
}
|
|
|
|
// Check if permission exists in granted permissions
|
|
if permissions == nil {
|
|
return false, nil // No permissions granted
|
|
}
|
|
|
|
// Look for the permission in the permissions object
|
|
// This is a simple implementation - in practice, you might want more sophisticated permission matching
|
|
permValue, exists := permissions[requiredPerm]
|
|
if !exists {
|
|
return false, nil // Permission not found
|
|
}
|
|
|
|
// Check if permission is enabled (assuming boolean values)
|
|
if permBool, ok := permValue.(bool); ok && !permBool {
|
|
return false, nil // Permission exists but is disabled
|
|
}
|
|
}
|
|
|
|
return true, nil // All required permissions are valid
|
|
}
|