yao/openapi/oauth/providers/user/role.go
Max 6c73395158 Refactor invitation handling and data parsing in user provider
- 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.
2025-09-16 17:54:56 +08:00

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
}