# How CoSky Implements Role-Based Access Control (RBAC): A Deep Dive into the Redis-Backed Security Model

> Discover how CoSky implements Role-Based Access Control RBAC using a reactive Redis-backed model. Learn about its secure architecture and permission management from the ahoo wang cosky source code.

- Repository: [Ahoo Wang/cosky](https://github.com/ahoo-wang/cosky)
- Tags: deep-dive
- Published: 2026-02-23

---

**CoSky implements RBAC using a reactive, Redis-backed architecture where roles and their namespace-action permissions are stored in Hash structures, exposed through a REST API via `RoleController` and managed by `RbacService` according to the ahoo-wang/cosky source code.**

CoSky's Role-Based Access Control (RBAC) system provides lightweight, scalable permission management for the service registry and configuration center. Implemented in the `cosky-rest-api` module, this security layer uses Redis as the persistence store and Spring WebFlux for non-blocking reactive operations. The design centers on simple Hash maps that bind roles to resource-specific actions, enabling fast authorization checks without external dependencies.

## Redis Data Model for CoSky RBAC

The implementation persists authorization data using two primary Redis Hash structures under the `system` namespace (defined by `Namespaced.SYSTEM`).

### Key Structure and Naming Convention

| Redis Key | Type | Purpose |
|-----------|------|---------|
| `system:role_idx` | Hash | Global index mapping `roleName` to `roleDescription` |
| `system:role_resource_bind:{roleName}` | Hash | Stores `namespace → action` pairs for each specific role |

Each role receives its own dedicated Hash key for resource bindings, allowing atomic updates to permissions without affecting other roles. The `system:role_idx` Hash serves as the authoritative registry of all available roles in the system.

## Core RBAC Service Layer

The `RbacService` class located at [`cosky-rest-api/src/main/kotlin/me/ahoo/cosky/rest/security/rbac/RbacService.kt`](https://github.com/ahoo-wang/cosky/blob/main/cosky-rest-api/src/main/kotlin/me/ahoo/cosky/rest/security/rbac/RbacService.kt) encapsulates all reactive CRUD operations against Redis.

### Role Lifecycle Management

**`saveRole(roleName: String, request: SaveRoleRequest)`** orchestrates atomic role creation or updates through three sequential operations:

1. Writes the role description to `system:role_idx` using `opsForHash.put(ROLE_IDX, roleName, request.desc)`
2. Removes existing permission bindings via `delete(ROLE_RESOURCE_BIND_KEY)`
3. Iterates `request.resourceActionBind` to populate the new Hash with `namespace → action` pairs

**`removeRole(roleName: String)`** performs cleanup by removing the role from the global index and deleting its dedicated resource binding key, returning `Mono<Boolean>` indicating whether the role existed.

**`allRole()`** retrieves all entries from `ROLE_IDX`, aggregates them into `Set<RoleDto>`, and always injects a built-in **ADMIN** role into the returned collection regardless of Redis state.

### Permission Resolution Methods

**`getRole(roleName)`** constructs the complete `Role` domain object by fetching the description from the global index and collecting all `ResourceAction` objects from the role-specific Hash.

**`getRoleNamespaces(roles: Set<String>)`** provides authorization filtering by flattening the distinct namespaces accessible across multiple role assignments. This method returns `Flux<String>` suitable for reactive stream processing in security filters.

## REST API for Role Management

The `RoleController` at [`cosky-rest-api/src/main/kotlin/me/ahoo/cosky/rest/security/rbac/RoleController.kt`](https://github.com/ahoo-wang/cosky/blob/main/cosky-rest-api/src/main/kotlin/me/ahoo/cosky/rest/security/rbac/RoleController.kt) exposes a thin HTTP layer over `RbacService`.

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/roles` | `GET` | Returns `Set<RoleDto>` including the built-in ADMIN role |
| `/roles/{roleName}/bind` | `GET` | Returns `List<ResourceActionDto>` showing namespace-action permissions |
| `/roles/{roleName}` | `PUT` | Accepts `SaveRoleRequest` JSON to create or replace a role |
| `/roles/{roleName}` | `DELETE` | Removes the role and its bindings, returning deletion status |

The controller uses standard Spring WebFlux annotations and delegates all business logic to the service layer, maintaining separation of concerns.

## Data Transfer Objects and Domain Model

The API separates internal domain models from external contracts using dedicated DTOs:

- **`SaveRoleRequest`** ([`cosky-rest-api/.../rbac/SaveRoleRequest.kt`](https://github.com/ahoo-wang/cosky/blob/main/cosky-rest-api/.../rbac/SaveRoleRequest.kt)): Request payload containing `desc: String` and `resourceActionBind: List<Pair<String, String>>`
- **`RoleDto`** ([`cosky-rest-api/.../rbac/RoleDto.kt`](https://github.com/ahoo-wang/cosky/blob/main/cosky-rest-api/.../rbac/RoleDto.kt)): Read-only view with role name and description
- **`ResourceActionDto`** ([`cosky-rest-api/.../rbac/ResourceActionDto.kt`](https://github.com/ahoo-wang/cosky/blob/main/cosky-rest-api/.../rbac/ResourceActionDto.kt)): Exposes permission tuples as `namespace` and `action` strings
- **`Role`** and **`ResourceAction`** ([`cosky-rest-api/.../rbac/Role.kt`](https://github.com/ahoo-wang/cosky/blob/main/cosky-rest-api/.../rbac/Role.kt), [`ResourceAction.kt`](https://github.com/ahoo-wang/cosky/blob/main/ResourceAction.kt)): Internal domain representations used by `RbacService`

## Practical Usage Examples

### Creating a Role with Namespace Permissions

To define a role named "editor" with write access to article and comment namespaces:

```bash
curl -X PUT "http://localhost:8080/roles/editor" \
  -H "Content-Type: application/json" \
  -d '{
    "desc": "Content editor with write permissions",
    "resourceActionBind": [
      ["article", "WRITE"],
      ["comment", "WRITE"]
    ]
  }'

```

This creates:
- Entry in `system:role_idx`: `editor → "Content editor with write permissions"`
- Hash `system:role_resource_bind:editor` with fields `article=WRITE` and `comment=WRITE`

### Querying Effective Namespaces for a User

To determine all accessible namespaces for a user holding multiple roles:

```kotlin
val rbacService: RbacService = // injected bean
val userRoles = setOf("editor", "viewer")

rbacService.getRoleNamespaces(userRoles)
    .collectList()
    .subscribe { namespaces ->
        println("Accessible namespaces: $namespaces")
        // Output: [article, comment, user-profile]
    }

```

The `getRoleNamespaces` method aggregates permissions across all provided roles, returning distinct namespace strings suitable for resource filtering.

## Summary

- **CoSky RBAC** uses Redis Hash structures to store role definitions (`system:role_idx`) and their permission bindings (`system:role_resource_bind:{roleName}`).
- **`RbacService`** provides reactive CRUD operations using `ReactiveStringRedisTemplate`, ensuring non-blocking I/O throughout the security layer.
- The **REST API** exposes role management through `RoleController` with endpoints for listing, binding, creating, and deleting roles.
- A **built-in ADMIN role** is automatically included in all role listings, providing default superuser access.
- The system supports **namespace-level authorization**, allowing fine-grained control over service registry and configuration resources.

## Frequently Asked Questions

### How does CoSky store role permissions in Redis?

CoSky stores RBAC data in two Redis Hash keys under the `system` namespace. The `system:role_idx` Hash maps role names to descriptions, while each role has a dedicated Hash at `system:role_resource_bind:{roleName}` containing field-value pairs of `namespace → action` (such as `article → WRITE`).

### What is the purpose of the SaveRoleRequest class in CoSky's RBAC system?

The `SaveRoleRequest` class serves as the Data Transfer Object for creating or updating roles through the REST API. It contains a `desc` field for the role description and a `resourceActionBind` list of pairs mapping resource namespaces to permitted actions, which `RbacService` persists to Redis.

### Does CoSky RBAC support reactive programming patterns?

Yes, the entire RBAC implementation is built on Spring WebFlux and Project Reactor. The `RbacService` class returns `Mono` and `Flux` types for all operations, using `ReactiveStringRedisTemplate` to perform non-blocking Redis interactions suitable for high-throughput reactive applications.

### How does CoSky handle the default administrator role?

CoSky automatically injects a built-in **ADMIN** role into every response from the `allRole()` method. This hardcoded role exists independently of Redis storage and ensures that administrative access is always available regardless of the dynamic role configuration stored in `system:role_idx`.