User Role Assignment
Overviewβ
The /api/set-user-role
endpoint allows authorized users to assign roles to other users. This endpoint is primarily used for role management and user promotion within the Ring Platform hierarchy.
Authenticationβ
Required: Admin role or specific permissions Rate Limit: 60 requests per minute for admins, 10 for other authorized users
Endpoint Detailsβ
POST /api/set-user-role
Headers:
Authorization: Bearer <session-token>
Content-Type: application/json
Requestβ
Body Parametersβ
Parameter | Type | Required | Description |
---|---|---|---|
userId | string | Yes | Target user ID |
role | string | Yes | New role to assign |
reason | string | No | Reason for role change |
notifyUser | boolean | No | Send notification to user (default: true) |
Available Rolesβ
Role | Description | Permissions |
---|---|---|
visitor | Basic access | View public content only |
subscriber | Newsletter access | Public content + newsletters |
member | Full platform access | All public features + entity/opportunity creation |
confidential | Premium access | Member features + confidential content |
admin | Administrative access | Full platform control |
Request Exampleβ
{
"userId": "user_123",
"role": "member",
"reason": "User completed verification process",
"notifyUser": true
}
Responseβ
Success Response (200)β
{
"success": true,
"message": "User role updated successfully",
"data": {
"userId": "user_123",
"previousRole": "subscriber",
"newRole": "member",
"updatedBy": "admin_456",
"updatedAt": "2024-12-14T16:00:00Z",
"reason": "User completed verification process",
"notificationSent": true
}
}
Code Examplesβ
JavaScript/TypeScriptβ
// Role assignment service
class RoleService {
async assignRole(
userId: string,
role: string,
reason?: string,
notifyUser = true
) {
try {
const response = await fetch('/api/set-user-role', {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.getSessionToken()}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
userId,
role,
reason,
notifyUser
})
})
if (!response.ok) {
const error = await response.json()
throw new Error(error.message || 'Failed to assign role')
}
return await response.json()
} catch (error) {
console.error('Error assigning role:', error)
throw error
}
}
async promoteToMember(userId: string, reason?: string) {
return this.assignRole(userId, 'member', reason)
}
async promoteToConfidential(userId: string, reason?: string) {
return this.assignRole(userId, 'confidential', reason)
}
async demoteToSubscriber(userId: string, reason?: string) {
return this.assignRole(userId, 'subscriber', reason)
}
private getSessionToken(): string {
// Implementation to get session token
return localStorage.getItem('sessionToken') || ''
}
}
// Usage example
const roleService = new RoleService()
// Promote user to member
await roleService.promoteToMember(
'user_123',
'User completed onboarding process'
)
// Assign confidential role
await roleService.assignRole(
'user_456',
'confidential',
'Verified industry professional',
true
)
React Componentβ
import { useState } from 'react'
import { useSession } from 'next-auth/react'
interface RoleAssignmentProps {
userId: string
currentRole: string
onRoleChanged?: (newRole: string) => void
}
export function RoleAssignment({
userId,
currentRole,
onRoleChanged
}: RoleAssignmentProps) {
const { data: session } = useSession()
const [selectedRole, setSelectedRole] = useState(currentRole)
const [reason, setReason] = useState('')
const [loading, setLoading] = useState(false)
const [message, setMessage] = useState('')
const roles = [
{ value: 'visitor', label: 'Visitor', description: 'Basic access' },
{ value: 'subscriber', label: 'Subscriber', description: 'Newsletter access' },
{ value: 'member', label: 'Member', description: 'Full platform access' },
{ value: 'confidential', label: 'Confidential', description: 'Premium access' },
{ value: 'admin', label: 'Admin', description: 'Administrative access' }
]
const handleRoleChange = async () => {
if (selectedRole === currentRole) {
setMessage('No change in role')
return
}
try {
setLoading(true)
setMessage('')
const response = await fetch('/api/set-user-role', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
userId,
role: selectedRole,
reason: reason || `Role changed from ${currentRole} to ${selectedRole}`,
notifyUser: true
})
})
if (!response.ok) {
const error = await response.json()
throw new Error(error.message)
}
const result = await response.json()
setMessage('Role updated successfully')
onRoleChanged?.(selectedRole)
} catch (error) {
setMessage(`Error: ${error.message}`)
} finally {
setLoading(false)
}
}
// Only show to admins or authorized users
if (session?.user?.role !== 'admin') {
return <div>Access denied. Admin privileges required.</div>
}
return (
<div className="role-assignment">
<h3>Role Assignment</h3>
<div className="current-role">
<strong>Current Role:</strong> {currentRole}
</div>
<div className="role-selector">
<label htmlFor="role-select">New Role:</label>
<select
id="role-select"
value={selectedRole}
onChange={(e) => setSelectedRole(e.target.value)}
disabled={loading}
>
{roles.map(role => (
<option key={role.value} value={role.value}>
{role.label} - {role.description}
</option>
))}
</select>
</div>
<div className="reason-input">
<label htmlFor="reason">Reason (optional):</label>
<textarea
id="reason"
value={reason}
onChange={(e) => setReason(e.target.value)}
placeholder="Enter reason for role change..."
disabled={loading}
rows={3}
/>
</div>
<button
onClick={handleRoleChange}
disabled={loading || selectedRole === currentRole}
className="update-role-btn"
>
{loading ? 'Updating...' : 'Update Role'}
</button>
{message && (
<div className={`message ${message.includes('Error') ? 'error' : 'success'}`}>
{message}
</div>
)}
</div>
)
}
cURL Exampleβ
# Assign member role
curl -X POST "https://ring.ck.ua/api/set-user-role" \
-H "Authorization: Bearer admin-session-token" \
-H "Content-Type: application/json" \
-d '{
"userId": "user_123",
"role": "member",
"reason": "User completed verification process",
"notifyUser": true
}'
# Promote to confidential without notification
curl -X POST "https://ring.ck.ua/api/set-user-role" \
-H "Authorization: Bearer admin-session-token" \
-H "Content-Type: application/json" \
-d '{
"userId": "user_456",
"role": "confidential",
"reason": "Industry professional verification",
"notifyUser": false
}'
Error Handlingβ
Unauthorized Access (403)β
{
"error": "FORBIDDEN",
"message": "Insufficient privileges to assign roles",
"code": 403
}
User Not Found (404)β
{
"error": "USER_NOT_FOUND",
"message": "User with specified ID does not exist",
"code": 404
}
Invalid Role (400)β
{
"error": "INVALID_ROLE",
"message": "Invalid role specified",
"code": 400,
"validRoles": ["visitor", "subscriber", "member", "confidential", "admin"]
}
Cannot Assign Admin Role (400)β
{
"error": "ADMIN_ASSIGNMENT_RESTRICTED",
"message": "Admin role assignment requires special authorization",
"code": 400
}
Self-Assignment Restriction (400)β
{
"error": "SELF_ASSIGNMENT_DENIED",
"message": "Cannot modify your own role",
"code": 400
}
Role Hierarchy and Permissionsβ
Permission Matrixβ
Feature | Visitor | Subscriber | Member | Confidential | Admin |
---|---|---|---|---|---|
View public content | β | β | β | β | β |
Receive newsletters | β | β | β | β | β |
Create entities | β | β | β | β | β |
Create opportunities | β | β | β | β | β |
Access confidential content | β | β | β | β | β |
Admin panel access | β | β | β | β | β |
User management | β | β | β | β | β |
Role Transition Rulesβ
interface RoleTransition {
from: string
to: string
requiresApproval: boolean
autoApprove?: boolean
}
const roleTransitions: RoleTransition[] = [
// Promotions
{ from: 'visitor', to: 'subscriber', requiresApproval: false, autoApprove: true },
{ from: 'subscriber', to: 'member', requiresApproval: true },
{ from: 'member', to: 'confidential', requiresApproval: true },
{ from: 'confidential', to: 'admin', requiresApproval: true },
// Demotions
{ from: 'admin', to: 'confidential', requiresApproval: true },
{ from: 'confidential', to: 'member', requiresApproval: false },
{ from: 'member', to: 'subscriber', requiresApproval: false },
{ from: 'subscriber', to: 'visitor', requiresApproval: false }
]
Notification Systemβ
Role Change Notificationsβ
When a user's role is changed, they receive notifications through multiple channels:
Email Notificationβ
Subject: Your Ring Platform Role Has Been Updated
Dear [User Name],
Your role on Ring Platform has been updated:
- Previous Role: [Previous Role]
- New Role: [New Role]
- Updated By: [Admin Name]
- Reason: [Reason]
Your new permissions are now active. You can explore your enhanced features by logging into your account.
Best regards,
Ring Platform Team
In-App Notificationβ
{
"type": "role_change",
"title": "Role Updated",
"message": "Your role has been updated to [New Role]",
"data": {
"previousRole": "subscriber",
"newRole": "member",
"updatedBy": "admin@ring.ck.ua",
"reason": "User completed verification process"
},
"priority": "high",
"timestamp": "2024-12-14T16:00:00Z"
}
Audit and Loggingβ
Role Change Audit Logβ
interface RoleChangeAuditLog {
id: string
userId: string
userEmail: string
previousRole: string
newRole: string
changedBy: string
changedByEmail: string
reason?: string
timestamp: string
ipAddress: string
userAgent: string
notificationSent: boolean
}
Audit Log Exampleβ
{
"id": "audit_role_789",
"userId": "user_123",
"userEmail": "john.doe@example.com",
"previousRole": "subscriber",
"newRole": "member",
"changedBy": "admin_456",
"changedByEmail": "admin@ring.ck.ua",
"reason": "User completed verification process",
"timestamp": "2024-12-14T16:00:00Z",
"ipAddress": "192.168.1.100",
"userAgent": "Mozilla/5.0...",
"notificationSent": true
}
Security Featuresβ
Access Controlβ
- Admin Verification: Only admins can assign most roles
- Self-Assignment Prevention: Users cannot change their own roles
- Admin Role Protection: Admin role assignment requires special authorization
- Audit Trail: All role changes are logged and tracked
Rate Limitingβ
- Admin Users: 60 requests per minute
- Other Authorized Users: 10 requests per minute
- IP-based Limiting: Additional protection against abuse
Validationβ
- Role Validation: Only valid roles can be assigned
- User Existence: Target user must exist
- Permission Checks: Assigner must have sufficient privileges
Related Endpointsβ
/api/admin/users/[id]/role
- Admin-specific role management/api/profile
- User profile information/api/auth/[...nextauth]
- Authentication/api/info
- Platform role statistics
Testingβ
Unit Testsβ
describe('Role Assignment API', () => {
test('should assign role with admin privileges', async () => {
const response = await request(app)
.post('/api/set-user-role')
.set('Authorization', `Bearer ${adminToken}`)
.send({
userId: 'user_123',
role: 'member',
reason: 'Test role assignment'
})
.expect(200)
expect(response.body.success).toBe(true)
expect(response.body.data.newRole).toBe('member')
})
test('should reject non-admin role assignment', async () => {
await request(app)
.post('/api/set-user-role')
.set('Authorization', `Bearer ${memberToken}`)
.send({
userId: 'user_123',
role: 'admin'
})
.expect(403)
})
test('should prevent self-role assignment', async () => {
await request(app)
.post('/api/set-user-role')
.set('Authorization', `Bearer ${adminToken}`)
.send({
userId: 'admin_456', // Same as token owner
role: 'member'
})
.expect(400)
})
})
Troubleshootingβ
Common Issuesβ
Issue: "Insufficient privileges to assign roles" Solution: Ensure user has admin role or specific permissions
Issue: "Cannot modify your own role" Solution: Role self-assignment is prohibited for security
Issue: "Admin role assignment requires special authorization" Solution: Admin role assignment has additional security requirements
Issue: "User not found" Solution: Verify the target user ID exists in the system