ACL Persistence Manager

ACL Persistence Manager

A persistence layer for the FXServer access control list.

For information on how the ACL works please take a look here for a comprehensive guide by Vespura

Download: Release v1.0.0 · Jazzuh/AclPersistenceManager · GitHub

Github: GitHub - Jazzuh/AclPersistenceManager: Persistence layer for the FXServer access control list.

Features

  • Automatic loading of ACEs and principals on server start
  • Support for different database adapters. Currently kvp and mysql are supported
  • Commands for registering ACEs & principals
  • Exports for registering ACEs & principals

To-do

  • Management UI (webadmin-esque type thing)

Dependencies

Installation

  1. Download the latest release from here.
  2. Extract it and place into your resources folder.
  3. Edit the config.cfg file found within the root of the resource.
  4. Add exec @aclmanager/exec.cfg to your server.cfg.

API

Commands

acl grant <principal> <object> <allow|deny> -- Equivalent to add_ace
acl revoke <principal> <object> <allow|deny> -- Equivalent to remove_ace

acl inherit <child> <parent> -- Equivalent to add_principal
acl disinherit <child> <parent> -- Equivalent to remove_principal

Exports

exports['aclmanager']:HasLoaded() -> bool

exports['aclmanager']:AddAce(string: principal, string: object, string: allowType) -> void
exports['aclmanager']:AddAces(Ace[]: aces) -> void

exports['aclmanager']:RemoveAce(string: principal, string: object, string: allowType) -> void
exports['aclmanager']:RemoveAces(Ace[]: aces) -> void

exports['aclmanager']:AddPrincipal(string: child, string: parent) -> void
exports['aclmanager']:AddPrincipals(Principal[]: principals) -> void

exports['aclmanager']:RemovePrincipal(string: child, string: parent) -> void
exports['aclmanager']:RemovePrincipals(Principal[]: principals) -> void

Events

-- All these events are only called on the server

-- Called on inital load of the ACL
AddEventHandler('acl:onLoaded', function() end)

-- Called whenever the ACL has entries added or removed
AddEventHandler('acl:onReloaded', function() end)

-- Called on resource stop once all ACL entries have been unloaded
AddEventHandler('acl:onUnloaded', function() end)

Permissions

To keep the nesting around ACE objects .s into account, any action will be hit with a permission check to ensure the calling source is allowed to perform the specified action.

Although nesting is only taken into account for ACEs, this check will also be applied to Principals as a “just in case” measure to protect against bad actors trying to give themselves permissions they were not explicitly granted.

Each action has a corresponding ACE that must be permitted in order for the action to take effect:

  • acl.grant.{ace}

    • This applies to the acl grant command and the AddAce(s) exports
  • acl.revoke.{ace}

    • This applies to the acl revoke command and the RemoveAce(s) exports
  • acl.inherit.{parentPrincipal}

    • This applies to the acl inherit command and the AddPrincipal(s) exports
  • acl.disinherit.{parentPrincipal}

    • This applies to the acl disinherit command and the RemovePrincipal(s) exports

Usage

Command

 -- Make sure the player doing this has the `acl.grant.command.killall` ace granted
 > acl grant group.admin command.killall allow

 -- Make sure the player doing this has the `acl.inherit.group.admin` ace granted
 > acl inherit group.superadmin group.admin

Lua

Lua comes with a helper class which you can use by adding server_script '@aclmanager/helper.lua' to your resource manifest

generic_admin_script.lua

-- Ensures that the ACL has been fully loaded before trying to edit it
ACL.OnLoaded(function()
    -- Give admin permission to all commands. Make sure the resource calling this has the 'acl.grant.command' granted
    ACL.AddAce("group.admin", "command", "allow")

    ACL.AddAces({
        Ace("group.moderator", "command.kick", "allow"), -- Helper function which will format the ace for you
        { "group.moderator", "auditlog", "allow"}, -- Can also just do it yourself if you want to
    })

    -- Let the admin group in inherit from the moderator group. Make sure the source calling this has the 'acl.inherit.group.moderator' granted
    ACL.AddPrincipal("group.admin", "group.moderator")
end)

C#

GenericAdminScript.cs

using CitizenFX.Core;
using System.Collections.Generic;

public class GenericAdminScript : BaseScript 
{
    // Ensures that the ACL has been fully loaded before trying to edit it
    [EventHandler("acl:onLoaded")]
    private void OnAclLoaded()
    {
        var aclManager = Exports["aclmanager"]

        // Give admin permission to all commands. Make sure the resource calling this has the 'acl.grant.command' granted
        aclManager.AddAce("group.admin", "command", "allow")

        aclManager.AddAces(new List<object>
        {
            new List<object> { "group.moderator", "command.kick", "allow" }, 
            new List<object> { "group.moderator", "auditlog", "allow"}, 
        })

        // Let the admin group in inherit from the moderator group. Make sure the source calling this has the 'acl.inherit.group.moderator' granted
        aclManager.AddPrincipal("group.admin", "group.moderator")
    }
}

What does this add that doesn’t already work out-of-the-box?

Persistence. The built-in ACL is transient so any entries added will only exist until the server stops. This resource just keeps track of added entries so they can be re-added on server startup.

I understand that, but what’s the difference between adding entries in a .cfg file and doing it via your resource? If anything, it seems like more steps are required for the same result.

You can do that if you want, just make sure to use the provided commands. Adding via script is just providing more flexibility to whoever decides to use the resource.

It also allows ACL entries to be added as a sort of “one and done” type deal where they add it once via script (presumably during some kind of registration) and then they are able to assume it’ll always exist after that.

Registering with this resource also allows for a management UI to be implemented in the future, which is what i’m working on right now