Implementing Role-Based Access Control (RBAC) in Go
This challenge focuses on building a robust authorization system in Go using Role-Based Access Control (RBAC). RBAC is a common security model that grants permissions to users based on their roles within an organization. Successfully implementing this will demonstrate your understanding of Go's concurrency, data structures, and error handling in the context of security.
Problem Description
You are tasked with creating an RBAC system that manages users, roles, and permissions. The system should allow administrators to:
- Define Roles: Create new roles (e.g., "admin", "editor", "viewer").
- Assign Permissions to Roles: Associate specific permissions (e.g., "read_document", "write_document", "delete_document") with roles. A single role can have multiple permissions.
- Assign Roles to Users: Grant one or more roles to a user. A user can have multiple roles.
- Check Permissions: Determine if a specific user has a particular permission, considering all the roles assigned to that user.
The system should be thread-safe, as multiple Goroutines might be accessing and modifying the RBAC data concurrently.
Key Requirements:
- Data Structures: Design appropriate Go data structures to represent users, roles, and their relationships.
- Core Operations: Implement functions for adding roles, assigning permissions to roles, assigning roles to users, and checking user permissions.
- Concurrency Safety: Ensure all operations that modify or read shared RBAC data are protected against race conditions.
- Error Handling: Implement clear and informative error handling for invalid operations (e.g., assigning a non-existent role to a user, checking permissions for a non-existent user).
Expected Behavior:
- A user should have a permission if any of their assigned roles possess that permission.
- The system should gracefully handle requests for non-existent users, roles, or permissions.
Edge Cases:
- A user with no roles assigned.
- A role with no permissions assigned.
- Attempting to remove a user or role that doesn't exist.
- Concurrent attempts to add/remove users, roles, or permissions.
Examples
Example 1:
Input:
Users: Alice, Bob
Roles: admin, editor
Permissions: read, write, delete
Operations:
1. Create role "admin"
2. Create role "editor"
3. Assign "read", "write", "delete" permissions to "admin"
4. Assign "read", "write" permissions to "editor"
5. Assign "admin" role to Alice
6. Assign "editor" role to Bob
7. Check if Alice has "delete" permission
8. Check if Bob has "delete" permission
9. Check if Alice has "read" permission
Output:
Alice can delete: true
Bob can delete: false
Alice can read: true
Explanation: Alice is an "admin", so she has all permissions including "delete". Bob is an "editor", so he has "read" and "write" but not "delete". Alice also has "read" because it's assigned to her "admin" role.
Example 2:
Input:
Users: Charlie
Roles: viewer
Permissions: view_profile
Operations:
1. Create role "viewer"
2. Assign "view_profile" permission to "viewer"
3. Assign "viewer" role to Charlie
4. Check if Charlie has "edit_profile" permission
Output:
Charlie can edit_profile: false
Explanation: Charlie has the "viewer" role, which only grants the "view_profile" permission. Therefore, he does not have the "edit_profile" permission.
Example 3: User with Multiple Roles
Input:
Users: David
Roles: contributor, moderator
Permissions: write, review
Operations:
1. Create role "contributor"
2. Create role "moderator"
3. Assign "write" permission to "contributor"
4. Assign "review" permission to "moderator"
5. Assign "contributor" role to David
6. Assign "moderator" role to David
7. Check if David has "write" permission
8. Check if David has "review" permission
9. Check if David has "delete" permission
Output:
David can write: true
David can review: true
David can delete: false
Explanation: David has both the "contributor" and "moderator" roles. The "contributor" role grants "write", and the "moderator" role grants "review". Therefore, David has both. He does not have "delete" as neither of his roles grant it.
Constraints
- The number of users, roles, and permissions can be up to 10,000 each.
- Each user can be assigned up to 50 roles.
- Each role can have up to 100 permissions assigned to it.
- The
HasPermissionoperation should ideally complete in O(R * P) time in the worst case, where R is the number of roles assigned to a user and P is the average number of permissions per role. However, optimizations are encouraged. - All data structures should be initialized cleanly.
Notes
- Consider using
sync.RWMutexfor managing concurrent access to your data structures. - Think about how you will represent the relationships between users, roles, and permissions. Maps and slices will likely be useful.
- For checking permissions, you'll need to iterate through the user's roles and then check if any of those roles have the requested permission.
- You can start by defining a
RBACManagerstruct to hold your system's state. - The challenge requires you to implement the core logic. You don't need to build a full HTTP API or web interface.