Files
Unify/Permissions.md
2025-12-25 11:16:59 +01:00

7.8 KiB
Raw Permalink Blame History

Permissions: Groups, Scopes, Authorization Rules

In Unify, the permissions system is designed to give you full control over who can read, write, or delete data, while allowing you to enforce security at various levels of the application.

Unify offers a flexible, declarative approach to managing permissions across different objects, allowing you to define authorization rules based on:

  • User Groups (e.g., admin, guest, member)
  • Scopes (e.g., a column, a table, or an entire resource)
  • Actions (e.g., READ, WRITE, DELETE)

1. Groups: Defining User Roles

In Unify, Groups represent user roles, and each group has certain permissions associated with it. These groups can be used to control access at various levels, such as:

  • Viewing data
  • Editing data
  • Deleting data

Example: Defining Groups

import groups from '/user/group/user.group.permission.js';

export default class user extends table {

    username = new username();
    email    = new email();
    groups   = new groups();  // Associate user with groups

}

Common Use Case

For instance, you might have:

  • Visitor: Can only read public information
  • Member: Can read and write content
  • Admin: Can perform all actions, including delete

You can easily enforce these permissions using:

permission() {
    this.allow(groups.visitor, "READ");   // Visitors can read
    this.allow(groups.member, "READ");    // Members can read
    this.allow(groups.member, "WRITE");   // Members can write
    this.allow(groups.admin, "READ");     // Admins can read
    this.allow(groups.admin, "WRITE");    // Admins can write
    this.allow(groups.admin, "DELETE");   // Admins can delete
}

2. Scopes: Fine-Grained Permission Control

Scopes allow you to define permissions not just for entire tables or models, but for specific fields or columns within those models.

For example, a user might be allowed to read all the data but only edit a certain field, such as a profile description or a specific product attribute.

Example: Scope-Based Permissions

permission() {
    // Allow "member" group to read all users but only update email
    this.allow(groups.member, "READ");
    this.allow(groups.member, "WRITE", "email"); // Allow update on email only
    this.allow(groups.admin, "READ");
    this.allow(groups.admin, "WRITE");
    this.allow(groups.admin, "DELETE");
}

In this example:

  • Members can read all data but only update the email field.
  • Admins have full access (can read, write, and delete all fields).

You can also control views or actions like deleting based on a particular column or resource.


3. Authorization Rules: Controlling Access

Authorization rules are how you declare which groups have what types of access to your tables, fields, or methods. These rules govern what actions a group can perform on what objects.

You can define rules for specific actions such as:

  • READ: View data
  • WRITE: Modify data
  • DELETE: Remove data

Each object can implement an associated permission() method to specify which group can perform what action on the object.

Example: Implementing Authorization Rules

export default class news extends table {

    title = new title();
    body  = new body();

    permission() {
        this.allow(groups.visitor, "READ");   // Visitors can read the news
        this.allow(groups.member, "WRITE");   // Members can write/edit news
        this.allow(groups.admin, "READ");     // Admins can read
        this.allow(groups.admin, "WRITE");    // Admins can write
        this.allow(groups.admin, "DELETE");   // Admins can delete
    }
}

4. Implementing Permissions in node Methods

In addition to controlling UI elements like columns, you may want to enforce permissions when executing actions on the server (such as creating, updating, or deleting records). This is where manual permission checks come into play.

Example: Using Permissions in node Methods

node async deleteRow(client) {
    const user = client.user;

    // Check if the user has permission to delete
    if (!user.hasPermission("DELETE_NEWS")) {
        throw new Error("Permission denied: User cannot delete");
    }

    this.delete();  // Perform the delete action
}

In this example, the deleteRow method checks whether the current user has permission to delete the record. If they do not, it throws an error and prevents the action.


5. Permissions with Collections

Since collections may contain multiple rows (or objects), you may also need to control access at the collection level. This allows you to define permissions that apply across the entire dataset.

Example: Permissions for Collection

export default class newsListTableBody extends renderCollection, gridViewBody {

    permission() {
        this.allow(groups.visitor, "READ");   // Visitors can read all news in the collection
        this.allow(groups.member, "WRITE");   // Members can write new news items
        this.allow(groups.admin, "DELETE");   // Admins can delete news items
    }
}

This ensures that each row in the collection respects the users permissions when they try to view, create, update, or delete entries.


6. Combining Permissions with UI

In Unify, you don't have to manually manage every UI element (such as showing or hiding a button) based on permissions. You can use the permission() method to dynamically control UI elements' visibility and accessibility by the roles assigned.

For instance:

node async click() {
    if (this.user.hasPermission("EDIT_NEWS")) {
        this.showEditForm();
    } else {
        throw new Error("Permission denied: Cannot edit news");
    }
}

You can pair the permissions check with UI changes to ensure users can only interact with elements they are authorized to use.


7. Custom Authorization Logic

You can also write custom authorization logic, such as role hierarchies (where one group has broader permissions than another).

Example: Custom Authorization

node async deleteRow(client) {
    const user = client.user;

    // Only admin or user who created the news can delete it
    if (!(user.hasPermission("DELETE_NEWS") || this.createdBy === user.id)) {
        throw new Error("Permission denied: User cannot delete this news item");
    }

    this.delete(); // Proceed to delete if authorized
}

This custom rule checks whether the current user is an admin or the creator of the news item before allowing them to delete it.


Summary of Permissions System

Element Description
Groups Roles that define the access level of a user (e.g., Admin, Member, Visitor)
Scopes Granular permissions tied to specific fields, columns, or actions within a model
Authorization Rules Methods (permission()) where groups are assigned specific actions (READ, WRITE, DELETE) for objects, fields, or collections
Custom Logic Developers can write custom authorization logic for complex rules like ownership or hierarchical access

Unify's permission system is flexible, allowing developers to define, check, and enforce rules throughout the application — from UI visibility to backend operations like SQL queries and method executions.