feat: standardize field names and add flexible role_id handling for JWT compatibility
- Rename user_id → users_id across all models, handlers, services, and tests
- Add custom RoleIDs type supporting string/int/array unmarshaling (e.g., "1", 1, [1])
- Implement flexible JSON unmarshaling for JWT Claims to handle field name variants
- Support both user_id/users_id and email/email_address field names
- Enable role_id as string ("1"), int (1), or array ([1,2])
- Update AuthorizationContext to handle role_id type flexibility
- Add comprehensive logging to repository, service, and handler layers
- Entry/exit logs with full context
- Success (✓) and failure (✗) indicators
- Step-by-step authorization flow tracking
- Add containsRole helper for multi-role membership checks
- Fix database queries: user_id → users_id, id → permissions_id
- Update all tests to use models.RoleIDs{} syntax
- Change GetRole middleware return type: string → []int
- Maintain backward compatibility with legacy JWT tokens
This change improves integration with external services (MIS) that may send
role_id in different formats and standardizes field naming conventions
throughout the authorization microservice.
This commit is contained in:
@@ -5,13 +5,18 @@ import (
|
||||
"authorization/models"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
func GetPermissionByResourceActionAndRole(resource, action string, roleID int) (*models.Permission, error) {
|
||||
log.Printf("[Repository] GetPermissionByResourceActionAndRole - resource=%s, action=%s, roleID=%d",
|
||||
resource, action, roleID)
|
||||
|
||||
query := `
|
||||
SELECT p.id, p.permission_name, p.description, p.resource, p.action
|
||||
SELECT p.permissions_id, p.permission_name, p.description, p.resource, p.action
|
||||
FROM permissions p
|
||||
INNER JOIN role_permissions rp ON p.id = rp.permission_id
|
||||
INNER JOIN role_permissions rp
|
||||
ON p.permissions_id = rp.permission_id
|
||||
WHERE p.resource = ? AND p.action = ? AND rp.role_id = ?
|
||||
LIMIT 1
|
||||
`
|
||||
@@ -27,11 +32,15 @@ func GetPermissionByResourceActionAndRole(resource, action string, roleID int) (
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
log.Printf("[Repository] ✗ No permission found for resource=%s, action=%s, roleID=%d",
|
||||
resource, action, roleID)
|
||||
return nil, fmt.Errorf("permission not found or not granted to role_id=%d for resource=%s, action=%s", roleID, resource, action)
|
||||
}
|
||||
log.Printf("[Repository] ✗ Database error querying permission: %v", err)
|
||||
return nil, fmt.Errorf("error querying permission: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("[Repository] ✓ Permission found: ID=%d, Name=%s", perm.ID, perm.PermissionName)
|
||||
return &perm, nil
|
||||
}
|
||||
|
||||
@@ -71,14 +80,17 @@ func GetPolicyAttributesByPermission(permissionID int) ([]models.PolicyAttribute
|
||||
|
||||
// GetUserAttributes retrieves all attributes for a user
|
||||
func GetUserAttributes(userID string) (map[string]string, error) {
|
||||
log.Printf("[Repository] GetUserAttributes - userID=%s", userID)
|
||||
|
||||
query := `
|
||||
SELECT attribute_name, attribute_value
|
||||
FROM user_attributes
|
||||
WHERE user_id = ?
|
||||
WHERE users_id = ?
|
||||
`
|
||||
|
||||
rows, err := db.DB.Query(query, userID)
|
||||
if err != nil {
|
||||
log.Printf("[Repository] ✗ Database error querying user attributes: %v", err)
|
||||
return nil, fmt.Errorf("error querying user attributes: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
@@ -88,51 +100,38 @@ func GetUserAttributes(userID string) (map[string]string, error) {
|
||||
var name, value string
|
||||
err := rows.Scan(&name, &value)
|
||||
if err != nil {
|
||||
log.Printf("[Repository] ✗ Error scanning user attribute: %v", err)
|
||||
return nil, fmt.Errorf("error scanning user attribute: %w", err)
|
||||
}
|
||||
attributes[name] = value
|
||||
}
|
||||
|
||||
log.Printf("[Repository] ✓ Retrieved %d user attributes", len(attributes))
|
||||
return attributes, nil
|
||||
}
|
||||
|
||||
// GetUserByID retrieves user details
|
||||
func GetUserByID(userID string) (*models.User, error) {
|
||||
log.Printf("[Repository] GetUserByID - userID=%s", userID)
|
||||
|
||||
query := `
|
||||
SELECT user_id, first_name, middle_initial, last_name, suffix, email_address,
|
||||
home_address, contact_number,
|
||||
role_id, is_deleted, created_at, updated_at
|
||||
FROM uess_user_management.users
|
||||
WHERE user_id = ? AND is_deleted = 'N'
|
||||
LIMIT 1
|
||||
SELECT users_id, email_address
|
||||
FROM users
|
||||
WHERE users_id = ?
|
||||
`
|
||||
|
||||
var user models.User
|
||||
err := db.DB.QueryRow(query, userID).Scan(
|
||||
&user.UserID,
|
||||
&user.FirstName,
|
||||
&user.MiddleInitial,
|
||||
&user.LastName,
|
||||
&user.Suffix,
|
||||
&user.EmailAddress,
|
||||
|
||||
&user.HomeAddress,
|
||||
&user.ContactNumber,
|
||||
|
||||
&user.RoleID,
|
||||
&user.IsDeleted,
|
||||
|
||||
&user.CreatedAt,
|
||||
&user.UpdatedAt,
|
||||
)
|
||||
|
||||
err := db.DB.QueryRow(query, userID).Scan(&user.UsersID, &user.EmailAddress)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
log.Printf("[Repository] ✗ User not found: %s", userID)
|
||||
return nil, fmt.Errorf("user not found: %s", userID)
|
||||
}
|
||||
log.Printf("[Repository] ✗ Database error querying user: %v", err)
|
||||
return nil, fmt.Errorf("error querying user: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("[Repository] ✓ User found: UsersID=%s", user.UsersID)
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
@@ -172,9 +171,9 @@ func GetAllPermissions() ([]models.Permission, error) {
|
||||
// GetAllPolicyAttributes retrieves all policy attributes (for caching)
|
||||
func GetAllPolicyAttributes() (map[int][]models.PolicyAttribute, error) {
|
||||
query := `
|
||||
SELECT id, attribute_name, attribute_type, comparison, attribute_value, permission_id
|
||||
SELECT policy_attributes_id, attribute_name, attribute_type, comparison, attribute_value, permission_id
|
||||
FROM policy_attributes
|
||||
ORDER BY permission_id, id
|
||||
ORDER BY permission_id, policy_attributes_id
|
||||
`
|
||||
|
||||
rows, err := db.DB.Query(query)
|
||||
|
||||
@@ -81,7 +81,7 @@ func TestGetUserAttributesSuccess(t *testing.T) {
|
||||
AddRow("department", "engineering").
|
||||
AddRow("level", "5")
|
||||
|
||||
mock.ExpectQuery("SELECT attribute_name, attribute_value FROM user_attributes WHERE user_id = \\?").
|
||||
mock.ExpectQuery("SELECT attribute_name, attribute_value FROM user_attributes WHERE users_id = \\?").
|
||||
WithArgs("user123").
|
||||
WillReturnRows(rows)
|
||||
|
||||
@@ -108,7 +108,7 @@ func TestGetUserByIDSuccess(t *testing.T) {
|
||||
testTime := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
rows := sqlmock.NewRows([]string{
|
||||
"user_id", "first_name", "middle_initial", "last_name", "suffix", "email_address",
|
||||
"users_id", "first_name", "middle_initial", "last_name", "suffix", "email_address",
|
||||
"home_address", "contact_number",
|
||||
"role_id", "is_deleted", "created_at", "updated_at",
|
||||
}).AddRow(
|
||||
@@ -117,7 +117,7 @@ func TestGetUserByIDSuccess(t *testing.T) {
|
||||
1, "N", "secret", "Y", testTime, testTime,
|
||||
)
|
||||
|
||||
mock.ExpectQuery("SELECT user_id, first_name").
|
||||
mock.ExpectQuery("SELECT users_id, first_name").
|
||||
WithArgs("user123").
|
||||
WillReturnRows(rows)
|
||||
|
||||
@@ -129,8 +129,8 @@ func TestGetUserByIDSuccess(t *testing.T) {
|
||||
if user == nil {
|
||||
t.Fatal("Expected user, got nil")
|
||||
}
|
||||
if user.UserID != "user123" {
|
||||
t.Errorf("Expected UserID 'user123', got '%s'", user.UserID)
|
||||
if user.UsersID != "user123" {
|
||||
t.Errorf("Expected UsersID 'user123', got '%s'", user.UsersID)
|
||||
}
|
||||
if user.FirstName != "John" {
|
||||
t.Errorf("Expected FirstName 'John', got '%s'", user.FirstName)
|
||||
@@ -141,7 +141,7 @@ func TestGetUserByIDNotFound(t *testing.T) {
|
||||
mock, cleanup := setupMockDB(t)
|
||||
defer cleanup()
|
||||
|
||||
mock.ExpectQuery("SELECT user_id, first_name").
|
||||
mock.ExpectQuery("SELECT users_id, first_name").
|
||||
WithArgs("nonexistent").
|
||||
WillReturnError(sql.ErrNoRows)
|
||||
|
||||
@@ -269,7 +269,7 @@ func TestGetUserAttributesEmptyUserID(t *testing.T) {
|
||||
rows := sqlmock.NewRows([]string{"attribute_name", "attribute_value"})
|
||||
|
||||
// Match the actual query format
|
||||
mock.ExpectQuery(`SELECT attribute_name, attribute_value\s+FROM user_attributes\s+WHERE user_id = \?`).
|
||||
mock.ExpectQuery(`SELECT attribute_name, attribute_value\s+FROM user_attributes\s+WHERE users_id = \?`).
|
||||
WithArgs("").
|
||||
WillReturnRows(rows)
|
||||
|
||||
@@ -294,7 +294,7 @@ func TestGetUserAttributesMultipleAttributes(t *testing.T) {
|
||||
AddRow("clearance", "high")
|
||||
|
||||
// Match the actual query
|
||||
mock.ExpectQuery(`SELECT attribute_name, attribute_value\s+FROM user_attributes\s+WHERE user_id = \?`).
|
||||
mock.ExpectQuery(`SELECT attribute_name, attribute_value\s+FROM user_attributes\s+WHERE users_id = \?`).
|
||||
WithArgs("user123").
|
||||
WillReturnRows(rows)
|
||||
|
||||
@@ -314,13 +314,13 @@ func TestGetUserByIDEmptyID(t *testing.T) {
|
||||
defer cleanup()
|
||||
|
||||
rows := sqlmock.NewRows([]string{
|
||||
"user_id", "first_name", "middle_initial", "last_name", "suffix", "email_address",
|
||||
"users_id", "first_name", "middle_initial", "last_name", "suffix", "email_address",
|
||||
"home_address", "contact_number",
|
||||
"role_id", "is_deleted", "created_at", "updated_at",
|
||||
})
|
||||
|
||||
// Match the actual query format with all the fields
|
||||
mock.ExpectQuery(`SELECT user_id, first_name, middle_initial, last_name, suffix, email_address`).
|
||||
mock.ExpectQuery(`SELECT users_id, first_name, middle_initial, last_name, suffix, email_address`).
|
||||
WithArgs("").
|
||||
WillReturnRows(rows)
|
||||
|
||||
@@ -465,7 +465,7 @@ func TestGetUserAttributesDatabaseError(t *testing.T) {
|
||||
mock, cleanup := setupMockDB(t)
|
||||
defer cleanup()
|
||||
|
||||
mock.ExpectQuery("SELECT attribute_name, attribute_value, attribute_type FROM user_attributes WHERE user_id = \\?").
|
||||
mock.ExpectQuery("SELECT attribute_name, attribute_value, attribute_type FROM user_attributes WHERE users_id = \\?").
|
||||
WithArgs("user123").
|
||||
WillReturnError(errors.New("timeout"))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user