-
Notifications
You must be signed in to change notification settings - Fork 234
Role management implementation #555
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
b9f6c29 to
c44f505
Compare
Codecov Report❌ Patch coverage is
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
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
c44f505 to
ea4c14d
Compare
ea4c14d to
2f79947
Compare
There was a problem hiding this 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 |
2f79947 to
5869e15
Compare
5869e15 to
b683c07
Compare
There was a problem hiding this 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.
b683c07 to
cfe897d
Compare
There was a problem hiding this 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.
There was a problem hiding this 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.
cfe897d to
1f0db2d
Compare
There was a problem hiding this 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 |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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
backend/internal/role/service.go
Outdated
|
|
||
| // Validate group IDs using group service | ||
| if len(groupIDs) > 0 { | ||
| for _, groupID := range groupIDs { |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)
backend/internal/role/handler.go
Outdated
| return | ||
| } | ||
|
|
||
| logger.Debug("Successfully created role", log.String("role id", createdRole.ID)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 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, |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is how it's been defined in the groups package
There was a problem hiding this comment.
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 ?
backend/internal/role/service.go
Outdated
| // RoleServiceInterface defines the interface for the role service. | ||
| type RoleServiceInterface interface { | ||
| GetRoleList(limit, offset int) (*RoleListResponse, *serviceerror.ServiceError) | ||
| CreateRole(request CreateRoleRequest) (*Role, *serviceerror.ServiceError) |
There was a problem hiding this comment.
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?
backend/internal/role/service.go
Outdated
| } | ||
|
|
||
| // getDisplayNameForAssignment retrieves the display name for a user or group assignment. | ||
| func (rs *roleService) getDisplayNameForAssignment(assignment *Assignment) (string, error) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the getDisplayNameForAssignment function
backend/internal/role/service.go
Outdated
|
|
||
| // Populate display names if requested | ||
| if includeDisplay { | ||
| for i := range assignments { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
1f0db2d to
49b0d57
Compare
49b0d57 to
8307bf6
Compare
There was a problem hiding this 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.
| // EncodingError is the error returned when encoding the response. | ||
| ErrorEncodingError = "{Code: \"ENC-5000\",Error: \"Encoding error\"," + | ||
| "ErrorDescription: \"An error occurred while encoding the response\"}" | ||
| ) |
Copilot
AI
Oct 30, 2025
There was a problem hiding this comment.
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.
| // 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", | |
| } |
There was a problem hiding this comment.
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
8307bf6 to
eb95510
Compare
There was a problem hiding this 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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
eb95510 to
dadda65
Compare
dadda65 to
b6261e4
Compare
There was a problem hiding this 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", |
Copilot
AI
Oct 31, 2025
There was a problem hiding this comment.
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.
| Code: "ROL-1014", | |
| Code: "ROL-1013", |
b6261e4 to
da0dbfc
Compare
da0dbfc to
d86c3b7
Compare
There was a problem hiding this 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.
d86c3b7 to
4d17e91
Compare
There was a problem hiding this 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", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| Code: "ROL-1015", | |
| Code: "ROL-1011", |
| for i, assignment := range request.Assignments { | ||
| sanitized.Assignments[i] = Assignment{ | ||
| ID: sysutils.SanitizeString(assignment.ID), | ||
| Type: assignment.Type, |
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // Internal service layer structs - used for business logic processing |
We don't need these comments right?
There was a problem hiding this comment.
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
603668a to
0d7afb2
Compare
0d7afb2 to
322e91d
Compare
There was a problem hiding this 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.
322e91d to
edc921e
Compare
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
ROLE,ROLE_PERMISSION, andROLE_ASSIGNMENTtables 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]storeconstants.go.Backend Service & API
Initializefunction for the role service, registering HTTP routes for role CRUD operations and assignment management, with appropriate CORS handling.Data Models
Error Handling
Testing & Mocking
These changes collectively enable robust role-based access control capabilities in the backend system.