Role Based Access Control

Implemented Role Based Access Control, and need some testing before i push it to master on 1-2 weeks.

With this system we will have more control of what an account can do, in an indivisual base. You can still define what Players can do, but you will be able to allow or deny certain actions to some players.

This system defines:

  • Permissions to performs some actions.[/li][li]Roles: a Set of permissions that has some relation.[/li][li]Group: a Set of roles that has some relation.

Operations:

  • Grant: Assign and allow[/li][li]Deny: Assign and no not allow[/li][li]Revoke: Remove

Precedence to know if something can be done: Grant, Deny. That means, if you are granted some action by a role but you have denied the permission, the action can’t be done.

Some Rules:

  • Groups can only have roles[/li][li]Roles can only have permissions[/li][li]An account can be assigned granted and denied roles. Permissions inherited from roles are granted if role is granted and denied if role is denied[/li][li]An account can be assigned granted and denied permissions.[/li][li]An account can have multiple groups, roles and permissions[/li][li]An account can not have same role granted and denied at same time[/li][li]An account can not have same permission granted and denied at same time

Added some basic structures: (- = Group, * = Role, > = Permission)
Groups:

  • Players
    • Use Battlegrounds

      Join Normal Battleground
      Join Random Battleground
      Join Arenas

    • Use Dungeon Finder

      Join Dungeon Finder

    • Player Commands

      Exec Player commands

  • Moderators
    • Instant Login/Logout

      Skip Queue (Login)
      Instant Logout

    • Moderator Commands

      Exec Moderator Commands

  • GameMasters
    • GameMaster Commands

      Exec GameMaster Commands

  • Administrators
    • Administrator Commands

      Exec Administrator Commands

So, any player (security_level = 0), should belong to group Players and any GameMaster (security_level = 2) should belong to groups “Players, Moderators, GameMasters”.

With this system you can Deny the use of arenas to any account you suspect is not using the Arenas as they should. Also, the system can be easily extended to check any in-game action like use mails, trade, use instances, use auction house, etc etc.

Notes: The diff will create sql/updates/world/world_misc.sql and sql/updates/auth/auth_misc.sql, sqls that need to be run in order to setup the system properly.

auth sql uses FOREIGN Keys to enforce db consistency

So… Need feedback before pushing to master. Also, any comment about improvements are welcome

Edit 1:

  • Corrected param check when using console

  • Added security level - Groups relation (changing gmlevel now updates groups properly)

Edit 2:

  • Corrected bad table order creation

RBAC.diff

FYI this is NOT in the core on any branch yet. this is a testing patch that you all should TEST! and report back with bugs.

will do so sir /emoticons/default_smile.png also this seems great can’t wait to see this patch in action /emoticons/default_smile.png

Patch is out of date

Patch updated to 705d0c8865

  • Added relation between security levels and groups

  • Fixed ReadParams when using console

I like this idea however, I think you might be over complicating it again. Why not follow similar structure as with disable table? Instead of all those tables we could use only one table and do something like this:

account_or_group | type | subtype | operation | comment

account_or_group: a negative number could be used for player account and positive for groups or other way around.

type: This will suggest which category do operations belong to

subtype: A subcategory which will behave using masks

operation: similar to what you described above

Lets say I want to disable the use of all Battleground related stuff for gm level 0, then I would do something like this:

account_or_group = 0 //For all players
type = 1 //Battlegrounds
subtype = 1 + 2 + 4 = 7 //Join Normal Battlegrounds, Join Random Battlegrounds, Join Arenas
operation = 1 //Disable

Now if I want to disable joining arenas for a particular account: (lets say id: 1337)

account_or_group = -1337 //Account Id type = 1 // Battlegrounds subtype = 4 //Join arenas operating = 1 //Disable

Yea that whould be awesome what @Nex said

also thanks for new updates and i will test it out now /emoticons/default_smile.png

SQL error :

– Need them first in case of re-execute due to foreign keys DROP TABLE IF EXISTS rbac_account_permissions; DROP TABLE IF EXISTS rbac_account_roles; DROP TABLE IF EXISTS rbac_account_groups; DROP TABLE IF EXISTS rbac_role_permissions; DROP TABLE IF EXISTS rbac_group_roles; DROP TABLE IF EXISTS rbac_permissions; DROP TABLE IF EXISTS rbac_roles; DROP TABLE IF EXISTS rbac_groups; DROP TABLE IF EXISTS rbac_security_level_groups; CREATE TABLE IF NOT EXISTS rbac_security_level_groups ( secId int(10) unsigned NOT NULL COMMENT ‘Security Level id’, groupId int(10) unsigned NOT NULL COMMENT ‘group id’, PRIMARY KEY (secId, groupId), CONSTRAINT fk__rbac_security_level_groups__rbac_groups FOREIGN KEY (groupId) REFERENCES rbac_groups(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘Default groups to assign when an account is set gm level’; /* SQL Error (1005): Can’t create table ‘auth.rbac_security_level_groups’ (errno: 150) Foreign key constraint is incorrectly formed / CREATE TABLE IF NOT EXISTS rbac_groups ( id int(10) unsigned NOT NULL DEFAULT ‘0’ COMMENT ‘Group id’, name varchar(50) NOT NULL COMMENT ‘Group name’, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘Group List’; CREATE TABLE IF NOT EXISTS rbac_roles ( id int(10) unsigned NOT NULL DEFAULT ‘0’ COMMENT ‘Role id’, name varchar(50) NOT NULL COMMENT ‘Role name’, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘Roles List’; CREATE TABLE IF NOT EXISTS rbac_permissions ( id int(10) unsigned NOT NULL DEFAULT ‘0’ COMMENT ‘Permission id’, name varchar(100) NOT NULL COMMENT ‘Permission name’, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘Permission List’; CREATE TABLE IF NOT EXISTS rbac_group_roles ( groupId int(10) unsigned NOT NULL COMMENT ‘group id’, roleId int(10) unsigned NOT NULL COMMENT ‘Role id’, PRIMARY KEY (groupId, roleId), CONSTRAINT fk__rbac_group_roles__rbac_roles FOREIGN KEY (roleId) REFERENCES rbac_roles(id) ON DELETE CASCADE ON UPDATE RESTRICT, CONSTRAINT fk__rbac_group_roles__rbac_groups FOREIGN KEY (groupId) REFERENCES rbac_groups(id) ON DELETE CASCADE ON UPDATE RESTRICT ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘Group Role relation’; CREATE TABLE IF NOT EXISTS rbac_role_permissions ( roleId int(10) unsigned NOT NULL COMMENT ‘Role id’, permissionId int(10) unsigned NOT NULL COMMENT ‘Permission id’, PRIMARY KEY (roleId, permissionId), CONSTRAINT fk__role_permissions__rbac_roles FOREIGN KEY (roleId) REFERENCES rbac_roles(id) ON DELETE CASCADE ON UPDATE RESTRICT, CONSTRAINT fk__role_permissions__rbac_permissions FOREIGN KEY (permissionId) REFERENCES rbac_permissions(id) ON DELETE CASCADE ON UPDATE RESTRICT ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘Role Permission relation’; CREATE TABLE IF NOT EXISTS rbac_account_groups ( accountId int(10) unsigned NOT NULL COMMENT ‘Account id’, groupId int(10) unsigned NOT NULL COMMENT ‘Group id’, realmId int(11) NOT NULL DEFAULT ‘-1’ COMMENT ‘Realm Id, -1 means all’, PRIMARY KEY (accountId, groupId, realmId), CONSTRAINT fk__rbac_account_groups__account FOREIGN KEY (accountId) REFERENCES account(id) ON DELETE CASCADE ON UPDATE RESTRICT, CONSTRAINT fk__rbac_account_groups__rbac_groups FOREIGN KEY (groupId) REFERENCES rbac_groups(id) ON DELETE CASCADE ON UPDATE RESTRICT ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘Account-Group relation’; CREATE TABLE IF NOT EXISTS rbac_account_roles ( accountId int(10) unsigned NOT NULL COMMENT ‘Account id’, roleId int(10) unsigned NOT NULL COMMENT ‘Role id’, granted tinyint(1) NOT NULL default 1 COMMENT ‘Granted = 1, Denied = 0’, realmId int(11) NOT NULL DEFAULT ‘-1’ COMMENT ‘Realm Id, -1 means all’, PRIMARY KEY (accountId, roleId, realmId), CONSTRAINT fk__rbac_account_roles__account FOREIGN KEY (accountId) REFERENCES account(id) ON DELETE CASCADE ON UPDATE RESTRICT, CONSTRAINT fk__rbac_account_roles__rbac_roles FOREIGN KEY (roleId) REFERENCES rbac_roles(id) ON DELETE CASCADE ON UPDATE RESTRICT ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘Account-Role relation’; CREATE TABLE IF NOT EXISTS rbac_account_permissions ( accountId int(10) unsigned NOT NULL COMMENT ‘Account id’, permissionId int(10) unsigned NOT NULL COMMENT ‘Permission id’, granted tinyint(1) NOT NULL default 1 COMMENT ‘Granted = 1, Denied = 0’, realmId int(11) NOT NULL DEFAULT ‘-1’ COMMENT ‘Realm Id, -1 means all’, PRIMARY KEY (accountId, permissionId, realmId), CONSTRAINT fk__rbac_account_permissions__account FOREIGN KEY (accountId) REFERENCES account(id) ON DELETE CASCADE ON UPDATE RESTRICT, CONSTRAINT fk__rbac_account_roles__rbac_permissions FOREIGN KEY (permissionId) REFERENCES rbac_permissions(id) ON DELETE CASCADE ON UPDATE RESTRICT ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=‘Account-Permission relation’; DELETE FROM rbac_permissions WHERE id BETWEEN 1 AND 10; INSERT INTO rbac_permissions (id, name) VALUES (1, ‘Instant logout’), (2, ‘Skip Queue’), (3, ‘Join Normal Battleground’), (4, ‘Join Random Battleground’), (5, ‘Join Arenas’), (6, ‘Join Dungeon Finder’), (7, ‘Player Commands (Temporal till commands moved to rbac)’), (8, ‘Moderator Commands (Temporal till commands moved to rbac)’), (9, ‘GameMaster Commands (Temporal till commands moved to rbac)’), (10, ‘Administrator Commands (Temporal till commands moved to rbac)’); DELETE FROM rbac_roles WHERE id BETWEEN 1 AND 7; INSERT INTO rbac_roles (id, name) VALUES (1, ‘Player Commands’), (2, ‘Moderator Commands’), (3, ‘GameMaster Commands’), (4, ‘Administrator Commands’), (5, ‘Quick Login/Logout’), (6, ‘Use Battleground/Arenas’), (7, ‘Use Dungeon Finder’); DELETE FROM rbac_groups WHERE id BETWEEN 1 AND 4; INSERT INTO rbac_groups (id, name) VALUES (1, ‘Player’), (2, ‘Moderator’), (3, ‘GameMaster’), (4, ‘Administrator’); DELETE FROM rbac_role_permissions WHERE roleId BETWEEN 1 AND 7; INSERT INTO rbac_role_permissions (roleId, permissionId) VALUES (5, 1), (5, 2), (6, 3), (6, 4), (6, 5), (7, 6), (1, 7), (2, 8), (3, 9), (4, 10); DELETE FROM rbac_group_roles WHERE groupId BETWEEN 1 AND 4; INSERT INTO rbac_group_roles (groupId, roleId) VALUES (1, 1), (1, 6), (1, 7), (2, 2), (2, 5), (3, 3), (4, 4); TRUNCATE rbac_account_groups; INSERT INTO rbac_account_groups (accountId, groupId, realmId) SELECT id, 1, -1 FROM account; – Add Player group to all accounts INSERT INTO rbac_account_groups (accountId, groupId, realmId) SELECT id, 2, RealmID FROM account_access WHERE gmlevel > 0; – Add Moderator group to all Moderator or higher GM level INSERT INTO rbac_account_groups (accountId, groupId, realmId) SELECT id, 3, RealmID FROM account_access WHERE gmlevel > 1; – Add GameMaster group to all GameMasters or higher GM level INSERT INTO rbac_account_groups (accountId, groupId, realmId) SELECT id, 4, RealmID FROM account_access WHERE gmlevel > 2; – Add Administrator group to all Administrators TRUNCATE rbac_security_level_groups; / SQL Error (1146): Table ‘auth.rbac_security_level_groups’ doesn’t exist / / Affected rows: 42 Found rows: 0 Warnings: 9 Duration for 33 of 35 queries: 1.202 sec. */

or to make it shorter :

TRUNCATE rbac_security_level_groups; /* SQL Error (1146): Table ‘auth.rbac_security_level_groups’ doesn’t exist / / Affected rows: 42 Found rows: 0 Warnings: 9 Duration for 33 of 35 queries: 1.202 sec. */

Ideal design

Permissions have to be known by core, that means you can’t create new permissions in DB.

Roles doesn’t need to be known and you can create as much as you want in DB.

Groups doesn’t need to be know and you can create as much as you want in DB.

  • rbac_permissions, rbac_roles, rbac_groups to allow to use foreign keys and make it easy to see names, or even change names without recompilation.

  • rbac_account_groups, rbac_account_roles and rbac_account_permissions could have been merged in one single table, but then, no foreign keys

Why use foreign keys?

  • Keeps data 100% valid, there is no way to add non-existing roles, groups or permissions, neither add valid ones to non-existing account.

  • Makes it simple code loading, as you know all the data is valid

  • Makes it simple when deleting accounts, as you don’t need to add code to delete data from this tables

@Nex:

  • You are forcing roles (type 1 = BGs)

  • You seem to use forced groups (one group per existing gmlevel)

  • Core needs to be recompiled in order to add a single role and there is no way to create new groups (lets say PVPers with permissions to join bgs and dueling)

  • You can have bad data in tables, being forced to check for valid data at loading and deleting data when deleting an account.

@wowgm123:

  • Thanks for the report, fixed in new patch. Edited 1st post

/emoticons/default_smile.png i am gona start testing this patch this weekend cuz of school /emoticons/default_sad.png

Is it possible to include with this more control over GM, such as each GM account has only 1 GM char at a time or something? add a console command that allows a GM account to specify a char to be his GM char (only if he has no current GM char) no command for making a GM char to a regular char, the old char must be deleted (permanently) before assigning a new one, to keep the GM from over powering all of his chars by promoting each one to GM… or, have a command, but, make it strip the char of all items and powers and make it either level 1 or a level set in the conf file.

maybe not to be included directly with this patch, but, some thoughts for future patches…

I know some of you will hate on me for this, as usual, but, I can’t help sharing my ideas…

Thats a pritty good idea man

I actually agree to a certain point; I’d rather suggest making it a command that grants ability to set a character to “GM”-char and limit the number altogether within the config file. Now that these changes are immediate, it would be fitting to make some adjustments, after all. For example, as rough overview, wouldn’t it make sense to attempt to make specific sub-command structure changes (e.g. “.loo spell” for one and “.loo item” for another. Or even “.unstuck” and “.unstuck person”) ?

@paradox: that is already planned for in the future, now test the patch and give feedback - it’s really needed for proper RBAC to hit the codebase

@d3reap3r: the command-roles are planned in the future (as it requires a bit of ‘changes’ all over the place), expanding the commandsystem to “access-blocks” or similar. That is however outside the scope of TESTING this patch for now, see the above answer to Paradox…

Pushed: https://github.com/TrinityCore/TrinityCore/commit/b980aff83e214bab60f141c879c2a392789a4d16