Skip to content

Conversation

@senthalan
Copy link
Contributor

@senthalan senthalan commented Oct 20, 2025

Part of #510

This pull request introduces a new Role Management feature to the backend, including database schema changes, service initialization, error handling, data models, and supporting configuration for mocking and testing. The main focus is to enable creation, management, and assignment of roles, along with permissions and assignments to users and groups, with full API and database support.

Key changes include:

Database Schema & Queries

  • New role management tables and indexes: Added ROLE, ROLE_PERMISSION, and ROLE_ASSIGNMENT tables to both Postgres and SQLite schemas, including supporting indexes for efficient authorization queries. These tables support storing roles, their permissions, and assignments to users/groups. [1] [2]
  • Role management SQL queries: Introduced a comprehensive set of queries for CRUD operations on roles, permissions, and assignments, as well as checks for uniqueness and pagination support in storeconstants.go.

Backend Service & API

  • Role service initialization and route registration: Implemented the Initialize function for the role service, registering HTTP routes for role CRUD operations and assignment management, with appropriate CORS handling.
  • Service wiring: Integrated the new role service into the main service manager, ensuring it is initialized with dependencies on user, group, and organization unit services. [1] [2]

Data Models

  • Role and assignment models: Defined the core data structures for roles, assignments, and related API requests/responses, supporting role details, permissions, assignments, and pagination.

Error Handling

  • Role management error constants: Added detailed error types and codes for client and server errors related to role operations, including validation, not found, and conflict scenarios.

Testing & Mocking

  • Mockery configuration updates: Updated mockery configuration files to support mocking of new role, group, and organization unit interfaces for both private and public test scenarios. [1] [2]

These changes collectively enable robust role-based access control capabilities in the backend system.

@codecov
Copy link

codecov bot commented Oct 20, 2025

Codecov Report

❌ Patch coverage is 82.90514% with 173 lines in your changes missing coverage. Please review.
✅ Project coverage is 31.68%. Comparing base (93b7171) to head (edc921e).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
backend/internal/role/store.go 70.63% 41 Missing and 38 partials ⚠️
backend/internal/role/init.go 0.00% 52 Missing ⚠️
backend/internal/role/handler.go 89.53% 19 Missing and 10 partials ⚠️
backend/internal/role/service.go 97.68% 6 Missing and 2 partials ⚠️
backend/internal/group/service.go 0.00% 3 Missing ⚠️
backend/cmd/server/servicemanager.go 0.00% 2 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (93b7171) and HEAD (edc921e). Click for more details.

HEAD has 4 uploads less than BASE
Flag BASE (93b7171) HEAD (edc921e)
backend-integration-postgres 1 0
backend-combined-postgres 1 0
backend-integration-sqlite 1 0
backend-combined-sqlite 1 0
Additional details and impacted files
@@             Coverage Diff             @@
##             main     #555       +/-   ##
===========================================
- Coverage   65.62%   31.68%   -33.95%     
===========================================
  Files         200      205        +5     
  Lines       17780    18788     +1008     
  Branches      255      255               
===========================================
- Hits        11669     5953     -5716     
- Misses       4772    12550     +7778     
+ Partials     1339      285     -1054     
Flag Coverage Δ
backend-combined-postgres ?
backend-combined-sqlite ?
backend-integration-postgres ?
backend-integration-sqlite ?
backend-unit 31.68% <82.90%> (+3.04%) ⬆️
frontend-apps-develop-unit 31.68% <82.90%> (+3.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements role management functionality, enabling permission-based access control through roles that can be assigned to users and groups. The implementation includes complete CRUD operations, assignment management, pagination support, and comprehensive test coverage.

Key Changes

  • New role management API with CRUD endpoints and role assignment capabilities
  • Database schema additions for roles, permissions, and assignments
  • Complete service layer with validation and error handling
  • Integration tests validating all API operations

Reviewed Changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated no comments.

Show a summary per file
File Description
backend/internal/role/service.go Implements role service with CRUD operations and assignment management
backend/internal/role/store.go Database layer for role operations with transaction handling
backend/internal/role/model.go Data models for roles, assignments, and API requests/responses
backend/internal/role/errorconstants.go Error constants for role-related operations
backend/internal/role/storeconstants.go Database query definitions for role operations
backend/internal/role/service_test.go Comprehensive unit tests for service layer
backend/internal/role/init.go Route registration and service initialization
backend/dbscripts/thunderdb/sqlite.sql SQLite schema additions for role tables
tests/integration/role/roleapi_test.go Integration tests for role API endpoints
tests/integration/role/model.go Test models for integration testing
tests/integration/testutils/models.go Added Group model for test utilities
tests/integration/testutils/apiutils.go Helper functions for group operations in tests
backend/.mockery.public.yml Mock configuration for group and OU services
docs/apis/role.yaml OpenAPI specification for role management API

@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from 2f79947 to 5869e15 Compare October 21, 2025 05:01
Copilot AI review requested due to automatic review settings October 29, 2025 11:07
@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from 5869e15 to b683c07 Compare October 29, 2025 11:07
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated 1 comment.

@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from b683c07 to cfe897d Compare October 29, 2025 11:12
@senthalan senthalan requested a review from Copilot October 29, 2025 11:14
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated 1 comment.

@senthalan senthalan requested a review from Copilot October 29, 2025 12:09
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated no new comments.

@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from cfe897d to 1f0db2d Compare October 29, 2025 12:51
@senthalan senthalan requested a review from Copilot October 29, 2025 12:53
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated no new comments.


if err := rs.roleStore.CreateRole(role); err != nil {
logger.Error("Failed to create role", log.Error(err))
return nil, &ErrorInternalServerError
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scenarios for duplicate assignments/permissions to a role should be treated as client-side errors. Currently, these seem not to be differentiated in pre-checks, so they will be treated as server errors due to DB constraints right ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the precheck to remove the duplicates


// Validate group IDs using group service
if len(groupIDs) > 0 {
for _, groupID := range groupIDs {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we use the existing validateGroupIDs here by making it public, instead of looping?

-- Table to store Roles
CREATE TABLE "ROLE" (
ID INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
ROLE_ID VARCHAR(36) UNIQUE NOT NULL,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be better to add a unique constraint on (OU_ID, NAME) at DB level too right ? Could reduce potential race conditions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added unique constraint on (OU_ID, NAME)

return
}

logger.Debug("Successfully created role", log.String("role id", createdRole.ID))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
logger.Debug("Successfully created role", log.String("role id", createdRole.ID))
logger.Debug("Successfully created role", log.String("roleId", createdRole.ID))

Shall we use camel case for log parameters for consistency?
Check other places as well

for i, assignment := range request.Assignments {
sanitized.Assignments[i] = Assignment{
ID: sysutils.SanitizeString(assignment.ID),
Type: assignment.Type,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need to sanitize this as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an AssigneeType. So don't need sanitize

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still this will be treated as a string right? AFAIK it doesn't guarantee this will be one of the defined AssigneeType.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since AssigneeType only has two valid values ("user" and "group"), and the service layer already validates this. Do we need to do string sanitize ?

If we want to do it, then the option is to Convert to string, sanitize, then convert back to AssigneeType

Type: AssigneeType(sysutils.SanitizeString(string(assignment.Type))),

I don't think that's good idea.


const (
// AssigneeTypeUser is the type for users.
AssigneeTypeUser AssigneeType = "user"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we define these type values as all caps for consistency?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to change them too. Maybe with a separate PR.
WDYT @darshanasbg ?

// RoleServiceInterface defines the interface for the role service.
type RoleServiceInterface interface {
GetRoleList(limit, offset int) (*RoleListResponse, *serviceerror.ServiceError)
CreateRole(request CreateRoleRequest) (*Role, *serviceerror.ServiceError)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to define separate structs for service layer rather than using request models.
WDYT?

}

// getDisplayNameForAssignment retrieves the display name for a user or group assignment.
func (rs *roleService) getDisplayNameForAssignment(assignment *Assignment) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we handle service errors properly when calling other services from this method? Currently even client errors gets wrapped as a server error right?
Maybe we can return a service error from getDisplayNameForAssignment method too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the getDisplayNameForAssignment function


// Populate display names if requested
if includeDisplay {
for i := range assignments {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed shall we add a note/ create an issue to improve display name retrieval?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created an issue to track this #639

// Validate user IDs using user service
if len(userIDs) > 0 {
invalidUserIDs, svcErr := rs.userService.ValidateUserIDs(userIDs)
if svcErr != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we handle service errors properly?

@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from 1f0db2d to 49b0d57 Compare October 30, 2025 18:14
Copilot AI review requested due to automatic review settings October 30, 2025 18:15
@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from 49b0d57 to 8307bf6 Compare October 30, 2025 18:15
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 32 out of 32 changed files in this pull request and generated 1 comment.

Comment on lines +56 to +59
// EncodingError is the error returned when encoding the response.
ErrorEncodingError = "{Code: \"ENC-5000\",Error: \"Encoding error\"," +
"ErrorDescription: \"An error occurred while encoding the response\"}"
)
Copy link

Copilot AI Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error is defined as a raw JSON string instead of following the established ServiceError struct pattern used elsewhere in the codebase. This should be refactored to use the ServiceError struct type for consistency with other error definitions like ErrorInvalidRequestFormat in the role package.

Suggested change
// EncodingError is the error returned when encoding the response.
ErrorEncodingError = "{Code: \"ENC-5000\",Error: \"Encoding error\"," +
"ErrorDescription: \"An error occurred while encoding the response\"}"
)
// ErrorEncodingError is the error returned when encoding the response.
ErrorEncodingError = &ServiceError{
Code: "ENC-5000",
Type: ServerErrorType,
Error: "Encoding error",
ErrorDescription: "An error occurred while encoding the response",
}

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intensional to send the json as string to the client incase of a encoding issue

@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from 8307bf6 to eb95510 Compare October 30, 2025 18:20
@senthalan senthalan requested a review from Copilot October 30, 2025 18:25
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 32 out of 32 changed files in this pull request and generated no new comments.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this directory structure intended? (internal/role/internal/role)

// Validate organization unit exists using OU service
_, svcErr := rs.ouService.GetOrganizationUnit(request.OrganizationUnitID)
if svcErr != nil {
if svcErr.Code == oupkg.ErrorOrganizationUnitNotFound.Code {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we check for the exact error using errors.Is()? WDYT about returning the OU service error without having a new error object?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't do errors.Is() because svcErr is not a type of go error

@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from eb95510 to dadda65 Compare October 31, 2025 13:23
Copilot AI review requested due to automatic review settings October 31, 2025 13:28
@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from dadda65 to b6261e4 Compare October 31, 2025 13:28
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 1 comment.

// ErrorEmptyAssignments is the error returned when assignments list is empty.
ErrorEmptyAssignments = serviceerror.ServiceError{
Type: serviceerror.ClientErrorType,
Code: "ROL-1014",
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error code ROL-1013 in the sequence. The error codes jump from ROL-1012 to ROL-1014. Consider adding the missing code or renumbering ErrorEmptyAssignments to ROL-1013 to maintain sequential error codes.

Suggested change
Code: "ROL-1014",
Code: "ROL-1013",

Copilot uses AI. Check for mistakes.
@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from b6261e4 to da0dbfc Compare October 31, 2025 13:35
Copilot AI review requested due to automatic review settings October 31, 2025 13:39
@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from da0dbfc to d86c3b7 Compare October 31, 2025 13:39
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 1 comment.

@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from d86c3b7 to 4d17e91 Compare November 1, 2025 13:45
Copilot AI review requested due to automatic review settings November 2, 2025 05:43
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 28 out of 28 changed files in this pull request and generated no new comments.

// ErrorMissingUserOrGroups is the error returned when both user ID and groups are missing.
ErrorMissingUserOrGroups = serviceerror.ServiceError{
Type: serviceerror.ClientErrorType,
Code: "ROL-1015",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Code: "ROL-1015",
Code: "ROL-1011",

for i, assignment := range request.Assignments {
sanitized.Assignments[i] = Assignment{
ID: sysutils.SanitizeString(assignment.ID),
Type: assignment.Type,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still this will be treated as a string right? AFAIK it doesn't guarantee this will be one of the defined AssigneeType.


const (
// AssigneeTypeUser is the type for users.
AssigneeTypeUser AssigneeType = "user"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to change them too. Maybe with a separate PR.
WDYT @darshanasbg ?

Links []LinkResponse `json:"links"`
}

// Internal service layer structs - used for business logic processing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Internal service layer structs - used for business logic processing

We don't need these comments right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to keep it as a separator to differentiate the structs used in the handler and service layer

@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from 603668a to 0d7afb2 Compare November 2, 2025 16:16
Copilot AI review requested due to automatic review settings November 2, 2025 16:31
@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from 0d7afb2 to 322e91d Compare November 2, 2025 16:31
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated no new comments.

@senthalan senthalan enabled auto-merge November 2, 2025 19:13
@senthalan senthalan force-pushed the implement-rbac-role-mgt branch from 322e91d to edc921e Compare November 3, 2025 04:41
@senthalan senthalan merged commit 952f916 into asgardeo:main Nov 3, 2025
5 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants