Basic Aces & Principals overview/guide

Basics: Aces & Principals

Aces and principals are the default “permissions” system built into fxserver. It’s very easy to use, as long as you know the basics and you just start trying out some stuff.

It’s also very easy to use this from within resources. Just use ExecuteCommand() to add/remove aces or principals, and use IsPlayerAceAllowed() to check if a player is allowed access to an ace (either directly or via inherited permissions).


Principals

Let’s start by taking a look at what a Principal is, and how you can use it in the most simplistic way.
Principals are identifiers that hold permissions (aces). Those identifiers can either be player identifiers (ip, steam or license), or they can be made up identifiers (like: group.admin, group.moderator, vip or snail).

Command syntax

The command used to add identifiers has the following syntax:

# Adds the child principal to the parent principal.
add_principal <child_identifier> <parent_identifier>

# Removes the child principal from the parent principal.
remove_principal <child_identifier> <parent_identifier>

Example

Let’s say we have 2 players. “Player A” and “Player B”. We’ll assume that their (fake) identifiers are: player:a and player:b. We also have 2 groups, which are identified by: snail and group.admin.
To add “Player A” to the “snail” group, you can use the following command in the server console:

add_principal identifier.player:a snail

Now, all aces that get added to the “snail” group, will automatically be set for identifier.player:a (so “Player A”).

Inheritance

Principals can also inherit from other principals. For example, if we want the “group.admin” group to also get all permissions from the “snail” group, you’d do this:

add_principal group.admin snail

NOTE There is an important catch with using inheritance, which will be explained in the Aces section.

builtin.everyone

The builtin.everyone principal is a “group” that every single player is in. This means, if you allow builtin.everyone a specific ace, everyone will have that ace as allowed.
Even if you add someone to a specific group/principal, they will still always inherit all permissions from builtin.everyone.

At this point, the inheritance tree should look something like this:

builtin.everyone # (global parent of everything)
|
| .... snail # (parent & child)
|       |
| ....  +---- group.admin # (child, has all "snail" permissions)
|       |
| ....  +--- identifier.player:a # (child, has all "snail" permissions)

Aces

Aces can be seen as a “permission node”, which can either be allowed or denied.
By default, all aces are set to unset aka deny. Which means, that if you were to check for an ace that was not setup, it will return false (deny).

The default unset value can be overwritten by (manually) setting it to either deny or allow. Setting something to deny will actually set it to DENY-ALWAYS. Which means, it can NOT be overwritten by something like parent principals. Setting it to allow will grant the ace and is also NOT able to be overwritten by parent principals. Basically it’s just a fist come first serve principle, the first permission to be set, cannot be overwritten by setting it to the opposite state after setting the first state.

Ace-command syntax

# Adds the ace to the principal, and either "allows" or "denies" it.
add_ace <principal_identifier> <ace_string> <allow|deny>

# Removes the ace from the principal, note it only removes the "allow" or "deny" ace, depending on whichever one you select.
remove_ace <principal_identifier> <ace_string> <allow|deny>

To illustrate this, take a look at the following example:

Example

Let’s take Player A and Player B, snail and group.admin principals from the previous example.
In the previous example, we added PLayer A to the snail group, and we also set the group.admin group to inherit everything from the snail principal (group)
Let’s say that Player B has been added to the group.admin principal. (add_principal identifier.player:b group.admin)

At this point, the inheritance tree looks like this:

builtin.everyone # (global parent of everything)
|
| .... snail # (parent & child)
|       |
| ....  +---- group.admin # (parent & child, has all "snail" permissions)
|       |       |
| ....  |  ...  +--- identifier.player:b # (child, has all "group.admin" AND all "snail" permissions)
|       |
| ....  +--- identifier.player:a # (child, has all "snail" permissions)

If we want to give Player B the i.am.cool ace, we 4 options to do so in this current setup.

  1. Add the ace to builtin.everyone
  2. Add the ace to snail
  3. Add the ace to group.admin
  4. Add the ace directly to identifier.player:b

However, we probably only want to add it to the 3rd or 4th option. Because adding it to 1 will grant it to everyone, and we don’t want that, and adding it to option 2 will allow everyone in the snail group to use it, which is also not what we want in this case.

So to add the i.am.cool ace to the group.admin principal, we’d do something like this:

add_ace group.admin "i.am.cool" allow

The group.admin principal, and all of it’s child principals (in this case, identifier.player:b), now have access to the i.am.cool ace.

Wildcards

Of course, aces have wildcards!
So, let’s say we want to give Player A permission to every i.am.*** ace, because Player A is .cool, .awesome and .snailsome as well. Then we can simply add the i.am ace to identifier.player:a, now player a has every i.am.*** ace.

add_ace identifier.player:a "i.am" allow
# Player a is now: cool, awesome and snailsome!

Player B however, is still only cool. Let’s make Player B snailsome as well, because they’re part of the snail group after all. But we DON’T want to give them the i.am.awesome ace. In this case, we could simply add the i.am.snailsome ace to either the group.admin or identifier.player:b principals, but we’re going to do it slightly different, just because WE CAN. So how are we going to do it you may ask? Well just like this:

add_ace group.admin "i.am.awesome" deny
add_ace group.admin "i.am" allow
# Player b is now: cool & snailsome but NOT awesome!

Notice how we set the deny for the i.am.awesome first, and then we set the i.am wildcard to allow. Remember what we said about the first come first serve principle?
Now, group.admin (and thus Player B) has access to all states i.am.cool and i.am.snailsome EXCEPT for one, which is the i.am.awesome ace, because we set that to deny FIRST.


Commands overview

To summarize everything above, here’s a quick overview of all commands + 2 commands that aren’t listed above.

Command Syntax Description
add_ace <principal_identifier> <ace_name> <allow/deny> Sets the ace for the specified principal identifier to either allow or deny.
removes_ace <principal_identifier> <ace_name> <allow/deny> Removes either the allow or deny ace for that principal.
add_principal <child_principal> <parent_principal> Adds the child principal to the parent principal, giving the child all aces of the parent principal.
remove_principal <child_principal> <parent_principal> Removes the child principal from the specified parent principal (revoking all previously inherited permissions).
Bonus commands
list_aces Will show a list of all aces, for each principal identifier and show’s the granted status (allow, deny(/unset) or deny-always)
list_principals Will show a list of all principals (child > parent)
49 Likes

Very useful, thanks for this.

5 Likes

@Vespura do you know, how i can give a resource permissions for commands by ace?

Use resource.ResourceNameHere as an identifier for it.

@Briglair thank you :smiley:

1 Like

@Briglair
if i do

add_ace resource.NoFormatName command.say allow

in the server.cfg, it should give the script “NoFormatName” the ace for command “/say”, right?

So it should work, when my script looks like this:

ExecuteCommand("say Test!");

right? It doesnt.

It should… is your resource folder spelled exactly with those caps, too? I would imagine this to be case sensitive. You could also just try

add_ace resource.NoFormatName command allow

if you don’t mind it having all commands, although the prior should work.

1 Like

@Briglair
So, heres the code:

using CitizenFX.Core;
using static CitizenFX.Core.Native.API;

namespace NoFormatName
{
    public class Class1
    {
        private void HasCon()
        {
            rescan:;
            Wait(300000);
            ExecuteCommand("say Test...");
            PlayerList players = new PlayerList();
            foreach (Player player in players)
            {
                if (player.Name.Contains("^") || player.Name.Contains("~"))
                {
                    int pID = player.ServerId;

                    try
                    {
                        ExecuteCommand("clientkick " + pID + " Dein Name darf keine Formatierungs-Codes enthalten!");
                        ExecuteCommand("say Der Spieler (ID: " + pID + ") wurde gekickt!");
                    }
                    catch
                    {
                        ExecuteCommand("say Der Spieler (ID: " + pID + ") konnte nicht vom Server geworfen werden!");
                    }
                    goto rescan;
                }
                goto rescan;
            }
        }
    }
}

Did i forget something or made a mistake? I couldnt find a mistake.

Well, your code will wait 300000 ms (~83 minutes) before running anything below that Wait. Move the ExecuteCommand before that in order for it to actually be ran.

In addition, do you have any other script calling this method? If that is the only C# code you have, that is not setup properly.

@Briglair
lol. I have to check that time again. Actually i wanted it to be 5 minutes… lol.
Thank you :wink:

Im an idiot. That is 5 min. You should also reference this when setting up a C# environment Scripting in C# - Cfx.re Docs

@Briglair ye, i just calculated it again, i still got 300000ms. But why doesnt the script run then? Do i need to tell it, that it needs to run the void HasCon();?

Yep… I’m just an idiot and did 300000/60/60 instead of /1000/60…

Anyway… Look at this instead [How-to] Setting up C# and creating a basic resource It provides a bit more info. You need to derive from BaseScript in order for FiveM to run your stuff.

1 Like

Maybe something wrong in my __resource.lua file?

resource_manifest_version '05cfa83c-a124-4cfa-a768-c24a5811d8f9'

name 'NoFormatName'
description 'Kicks players having formating-codes in their names.'
author 'Agiger03'
version 'v1.0'

files {

}

client_scripts {

}

server_scripts {
    'NoFormatName.dll',
}

@Briglair
maybe @Vespura can help…

1 Like

This is no longer related to aces/permissions is it?

2 Likes

@Vespura nah, actually it isnt.

Then please make a new topic for it so we can keep this related to aces :wink:

@Vespura i did: Help with script (C#)

Hey i need help badly so i made a script that attaches a flashlight to a pistol if u have it and it worked without perms when i tried to add perms it didnt work here are all my things:-
CLIENT.LUA

RegisterNetEvent("Police:GiveFlash")
AddEventHandler("Police:GiveFlash", function()
    if HasPedGotWeapon(PlayerPedId(), "weapon_pistol", 0) then
        GiveWeaponComponentToPed(PlayerPedId(), "weapon_pistol", "COMPONENT_AT_PI_FLASH")
    end
end)

SERVER.LUA

RegisterCommand("flash", function(source, args, rawCommand)
    if IsPlayerAceAllowed(source, "police.flash") then
        TriggerClientEvent("Police:GiveFlash", source)
    end
end, false)

RESOURCE

resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937'

client_script "client.lua"

server_script "server.lua"

PERMS.CFG

add_principal police police.flash

add_principal identifier.steam:1100001152b4999 police.flash allow
2 Likes

Here you’re adding group police to group police.flash… why?

You almost did it right here, that should be add_ace, not add_principal.

1 Like