Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/database-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,14 @@

## M

- [module_rbac_permissions](module_rbac_permissions)
- [motd](motd)

## R
- [rbac_account_permissions](rbac_account_permissions)
- [rbac_default_permissions](rbac_default_permissions)
- [rbac_linked_permissions](rbac_linked_permissions)
- [rbac_permissions](rbac_permissions)
- [realmcharacters](realmcharacters)
- [realmlist](realmlist)

Expand Down
1 change: 1 addition & 0 deletions docs/documentation-index.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ redirect_from: /documentation_index
* [Monitoring AzerothCore with Grafana](monitoring-azerothcore-with-grafana)
* [MySQL types (C++)](mysqltypescpp)
* [Project Versioning](project-versioning)
* [RBAC](rbac)
* [Remote Access](remote-access)
* [Sniffing & Parsing](sniffing-and-parsing)
* [Spell Effects Reference](spell-effects-reference)
Expand Down
121 changes: 121 additions & 0 deletions docs/module_rbac_permissions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# module\_rbac\_permissions

[<-Back-to:Auth](database-auth)

**The \`module\_rbac\_permissions\` table**

This table allows modules to register their own RBAC permissions without risk of ID collisions with core permissions or other modules. Each module uses local IDs (1, 2, 3, ...) and the table auto-assigns global IDs starting at 100000.

For a system overview, see [RBAC](rbac).

**Table Structure**

| Field | Type | Attributes | Key | Null | Default | Extra | Comment |
| -------------- | ------------ | ---------- | ------ | ---- | ------- | -------------- | ------------------------------------ |
| [module](#module) | VARCHAR(255) | SIGNED | PRI | NO | | | Module dir name, e.g. mod-cfbg |
| [id](#id) | INT | UNSIGNED | PRI | NO | | | Module-local permission id |
| [global_id](#globalid) | INT | UNSIGNED | UNIQUE | NO | | AUTO_INCREMENT | Auto-assigned global permission ID |
| [name](#name) | VARCHAR(100) | SIGNED | | NO | | | Permission name |

The primary key is the composite `(module, id)`, which prevents cross-module ID collisions. The `global_id` column has a separate `UNIQUE` index and auto-increments starting at 100000.

**Description of the fields**

### module

The module directory name (e.g. `mod-cfbg`, `mod-eluna`). This must match the module's directory name under `modules/`.

### id

A module-local permission ID. Each module manages its own ID sequence starting at 1. The same local ID can be used by different modules without conflict because the primary key includes the module name.

### global\_id

An auto-assigned globally unique permission ID used by the core RBAC system. The `AUTO_INCREMENT` starts at 100000 to avoid collisions with core permission IDs (1–924). Modules should never set this value manually — it is assigned by the database on insert.

### name

A human-readable name for the permission. By convention, command permissions use the format `Command: .commandname subcommand`.

## Module Integration Guide

### Step 1: Register permissions in SQL

Create a SQL file in your module's `data/sql/db-auth/` directory:

```sql
INSERT IGNORE INTO `module_rbac_permissions` (`module`, `id`, `name`) VALUES
('mod-example', 1, 'Command: .example hello'),
('mod-example', 2, 'Command: .example info');
```

Use `INSERT IGNORE` so the SQL is safe to re-run.

### Step 2: Look up global IDs in C++

In your module's C++ code, use `AccountMgr` to translate local IDs to global IDs:

```cpp
#include "AccountMgr.h"

uint32 globalId = sAccountMgr->GetModulePermission("mod-example", 1);
```

Returns 0 if the permission is not found in the database.

### Step 3: Use in CommandScript

When defining commands, use the global ID as the permission for each command:

```cpp
static uint32 GetPermission(uint32 localId)
{
return sAccountMgr->GetModulePermission("mod-example", localId);
}

std::vector<ChatCommand> GetCommands() const override
{
static std::vector<ChatCommand> exampleCommandTable =
{
{ "hello", GetPermission(1), false, &HandleHelloCommand, "" },
{ "info", GetPermission(2), false, &HandleInfoCommand, "" },
};

static std::vector<ChatCommand> commandTable =
{
{ "example", SEC_PLAYER, false, nullptr, "", exampleCommandTable },
};

return commandTable;
}
```

### Step 4: Assign permissions

Module permissions need to be assigned to roles or accounts before anyone can use them.

**Assign to a role** — link the module permissions into a command role via [rbac_linked_permissions](rbac_linked_permissions). Everyone who inherits that role will gain access:

```sql
-- Grant all mod-example commands to the GM Commands role (197)
INSERT IGNORE INTO `rbac_linked_permissions` (`id`, `linkedId`)
SELECT 197, `global_id`
FROM `module_rbac_permissions`
WHERE `module` = 'mod-example';
```

**Assign to a specific account** — insert into [rbac_account_permissions](rbac_account_permissions) or use the `.rbac` command in-game:

```sql
-- Grant mod-example's first command to account 5 on all realms
INSERT INTO `rbac_account_permissions` (`accountId`, `permissionId`, `granted`, `realmId`)
SELECT 5, `global_id`, 1, -1
FROM `module_rbac_permissions`
WHERE `module` = 'mod-example' AND `id` = 1;
```

Or in-game, once you know the global ID (e.g. 100001):

```
.rbac account grant 5 100001
```
109 changes: 109 additions & 0 deletions docs/rbac.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# RBAC

[<-Back-to:Documentation Index](documentation-index)

## Overview

Role-Based Access Control (RBAC) is AzerothCore's permission system. It provides fine-grained control over what each account can do — from individual commands to gameplay privileges like skipping the login queue or joining battlegrounds.

With RBAC you can:

- Control access to every command individually
- Create roles that bundle related permissions together
- Override specific permissions per account without changing their overall rank
- Let modules register their own permissions without conflicting with core IDs

## Architecture

The RBAC system is built on four concepts:

1. **Permissions** — Individual capabilities (e.g. "can use `.tele`", "skip queue", "join battlegrounds")
2. **Roles** — Permissions that link to other permissions, forming a group. Granting a role grants everything it contains
3. **Defaults** — Mappings from `account_access.gmlevel` to an initial RBAC role, so security levels map to permission sets automatically
4. **Account Overrides** — Per-account grants or denies that fine-tune access beyond the defaults

### Database Tables

| Table | Purpose |
| ----- | ------- |
| [rbac_permissions](rbac_permissions) | Defines all available permissions |
| [rbac_linked_permissions](rbac_linked_permissions) | Links roles to their child permissions |
| [rbac_default_permissions](rbac_default_permissions) | Maps security levels to default roles |
| [rbac_account_permissions](rbac_account_permissions) | Per-account overrides (grant/deny) |
| [module_rbac_permissions](module_rbac_permissions) | Module-registered permissions |

A convenience view `vw_rbac` joins the linked and default tables for easier querying.

## Permission ID Ranges

| Range | Purpose | Examples |
| ----- | ------- | ------- |
| 1–53 | Gameplay permissions | Instant logout, skip queue, join BG/arena/dungeon finder |
| 192–195 | Security-level roles | Administrator (192), Gamemaster (193), Moderator (194), Player (195) |
| 196–199 | Command roles | Admin Commands (196), GM Commands (197), Mod Commands (198), Player Commands (199) |
| 200–924 | Individual command permissions | One per `.command` |
| 100000+ | Module permissions | Auto-assigned via [module_rbac_permissions](module_rbac_permissions) |

## Role Hierarchy

Roles inherit from each other through linked permissions. Each higher role links to the one below it, so an Administrator automatically receives every permission a Player has.

```
Administrator (192)
├── Core admin perms (7, 21, 42, 43)
├── Admin Commands (196)
└── Gamemaster (193)
├── Core GM perms (45, 48, 52, 53)
├── GM Commands (197)
└── Moderator (194)
├── Core mod perms (1, 2, 9, 11, 13–47, 51, ...)
├── Mod Commands (198)
└── Player (195)
├── Core player perms (3, 4, 5, 6, 24, 49, 50)
└── Player Commands (199)
```

### Default Security Level Mapping

When a player logs in, their `account_access.gmlevel` determines which role they receive:

| gmlevel | Role | Permission ID |
| ------- | ---- | ------------- |
| 3 | Administrator | 192 |
| 2 | Gamemaster | 193 |
| 1 | Moderator | 194 |
| 0 | Player | 195 |

These defaults are stored in [rbac_default_permissions](rbac_default_permissions).

## Permission Resolution

When an account's permissions are calculated, the following steps occur:

1. **Collect grants** — Account-specific grants from [rbac_account_permissions](rbac_account_permissions) plus defaults from [rbac_default_permissions](rbac_default_permissions)
2. **Expand grants** — Each granted permission is expanded recursively through [rbac_linked_permissions](rbac_linked_permissions). If permission 192 links to 193, and 193 links to 194, all are included
3. **Collect denies** — Account-specific denies from [rbac_account_permissions](rbac_account_permissions)
4. **Expand denies** — Denied permissions are also expanded through linked permissions
5. **Subtract** — Final permissions = Expanded Grants − Expanded Denies

This means denying a role denies everything that role contains.

## Commands

The `.rbac` commands allow live management of account permissions without restarting the server.

| Command | Permission | Description |
| ------- | ---------- | ----------- |
| `.rbac account list <account>` | 202 | List granted, denied, and default permissions for an account |
| `.rbac account grant <account> <permId> [realmId]` | 203 | Grant a permission to an account |
| `.rbac account deny <account> <permId> [realmId]` | 204 | Deny a permission for an account |
| `.rbac account revoke <account> <permId> [realmId]` | 205 | Revoke a previously granted or denied permission |
| `.rbac list [permId]` | 206 | List all permissions, or show details for a specific permission |

Changes take effect immediately for online players.

## Module Integration

Modules can register their own RBAC permissions using the [module_rbac_permissions](module_rbac_permissions) table. Each module uses local IDs (1, 2, 3, ...) that are automatically mapped to global IDs starting at 100000, avoiding conflicts with core permission IDs and between modules.

See [module_rbac_permissions](module_rbac_permissions) for the full integration guide.
48 changes: 48 additions & 0 deletions docs/rbac_account_permissions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# rbac\_account\_permissions

[<-Back-to:Auth](database-auth)

**The \`rbac\_account\_permissions\` table**

This table stores per-account permission overrides. Use it to grant or deny specific permissions to individual accounts, beyond what their default security level provides.

For a system overview, see [RBAC](rbac).

**Table Structure**

| Field | Type | Attributes | Key | Null | Default | Extra | Comment |
| ----------------- | ---------- | ---------- | --- | ---- | ------- | ----- | ------------------------ |
| [accountId](#accountid) | INT | UNSIGNED | PRI | NO | | | Account id |
| [permissionId](#permissionid) | INT | UNSIGNED | PRI | NO | | | Permission id |
| [granted](#granted) | TINYINT(1) | SIGNED | | NO | 1 | | Granted = 1, Denied = 0 |
| [realmId](#realmid) | INT | SIGNED | PRI | NO | -1 | | Realm Id, -1 means all |

The `accountId` field has a foreign key to [account.id](account#id) with `ON DELETE CASCADE`.
The `permissionId` field has a foreign key to [rbac_permissions.id](rbac_permissions#id) with `ON DELETE CASCADE`.

**Description of the fields**

### accountId

The [account ID](account#id).

### permissionId

The permission ID from [rbac_permissions](rbac_permissions). This can be an individual permission or a role. Granting/denying a role affects all permissions linked to it.

### granted

Controls whether this entry is a grant or a deny:

| Value | Meaning |
| ----- | ------- |
| 1 | **Grant** — adds this permission to the account |
| 0 | **Deny** — removes this permission (and its linked permissions) from the account |

During [permission resolution](rbac#permission-resolution), denies are subtracted from grants after expansion. This means denying a role denies everything that role contains.

### realmId

The [realm ID](realmlist#id) this override applies to. Use `-1` to apply to all realms.

These overrides can be managed in-game with the [.rbac commands](rbac#commands).
42 changes: 42 additions & 0 deletions docs/rbac_default_permissions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# rbac\_default\_permissions

[<-Back-to:Auth](database-auth)

**The \`rbac\_default\_permissions\` table**

This table maps [account_access.gmlevel](account_access#gmlevel) security levels to default RBAC roles. When a player logs in, their security level determines which role they receive automatically.

For a system overview, see [RBAC](rbac).

**Table Structure**

| Field | Type | Attributes | Key | Null | Default | Extra | Comment |
| ----------------- | ------- | ---------- | --- | ---- | ------- | ----- | ------------------------ |
| [secId](#secid) | INT | UNSIGNED | PRI | NO | | | Security Level id |
| [permissionId](#permissionid) | INT | UNSIGNED | PRI | NO | | | Permission id |
| [realmId](#realmid) | INT | SIGNED | PRI | NO | -1 | | Realm Id, -1 means all |

The `permissionId` field has a foreign key to [rbac_permissions.id](rbac_permissions#id).

**Description of the fields**

### secId

The security level from [account_access.gmlevel](account_access#gmlevel).

### permissionId

The RBAC permission (role) to grant by default for this security level. The default assignments are:

| secId | permissionId | Role |
| ----- | ------------ | ---- |
| 0 | 195 | Player |
| 1 | 194 | Moderator |
| 2 | 193 | Gamemaster |
| 3 | 192 | Administrator |

Because roles chain through [rbac_linked_permissions](rbac_linked_permissions), granting Administrator (192) automatically includes Gamemaster, Moderator, and Player permissions.

### realmId

The [realm ID](realmlist#id) this default applies to. Use `-1` to apply to all realms.
39 changes: 39 additions & 0 deletions docs/rbac_linked_permissions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# rbac\_linked\_permissions

[<-Back-to:Auth](database-auth)

**The \`rbac\_linked\_permissions\` table**

This table defines the parent-child relationships between permissions. When a permission (typically a role) is granted, all of its linked permissions are also granted. This is how role inheritance works in the [RBAC](rbac) system.

**Table Structure**

| Field | Type | Attributes | Key | Null | Default | Extra | Comment |
| ------------- | ---- | ---------- | --- | ---- | ------- | ----- | -------------------- |
| [id](#id) | INT | UNSIGNED | PRI | NO | | | Permission id |
| [linkedId](#linkedid) | INT | UNSIGNED | PRI | NO | | | Linked Permission id |

Both fields have a foreign key to [rbac_permissions.id](rbac_permissions#id) with `ON DELETE CASCADE`.

**Description of the fields**

### id

The parent permission (role) that contains other permissions. Typically one of the role IDs:

| ID | Role |
| -- | ---- |
| 192 | Administrator |
| 193 | Gamemaster |
| 194 | Moderator |
| 195 | Player |
| 196 | Admin Commands |
| 197 | GM Commands |
| 198 | Mod Commands |
| 199 | Player Commands |

### linkedId

The child permission that is granted when the parent (`id`) is granted. Can be any permission from [rbac_permissions](rbac_permissions), including another role — this is how the hierarchy chains together (e.g. Administrator 192 links to Gamemaster 193, which links to Moderator 194, and so on).

Linked permissions are expanded recursively during [permission resolution](rbac#permission-resolution).
Loading
Loading