Complete guide to the Cotonti access rights system: masks, bits, groups using the example of the Market module
Detailed guide to the Cotonti access rights system: bitmasks, symbols R, W, 1-5, A, user groups, database storage, management functions, and an example of the Market module installer.
1. Introduction
Cotonti is a flexible content management system that employs a sophisticated permission model. This model is built upon user groups, access objects, and bitmask permissions. Understanding this system is critical for developing modules, configuring access rights, and ensuring site security.
This guide provides a detailed explanation of how the permission system works in Cotonti, how symbolic masks ('R', 'W', '1'–'5', 'A') are formed, how they are converted to numeric values and back, how these permissions are stored in the database, and how to use them through the example of the Market module installer. The guide is based on analysis of the core Cotonti code (files functions.php and auth.php) and the Market module installer. All statements are supported by code, without speculation.
2. Fundamentals of Cotonti Permission System
2.1. User Groups
In Cotonti, each user belongs to one main group and can be a member of several additional groups. Groups define the user's base permissions. The core defines the following standard groups (constants):
| Constant | Numeric Value | Description |
|---|---|---|
COT_GROUP_DEFAULT | 0 | Template for new groups (not used as a real user group) |
COT_GROUP_GUESTS | 1 | Guests (unauthenticated visitors) |
COT_GROUP_INACTIVE | 2 | Inactive users (registered but not activated) |
COT_GROUP_BANNED | 3 | Banned users |
COT_GROUP_MEMBERS | 4 | Regular registered users |
COT_GROUP_SUPERADMINS | 5 | Administrators (superadmins) |
COT_GROUP_MODERATORS | 6 | Moderators (if used) |
These constants are declared in functions.php:
const COT_GROUP_DEFAULT = 0;
const COT_GROUP_GUESTS = 1;
const COT_GROUP_INACTIVE = 2;
const COT_GROUP_BANNED = 3;
const COT_GROUP_MEMBERS = 4;
const COT_GROUP_SUPERADMINS = 5;
const COT_GROUP_MODERATORS = 6;Custom user groups can have IDs greater than 6; they inherit rules from COT_GROUP_DEFAULT (see details in the cot_auth_add_item() section).
2.2. Access Objects: area and option
Permissions in Cotonti are assigned not globally but to specific access objects. An object is identified by a pair:
area— a realm or module, e.g.,'page','forums','market'. This string defines the permission context.option— a specific element within the area: a category code, a section ID, or the special marker'a'(meaning the area as a whole). For structure categories, option usually matches their code.
For example, the right to read the category computers-components of the Market module is recorded as area = 'market', option = 'computers-components'.
When checking permissions, the function cot_auth($area, $option, $mask) uses these two parameters to find the corresponding bitwise permissions.
2.3. Bitmask Permissions
Permissions are stored as an integer, each bit of which corresponds to a specific action. This allows efficient packing of multiple permissions into a single number and quick checking using bitwise operations.
Cotonti defines the following permission bits (values from the cot_auth_getvalue() function):
| Symbol | Bit (position) | Decimal Value | Description |
|---|---|---|---|
R | 1 | 1 | Read — viewing objects |
W | 2 | 2 | Write — adding/editing |
1 | 3 | 4 | Special permission level 1 (module-defined) |
2 | 4 | 8 | Special permission level 2 |
3 | 5 | 16 | Special permission level 3 |
4 | 6 | 32 | Special permission level 4 |
5 | 7 | 64 | Special permission level 5 |
A | 8 | 128 | Administration — managing rights and settings |
The final numeric value is calculated as the sum of the values of the allowed bits. For example:
R→ 1RW→ 1 + 2 = 3R1→ 1 + 4 = 5RW1A→ 1 + 2 + 4 + 128 = 135RW12345A→ 1+2+4+8+16+32+64+128 = 255
3. Symbolic Permission Masks
For human convenience, permissions are written as a string of characters. Each character corresponds to one bit. The set of characters is strictly defined.
3.1. Symbols R, W, 1–5, A
R(Read) — permission to read or view the contents of an object.W(Write) — permission to write, create, or modify an object.1,2,3,4,5— five additional permission levels whose interpretation entirely depends on the module. In the core they have no fixed meaning, giving developers flexibility. For example, in the Market module one could assign:1— permission to publish listings;2— permission to edit others' listings;3— permission to moderate (approve/reject);4— permission to manage categories;5— permission for extended settings.
These meanings are not mandatory; each module decides how to use them.
A(Admin) — administration permission. Usually implies the ability to manage access rights to the object, as well as perform actions inaccessible to regular users.
3.2. Function cot_auth_getvalue(): mask → number
The function cot_auth_getvalue($mask) converts a symbolic mask into an integer. It is defined in auth.php:
function cot_auth_getvalue($mask)
{
$mn['0'] = 0;
$mn['R'] = 1;
$mn['W'] = 2;
$mn['1'] = 4;
$mn['2'] = 8;
$mn['3'] = 16;
$mn['4'] = 32;
$mn['5'] = 64;
$mn['A'] = 128;
$res = 0;
$masks = str_split($mask);
foreach ($masks as $k) {
if(isset($mn[$k])) $res += $mn[$k];
}
return $res;
}How it works:
- Initializes an array mapping symbols to values.
- Splits the input string into individual characters.
- For each character, if it exists in the array, adds its value to the result.
- Returns the sum.
Examples:
cot_auth_getvalue('R')→ 1cot_auth_getvalue('RW')→ 3cot_auth_getvalue('RW1A')→ 135cot_auth_getvalue('')→ 0cot_auth_getvalue('0')→ 0 (the character '0' is added for convenience, value 0)
3.3. Function cot_auth_getmask(): number → mask
The reverse function cot_auth_getmask($rn) converts a numeric value into a symbolic mask. It is defined in auth.php:
function cot_auth_getmask($rn)
{
$res = ($rn & 1) ? 'R' : '';
$res .= (($rn & 2) == 2) ? 'W' : '';
$res .= (($rn & 4) == 4) ? '1' : '';
$res .= (($rn & 8) == 8) ? '2' : '';
$res .= (($rn & 16) == 16) ? '3' : '';
$res .= (($rn & 32) == 32) ? '4' : '';
$res .= (($rn & 64) == 64) ? '5' : '';
$res .= (($rn & 128) == 128) ? 'A' : '';
return $res;
}It checks each bit and appends the corresponding symbol. The order of symbols is fixed: R, W, 1, 2, 3, 4, 5, A.
Examples:
cot_auth_getmask(1)→'R'cot_auth_getmask(3)→'RW'cot_auth_getmask(135)→'RW1A'cot_auth_getmask(0)→''
4. Storing Permissions in Database
4.1. Table cot_auth
All access permissions are stored in the cot_auth table (usually $db_auth). This table links user groups, areas, and options with numeric values of permissions and locks.
4.2. Record Structure and Keys
Each table record contains at least the following fields (based on usage in code):
auth_groupid— user group ID.auth_code— area code, e.g.,'market'.auth_option— object (option), e.g., category code, or'a'for the whole area.auth_rights— integer value of allowed permissions (sum of bits).auth_rights_lock— integer value of locked permissions (bitmask of what cannot be changed via interface).auth_setbyuserid— ID of the user who set the permissions (for audit).
Record uniqueness is ensured by the combination (auth_groupid, auth_code, auth_option). This means that for each group and each object (within an area) there is no more than one row with permissions.
When adding permissions via cot_auth_add_item(), one record is created for each group that does not have the skiprights flag set.
5. Permission Management Functions
5.1. cot_auth() — checking user permissions
The main function for checking current user permissions:
function cot_auth($area, $option = null, $mask = 'RWA')Parameters:
$area— area (module).$option— specific object (category code) ornull. Ifnullor'', then'a'(the area itself) is assumed.$mask— string of characters defining which permissions to check. Default is'RWA', i.e., read, write, and administration are checked simultaneously (returns an array of results for each character).
Return value:
- If
$maskconsists of a single character — returnsbool. - If
$maskcontains multiple characters — returns an array[symbol => bool].
How it works:
- Creates an array of bit values (
$mn). - Splits
$maskinto characters. - For each character, checks the corresponding permission of the current user using data from
Cot::$usr['auth'](permission cache built during authorization). - Logs the check in
Cot::$sys['auth_log'].
The function uses the bitwise & operator to check whether the required bit is set in the stored numeric permission value.
Usage example:
if (cot_auth('market', 'computers-components', 'R')) {
// User can read this category
}5.2. cot_auth_add_item() — adding permissions for an object
The function adds permission records for a new object (e.g., a category). Defined in auth.php.
function cot_auth_add_item($module_name, $item_id, $auth_permit = [], $auth_lock = [])Parameters:
$module_name— area.$item_id— object code (option), e.g., category code.$auth_permit— array of allowed permissions by group. Format:[group_id => 'RW1']. If a group is not specified, it is taken from$cot_auth_default_permit.$auth_lock— array of locks by group. Similarly supplemented from$cot_auth_default_lock.
Algorithm:
- Supplements
$auth_permitand$auth_lockwith default values (using the+operator, which preserves already set keys). - For each group from the global array
$cot_groups(if the group does not haveskiprightsset) creates a record:- Determines
$base_grp: if group ID >COT_GROUP_SUPERADMINS(5), usesCOT_GROUP_DEFAULT(0), otherwise the group ID itself. - Gets the symbolic masks from
$auth_permit[$base_grp]and$auth_lock[$base_grp]. - Converts them to numbers via
cot_auth_getvalue(). - Adds the record to the
$ins_array.
- Determines
- Inserts all records in one query via
Cot::$db->insert(Cot::$db->auth, $ins_array). - Calls
cot_auth_reorder()andcot_auth_clear('all').
Important: If a group is missing from $auth_permit, the value from $cot_auth_default_permit will be used. Same for locks. This ensures that all groups receive permissions.
5.3. cot_auth_remove_item() — removing object permissions
function cot_auth_remove_item($module_name, $item_id = null)Deletes all permission records for the specified object. If $item_id is not specified, all objects of the area are removed.
5.4. cot_auth_clear() — clearing permission cache
function cot_auth_clear($id = 'all')Resets the permission cache of users so that changes take effect. With 'all', clears the cache of all users and guests.
5.5. cot_auth_reorder() — sorting permission table
Executes SQL ALTER TABLE ... ORDER BY ... to physically sort records in the cot_auth table. This speeds up queries when there are many records.
5.6. cot_auth_add_group() — registering a new group
function cot_auth_add_group($group_id, $base_group_id = COT_GROUP_MEMBERS)Creates permission records for a new group by copying them from a base group. Used when dynamically creating groups.
5.7. cot_auth_remove_group() — removing a group from permissions
function cot_auth_remove_group($group_id)Deletes all permission records for the specified group.
6. Default Permissions and Their Values
6.1. $cot_auth_default_permit — standard allowances
In auth.php, a global array is defined:
$cot_auth_default_permit = [
COT_GROUP_DEFAULT => 'RW',
COT_GROUP_GUESTS => 'R',
COT_GROUP_INACTIVE => 'R',
COT_GROUP_BANNED => '0',
COT_GROUP_MEMBERS => 'RW',
COT_GROUP_SUPERADMINS => 'RW12345A'
];These values are used when a particular group is missing from the user-defined $auth_permit. Note: for COT_GROUP_BANNED it is '0' (no permissions), for superadmins — full set.
6.2. $cot_auth_default_lock — standard locks
$cot_auth_default_lock = [
COT_GROUP_DEFAULT => '0',
COT_GROUP_GUESTS => 'A',
COT_GROUP_INACTIVE => 'A',
COT_GROUP_BANNED => 'RW12345A',
COT_GROUP_MEMBERS => '0',
COT_GROUP_SUPERADMINS => 'RW12345A'
];By default, for guests and inactive users, the A permission is locked (cannot give them admin rights through the interface). For banned and superadmins, all permissions are locked — their rights are rigidly fixed.
7. Market Module Installer: Usage Example
The Market module, upon installation, creates many categories and assigns permissions for them. Let's examine the key fragments of its installer.
7.1. Category Array $categories
Categories are described by an array, each element contains:
'code'— unique category code (e.g.,'computers-components').'title'— name.'desc'— description.'path'— hierarchical path in the structure (e.g.,'001.001').
Example fragment:
$categories = [
[
'code' => 'computers-components',
'title' => 'Computers & Components',
'desc' => 'Desktop computers, components, and peripherals.',
'path' => '001',
],
// ...
];7.2. Arrays $authPermit and $authLock
The installer defines custom permissions for Market categories, overriding the standard ones:
$authPermit = [
COT_GROUP_DEFAULT => 'RW1',
COT_GROUP_GUESTS => 'R1',
COT_GROUP_INACTIVE => 'R',
COT_GROUP_BANNED => '',
COT_GROUP_MEMBERS => 'RW1',
COT_GROUP_SUPERADMINS => 'RW12345A',
COT_GROUP_MODERATORS => 'RW1A',
];
$authLock = [
COT_GROUP_DEFAULT => '0',
COT_GROUP_GUESTS => 'W2345A',
COT_GROUP_INACTIVE => 'W12345A',
COT_GROUP_BANNED => 'RW12345A',
COT_GROUP_MEMBERS => '0',
COT_GROUP_SUPERADMINS => 'RW12345A',
COT_GROUP_MODERATORS => '0',
];Note: for the COT_GROUP_MODERATORS (6) group, explicit values are set, although it is not present in the default arrays. This ensures correct permissions for moderators.
7.3. Installation Loop for Categories and Permissions
The installer sequentially creates each category using cot_structure_add(), and then, if the category is successfully created and $useDefaultAuth is false, adds permissions via cot_auth_add_item().
foreach ($categories as $cat) {
$result = cot_structure_add('market', [...], $useDefaultAuth);
if ($result === true && !$useDefaultAuth) {
cot_auth_add_item('market', $cat['code'], $authPermit, $authLock);
}
}Explanation: cot_structure_add() may return true on success, an array with an error (if the category already exists), or false. The check $result === true prevents duplicate permission addition when the installer is run again.
8. Analysis of Masks Used in Market
8.1. Permissions by Groups ($authPermit)
| Group | Mask | Numeric Value | Explanation |
|---|---|---|---|
COT_GROUP_DEFAULT | RW1 | 1+2+4 = 7 | Template: new groups get read, write, level 1 |
COT_GROUP_GUESTS | R1 | 1+4 = 5 | Guests can read and use level 1 (e.g., view contacts) |
COT_GROUP_INACTIVE | R | 1 | Read only |
COT_GROUP_BANNED | '' | 0 | No permissions |
COT_GROUP_MEMBERS | RW1 | 7 | Users: read, write, level 1 |
COT_GROUP_SUPERADMINS | RW12345A | 255 | Full access |
COT_GROUP_MODERATORS | RW1A | 1+2+4+128=135 | Read, write, level 1, administration |
8.2. Locks by Groups ($authLock)
| Group | Mask | Numeric Value | Explanation |
|---|---|---|---|
COT_GROUP_DEFAULT | 0 | 0 | Nothing locked |
COT_GROUP_GUESTS | W2345A | 2+8+16+32+64+128=250 | All rights locked except R and 1 |
COT_GROUP_INACTIVE | W12345A | 2+4+8+16+32+64+128=254 | All locked except R |
COT_GROUP_BANNED | RW12345A | 255 | All rights fixed |
COT_GROUP_MEMBERS | 0 | 0 | Nothing locked |
COT_GROUP_SUPERADMINS | RW12345A | 255 | Full lock (superadmin rights unchangeable) |
COT_GROUP_MODERATORS | 0 | 0 | Nothing locked |
Interpretation of locks:
A lock (auth_rights_lock) indicates which bits cannot be changed through the rights management interface. If a bit is set in the lock, the corresponding right is fixed and cannot be removed or added by a site administrator (except by direct database modification). For example, for guests all rights except R and 1 are locked, meaning the administrator cannot give guests W or A rights, but can change R and 1.
9. Common Mask Combinations and Their Meaning
Based on the bit system, many masks can be created. Here are frequently used combinations:
R— read only. Suitable for guest groups that are allowed to view.RW— read and write. Standard for registered users in most modules.R1— read + level 1. For example, guests can see the object and additional information available at level 1.RW1— read, write, and level 1. Typical set for participants who can add materials.RW12— read, write, levels 1 and 2. Extended rights, e.g., for trusted users.RW123— read, write, levels 1–3. Can be used for moderators without admin rights.RW1234— even wider rights.RW12345— almost full access, but without administration.RW12345A— full access, including administration.A— only administration (rarely used alone, usually with other rights).W— only write without read (illogical but possible).0or empty string — no permissions.
Combinations can be arbitrary; the main thing is that they make sense for a specific module. Levels 1–5 can be used by a module for action gradation.
10. Permission Locks (auth_rights_lock)
10.1. Why Locks Are Needed
Locks prevent accidental or malicious changes to critical permissions through the admin interface. For example, if the A right is locked for guests, the administrator cannot accidentally give guests administrative rights by simply checking a box. The lock fixes the bit, and changing that bit through standard means becomes impossible.
10.2. Examples of Locks and Their Effect
Consider the lock mask for guests in Market: W2345A. This means bits W, 2, 3, 4, 5, A are locked. The administrator can change only bits R and 1 (remove or grant those rights). But even if they give guests the W right, it will not work because Cotonti checks only auth_rights, and the lock does not affect the actual permission value, only the ability to edit it. Nevertheless, the lock ensures that after installation, rights are not accidentally expanded.
For banned and superadmins, the lock RW12345A means their rights are fully fixed and cannot be changed through the interface.
11. Practical Recommendations for Module Developers
- Use
cot_auth_add_item()to add permissions when creating new objects. This ensures that permissions are added for all groups with default values taken into account. - Explicitly set permissions for groups if you want to deviate from the defaults. In the Market example, permissions are set for all standard groups, including moderators.
- Lock critical rights to prevent their modification. Especially important to lock
Afor guests and inactive users, and fully fix superadmin and banned rights. - Remember default values: if you do not pass a group in
$auth_permit, it will receive rights from$cot_auth_default_permit. This can lead to unexpected results if you intended to give the group fewer rights. - Check permissions with
cot_auth()in module code. Do not directly accessCot::$usr['auth'], as the function ensures correct handling of the'any'option and logging. - Clear the permission cache after programmatic permission changes using
cot_auth_clear(). - Maintain uniqueness of the
(area, option)pair. If the object already exists, do not add permissions again unless necessary.
12. Frequently Asked Questions (FAQ)
Question: What do digits 1–5 in masks mean?
Answer: They are five additional permission levels that a module can use at its discretion. The core imposes no semantics on them.
Question: How does R differ from 1?
Answer: R is the standard read permission, checked by the core for displaying objects. 1 is an additional permission that can mean anything (e.g., "see hidden fields"). The module decides how to interpret it.
Question: Can a user be given a right that is locked?
Answer: Through the interface — no, but technically you can change auth_rights directly in the database. The lock (auth_rights_lock) only prohibits changes through standard means.
Question: What happens if a lock is not specified for a group?
Answer: The value from $cot_auth_default_lock will be used. If you do not want to lock rights, explicitly specify '0'.
Question: How does permission checking work if a user belongs to multiple groups?
Answer: When building the ACL (cot_auth_build()), rights from all user groups are combined using bitwise OR. Thus the user gets the maximum rights from all their groups.
Question: Why is the cot_auth table needed and why not store rights in groups?
Answer: Rights are tied not only to groups but also to specific objects (categories). Groups define the general access level, while the cot_auth table allows flexible per-category permission configuration.
13. Conclusion
The Cotonti permission system is a powerful and flexible mechanism based on bitmasks. Understanding symbolic notations (R, W, 1–5, A) and the ability to correctly form permission and lock arrays enables developers to create secure and customizable modules. The Market module example demonstrates how to assign permissions to categories upon their creation, following Cotonti best practices. We hope this guide has helped you understand the intricacies of working with access rights.
Comments (0)
Page Discussion in Telegram
Content author
Offline
Sodium Carbonate
Last logged: 2026-08-28 16:59
- Page published: 2026-08-28 16:41
- Last update: 2026-08-28 16:59
- Language:
Русский