# **Rab Access Control Middleware**

A robust, flexible, and declarative access control library for Node.js applications. With `rab-access`, you can define and enforce fine-grained access policies using schemas, roles, and custom validations. It’s ideal for multi-role systems requiring granular control over resource access.

---

## **Features**
- **Declarative Schema-Based Access Control**: Define access policies for resources and operations.
- **Role-Based Permissions**: Grant permissions based on roles and extend roles with additional access rights.
- **Dynamic Filters and Validators**: Use dynamic data (e.g., user attributes, query parameters) to filter or validate access.
- **Middleware Generation**: Seamlessly integrate with your Node.js apps via middleware.

---

## **Installation**
Install the package using npm:
```bash
npm install rab-access
```

---

## **Getting Started**

### **1. Import Required Modules**
```javascript
const { Rab } = require("rab-access");
```

### **2. Define Roles**
Define the roles within your system:
```javascript
const roles = {
  SYSTEM_ADMIN: "System Admin",
  ADMIN: "Admin",
  SUPERVISOR: "Supivisor",
};
```

### **3. Define Access Control Schema**
Create a schema that specifies access rules for various operations:
```javascript
const userAccessControl = Rab.schema({
  canReadUser: [
    Rab.alwaysGrant().ifEqual(Rab.user("user_id"), Rab.params("user_id")),
    Rab.grant(roles.SYSTEM_ADMIN).columns(["id"]),
    Rab.grant(roles.ADMIN).filters({
        "organization_id": Rab.user( "organization_id")
      })
      .columns(["id"]), 
    Rab.grant(roles.SUPERVISOR)
      .extend(roles.ADMIN, "canReadUser")
      .columns(["id", "name"]),
  ],
  canReadAllUsers: [Rab.grant(roles.SYSTEM_ADMIN).columns(["id"])],
  canDeleteUser: [
    Rab.alwaysGrant().ifEqual(Rab.user("user_id"), Rab.params("user_id")), 
    Rab.grant(roles.SYSTEM_ADMIN),
    Rab.grant(roles.SUPERVISOR).validator("customValidationFunction"),
  ],
});
```

### **4. Define Custom Validations**
Add custom validation logic for specific scenarios:
```javascript
const customValidations = {
  customValidationFunction: async (request) => {
    // Perform your custom validation
    return Promise.resolve();
  },
};
```

### **5. Generate Middleware**
Create middleware to enforce your access policies:
```javascript
const createMiddleware =
    (accessControl, validations) => (permission) => async (req, res, next) => {
        const { isAuthorized, fields, filters, lookupFilters } =
            await accessControl.can({
                validations,
                request: req,
                role: req.role,
                permission,
            });

        if (isAuthorized) {
            req.fields = fields;
            req.filters = filters;
            req.lookupFilters = lookupFilters;
            return next();
        }
        res.status(403).json({ error: "FORBIDDEN" });
    };

const accessMiddleware = createMiddleware(userAccessControl, customValidations);
```

### **6. Export and Use**
Export the middleware and roles for use in your application:
```javascript
module.exports = {
  accessMiddleware,
  roles,
};
```

---

## **Usage Example**
Integrate the middleware into your application to secure routes:
```javascript
const express = require("express");
const { accessMiddleware, roles } = require("./accessControl");

const app = express();

// Example route using middleware
app.get("/users/:userId", accessMiddleware("canReadUser"), (req, res) => {
  res.send("Access granted to a user data!");
});

app.get("/users", accessMiddleware("canReadAllUsers"), (req, res) => {
    res.send("Access granted to a users!");
});

app.listen(3000, () => console.log("Server running on port 3000"));
```


## **API Reference**

### **Core Classes**

#### **`Rab`**

The main class for managing access control. Use it to define schemas, manage roles, and evaluate permissions.

##### **Methods**

- **`Rab.schema(config: Record<string, Permission[]>)`**
  Defines the access control schema with a configuration object mapping actions to role-based permissions.
    - **Parameters**:
        - `config`: An object where keys are permission names and values are arrays of `Permission` instances.
    - **Example**:
      ```javascript
      Rab.schema({
        canReadUsers: [
          Rab.grant("Admin").columns(["id", "name"]),
        ],
      });
      ```

- **`Rab.grant(role: string): Permission`**
  Creates a new `Permission` instance for a specific role.

- **`Rab.user(path: string[] | string): string[]`**
  Retrieves the user's data path for dynamic access control rules.

- **`Rab.params(path: string[] | string): string[]`**
  Retrieves the request parameters path for dynamic access control.

- **`Rab.query(path: string[] | string): string[]`**
  Retrieves the query parameters path for dynamic access control.
- **`Rab.can(options: Omit<EvaluatePermissionParams, "permissions"> & { permission: string }): Promise<Authorization>`**
  Checks if a user is authorized to perform a specific action.
    - **Parameters**:
        - `options`: Includes the role, request, and permission name.
    - **Returns**: A promise resolving to an `Authorization` object.
    - **Example**:
      ```javascript
      const {isAuthorized} = await Rab.can({
        role: "Admin",
        request: req,
        permission: "canReadUsers",
      });
      ```

---

#### **`Permission`**

Defines the rules and constraints for a specific role's access.

##### **Methods**

- **`columns(columns: string[]): this`**
  Specifies the columns that the role can access.

- **`lookupFilters(filters: Record<string, any>): this`**
  Adds lookup filters to the permission.
- **`filters(filters: Record<string, any>): this`**
  Adds filters to refine the permission further.
- **`ifEqual(...fields: string[][]): this`**
  Adds an equality condition for the permission.
    - **Parameters**:
        - `fields`: Arrays of field pairs to compare for equality.

- **`ifContains(...fields: string[][]): this`**
  Adds a containment condition for the permission.
    - **Parameters**:
        - `fields`: Arrays of field pairs to check for containment.

- **`validator(method: string, variables?: Record<string, any>): this`**
  Adds a custom validator function to the permission.
    - **Parameters**:
        - `method`: The name of the custom validation method.
        - `variables`: Optional key-value map of variables.

- **`extend(role: string, permission: string): this`**
  Extends another permission for the current role.
    - **Parameters**:
        - `role`: The base role.
        - `permission`: The name of the permission to extend.

    

## **Contributing**
Contributions are welcome! Feel free to submit issues or pull requests to improve this library.

---

## **License**
This project is licensed under the [MIT License](LICENSE).

---