Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/developers/operations-api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ The operations API reference is available below and categorized by topic:
- [Configuration](operations-api/configuration)
- [Certificate Management](operations-api/certificate-management)
- [Token Authentication](operations-api/token-authentication)
- [Impersonation](../security/impersonation)
- [SQL Operations](operations-api/sql-operations)
- [Advanced JSON SQL Examples](operations-api/advanced-json-sql-examples)
- [Analytics](operations-api/analytics)
Expand Down
17 changes: 17 additions & 0 deletions docs/developers/replication/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,23 @@ replication:

When using controlled flow replication, you will typically have different route configurations for each node to every other node. In that case, typically you do want to ensure that you are _not_ replicating the `system` database, since the `system` database containes the node configurations, and replicating the `system` database will cause all nodes to be replicated and have identical route configurations.

The `replicates` property also allows you to specify routes with more granularity by specifying `sendsTo` and/or `receivesFrom` properties, which each can have an array with node and database names.

```yaml
replication:
databases:
- data
routes:
- host: node-two
replicates:
sendsTo:
- target: node-three
database: data
receivesFrom:
- source: node-four
database: system
```

#### Explicit Subscriptions

By default, Harper automatically handles connections and subscriptions between nodes, ensuring data consistency across your cluster. It even uses data routing to manage node failures. However, you can manage these connections manually by explicitly subscribing to nodes. This should _not_ be used for production replication and should be avoided and exists only for testing, debugging, and legacy migration. This will likely be removed in V5. If you choose to manage subscriptions manually, Harper will no longer handle data consistency for you. This means there’s no guarantee that all nodes will have consistent data if subscriptions don’t fully replicate in all directions. If a node goes down, it’s possible that some data wasn’t replicated before the failure. If you want single direction replication, you can use controlled replication flow described above.
Expand Down
154 changes: 154 additions & 0 deletions docs/developers/security/impersonation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
---
title: Impersonation
---

# Impersonation

Impersonation allows a `super_user` to execute operations API requests as if they were a different, less-privileged user. This is useful for testing permissions, debugging access issues, and building admin tools that preview what a given user or role can see and do — all without needing that user's credentials.

## How It Works

Add an `impersonate` property to any operations API request body. Harper will authenticate the request normally (the caller must be a `super_user`), then **downgrade** the effective permissions for that request to match the impersonated identity.

```http
POST https://my-harperdb-server:9925/
Authorization: Basic <super_user credentials>
Content-Type: application/json

{
"operation": "search_by_hash",
"database": "dev",
"table": "dog",
"hash_values": ["1"],
"impersonate": {
"username": "test_user"
}
}
```

The request above runs the `search_by_hash` as if `test_user` had made it — subject to that user's role permissions.

## Security Constraints

- **Super user only** — only users with `super_user` permissions can use `impersonate`. All other users receive a `403` error.
- **Downgrade only** — impersonation can never escalate privileges. The `super_user` and `cluster_user` flags are always forced to `false` on the impersonated identity.
- **Audit trail** — every impersonated request is logged, recording who initiated the impersonation and which identity was assumed.

## Impersonation Modes

There are three ways to specify the impersonated identity, depending on what you want to test.

### Impersonate an Existing User

Provide a `username` to run the request with that user's current role and permissions.

```json
{
"operation": "search_by_hash",
"database": "dev",
"table": "dog",
"hash_values": ["1"],
"impersonate": {
"username": "test_user"
}
}
```

The target user must exist and be active. If the user is not found, a `404` error is returned. If the user is inactive, a `403` error is returned.

### Impersonate an Existing Role

Provide a `role_name` to run the request with that role's permissions. You can optionally include a `username` to set the effective username (defaults to the caller's username).

```json
{
"operation": "search_by_value",
"database": "dev",
"table": "dog",
"search_attribute": "name",
"search_value": "Penny",
"impersonate": {
"role_name": "developer"
}
}
```

The role must exist. If the role is not found, a `404` error is returned.

### Impersonate with Inline Permissions

Provide a `role` object with a `permission` property to test with an ad-hoc set of permissions. This is useful for previewing the effect of a role you haven't created yet.

```json
{
"operation": "sql",
"sql": "SELECT * FROM dev.dog",
"impersonate": {
"username": "preview_user",
"role": {
"permission": {
"dev": {
"tables": {
"dog": {
"read": true,
"insert": false,
"update": false,
"delete": false,
"attribute_permissions": []
}
}
}
}
}
}
}
```

The `username` field is optional and defaults to the caller's username. The `permission` object follows the same structure as [role permissions](users-and-roles#role-permissions).

You can also restrict the impersonated identity to a specific set of operations API calls using the `operations` field inside `permission`:

```json
{
"operation": "search_by_hash",
"database": "dev",
"table": "dog",
"hash_values": ["1"],
"impersonate": {
"role": {
"permission": {
"operations": ["read_only"],
"dev": {
"tables": {
"dog": {
"read": true,
"insert": false,
"update": false,
"delete": false,
"attribute_permissions": []
}
}
}
}
}
}
}
```

## Impersonate Payload Reference

| Field | Type | Description |
|---|---|---|
| `username` | string | Target username. Required for existing-user mode. Optional for role-based modes (defaults to the caller's username). |
| `role_name` | string | Name of an existing role to assume. |
| `role` | object | Inline role definition. Must include a `permission` object. |
| `role.permission` | object | Permission object following the standard [role permissions](users-and-roles#role-permissions) structure. |

Exactly one of `username` (alone), `role_name`, or `role` must be provided. If `role` is present, it takes precedence.

## Use Cases

- **Admin dashboards** — preview what a user sees without switching accounts.
- **Permission testing** — verify that a role grants (or denies) the expected access before assigning it to users.
- **Debugging** — reproduce access issues reported by a user by impersonating them directly.
- **CI/CD** — automated tests can verify permission configurations by impersonating different roles against a single `super_user` credential.
1 change: 1 addition & 0 deletions docs/developers/security/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ Harper uses role-based, attribute-level security to ensure that users can only g

- [Configuration](security/configuration) - Security configuration and settings
- [Users and Roles](security/users-and-roles) - Role-based access control and permissions
- [Impersonation](security/impersonation) - Execute operations as a different user or role
105 changes: 88 additions & 17 deletions docs/developers/security/users-and-roles.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Users & Roles

# Users & Roles

Harper utilizes a Role-Based Access Control (RBAC) framework to manage access to Harper instances. A user is assigned a role that determines the users permissions to access database resources and run core operations.
Harper utilizes a Role-Based Access Control (RBAC) framework to manage access to Harper instances. A user is assigned a role that determines the user's permissions to access database resources and run core operations.

## Roles in Harper

Expand All @@ -13,9 +13,9 @@ Role permissions in Harper are broken into two categories – permissions around
**Database Manipulation**: A role defines CRUD (create, read, update, delete) permissions against database resources (i.e. data) in a Harper instance.

1. At the table-level access, permissions must be explicitly defined when adding or altering a role – _i.e. Harper will assume CRUD access to be FALSE if not explicitly provided in the permissions JSON passed to the `add_role` and/or `alter_role` API operations._
1. At the attribute-level, permissions for attributes in all tables included in the permissions set will be assigned based on either the specific attribute-level permissions defined in the tables permission set or, if there are no attribute-level permissions defined, permissions will be based on the tables CRUD set.
1. At the attribute-level, permissions for attributes in all tables included in the permissions set will be assigned based on either the specific attribute-level permissions defined in the table's permission set or, if there are no attribute-level permissions defined, permissions will be based on the table's CRUD set.

**Database Definition**: Permissions related to managing databases, tables, roles, users, and other system settings and operations are restricted to the built-in `super_user` role.
**Database Definition**: Permissions related to managing databases, tables, roles, users, and other system settings and operations are restricted to the built-in `super_user` role by default. Specific operations can be selectively granted to non-super_user roles using [`operations`](#roles-in-harper).

**Built-In Roles**

Expand Down Expand Up @@ -45,7 +45,7 @@ When creating a new, user-defined role in a Harper instance, you must provide a

- `permissions` used to explicitly define CRUD access to existing table data.

Example JSON for `add_role` request
### Example JSON for `add_role` request {#add-role-example}

```json
{
Expand Down Expand Up @@ -84,15 +84,86 @@ Example JSON for `add_role` request

**Setting Role Permissions**

There are two parts to a permissions set:
There are three parts to a permissions set:

- `super_user` – boolean value indicating if role should be provided super_user access.

_If `super_user` is set to true, there should be no additional database-specific permissions values included since the role will have access to the entire database schema. If permissions are included in the body of the operation, they will be stored within Harper, but ignored, as super_users have full access to the database._

- `permissions`: Database tables that a role should have specific CRUD access to should be included in the final, database-specific `permissions` JSON.
- `operations` – array of operation names and/or permission group names that this role is allowed to call. When set, it acts as a two-gate check: (1) only listed operations are reachable — any unlisted operation is denied regardless of table CRUD permissions; (2) for data operations that pass gate one, table-level CRUD permissions still apply as normal. Operations that are normally restricted to `super_user` can be selectively granted by including them in the list.

_For user-defined roles (i.e. non-super_user roles, blank permissions will result in the user being restricted from accessing any of the database schema._
_If `operations` is not set, existing behavior is unchanged — the role can call any non-super_user operation, subject to table CRUD permissions._

**Permission Groups**

Groups expand to a predefined set of operations and can be mixed with individual operation names:

- `read_only` – search, SQL SELECT, describe, and monitoring operations. No data modification. Operations: `search`, `search_by_conditions`, `search_by_hash`, `search_by_id`, `search_by_value`, `sql`, `describe_all`, `describe_schema`, `describe_database`, `describe_table`, `user_info`, `get_job`, `get_analytics`, `list_metrics`, `describe_metric`

- `standard_user` – everything in `read_only` plus full data manipulation and bulk load. Does not include any `super_user`-restricted operations, schema DDL (`create_attribute`), or token management. Additional operations beyond `read_only`: `insert`, `update`, `upsert`, `delete`, `csv_data_load`, `csv_file_load`, `csv_url_load`, `import_from_s3`

**Example: read-only role**

A role that can only search and describe — cannot insert, update, delete, or call any admin operations:

```json
{
"operation": "add_role",
"role": "read_only_analyst",
"permission": {
"operations": ["read_only"],
"orders_db": {
"tables": {
"orders": {
"read": true,
"insert": false,
"update": false,
"delete": false,
"attribute_permissions": []
}
}
}
}
}
```

**Example: full data access + targeted admin operations**

A role with all normally available data operations, plus the ability to call `get_configuration` and `system_information` without being a super_user. The `standard_user` group opens the operation gate for all non-SU data ops; table CRUD permissions then govern what the role can actually do in each table:

```json
{
"operation": "add_role",
"role": "ops_engineer",
"permission": {
"operations": ["standard_user", "get_configuration", "system_information"],
"orders_db": {
"tables": {
"orders": {
"read": true,
"insert": true,
"update": true,
"delete": true,
"attribute_permissions": []
},
"audit_log": {
"read": true,
"insert": false,
"update": false,
"delete": false,
"attribute_permissions": []
}
}
}
}
}
```

_This role can insert/update/delete `orders` (both gates pass), only read `audit_log` (operation gate passes, CRUD gate blocks writes), call `get_configuration` and `system_information` (super_user bypass), and cannot call `restart`, `drop_database`, or any other unlisted operation._

- `permission`: Database tables that a role should have specific CRUD access to should be included in the final, database-specific `permission` JSON.

_For user-defined roles (i.e. non-super_user roles), blank permissions will result in the user being restricted from accessing any of the database schema._

**Table Permissions JSON**

Expand Down Expand Up @@ -120,33 +191,33 @@ Each table that a role should be given some level of CRUD permissions to must be
**Important Notes About Table Permissions**

1. If a database and/or any of its tables are not included in the permissions JSON, the role will not have any CRUD access to the database and/or tables.
1. If a table-level CRUD permission is set to false, any attribute-level with that same CRUD permission set to true will return an error.
2. If a table-level CRUD permission is set to false, any attribute-level with that same CRUD permission set to true will return an error.

**Important Notes About Attribute Permissions**

1. If there are attribute-specific CRUD permissions that need to be enforced on a table, those need to be explicitly described in the `attribute_permissions` array.
1. If a non-hash attribute is given some level of CRUD access, that same access will be assigned to the tables `hash_attribute` (also referred to as the `primary_key`), even if it is not explicitly defined in the permissions JSON.
2. If a non-hash attribute is given some level of CRUD access, that same access will be assigned to the table's `hash_attribute` (also referred to as the `primary_key`), even if it is not explicitly defined in the permissions JSON.

_See table_name1s permission set for an example of this – even though the tables hash attribute is not specifically defined in the attribute_permissions array, because the role has CRUD access to attribute1, the role will have the same access to the tables hash attribute._
_See [`table_name1`'s permission set](#add-role-example) for an example of this – even though the table's hash attribute is not specifically defined in the attribute_permissions array, because the role has CRUD access to 'attribute1', the role will have the same access to the table's hash attribute._

1. If attribute-level permissions are set – _i.e. attribute_permissions.length > 0_ – any table attribute not explicitly included will be assumed to have not CRUD access (with the exception of the `hash_attribute` described in #2).
3. If attribute-level permissions are set – _i.e. attribute_permissions.length > 0_ – any table attribute not explicitly included will be assumed to have not CRUD access (with the exception of the `hash_attribute` described in #2).

_See table_name1s permission set for an example of this – in this scenario, the role will have the ability to create, insert and update attribute1 and the tables hash attribute but no other attributes on that table._
_See [`table_name1`'s permission set](#add-role-example) for an example of this – in this scenario, the role will have the ability to create, insert and update 'attribute1' and the table's hash attribute but no other attributes on that table._

1. If an `attribute_permissions` array is empty, the roles access to a tables attributes will be based on the table-level CRUD permissions.
4. If an `attribute_permissions` array is empty, the role's access to a table's attributes will be based on the table-level CRUD permissions.

_See table_name2s permission set for an example of this._
_See [`table_name2`'s permission set](#add-role-example) for an example of this._

1. The `__createdtime__` and `__updatedtime__` attributes that Harper manages internally can have read perms set but, if set, all other attribute-level permissions will be ignored.
1. Please note that DELETE permissions are not included as a part of an individual attribute-level permission set. That is because it is not possible to delete individual attributes from a row, rows must be deleted in full.
5. The `__createdtime__` and `__updatedtime__` attributes that Harper manages internally can have read perms set but, if set, all other attribute-level permissions will be ignored.
6. Please note that DELETE permissions are not included as a part of an individual attribute-level permission set. That is because it is not possible to delete individual attributes from a row, rows must be deleted in full.
- If a role needs the ability to delete rows from a table, that permission should be set on the table-level.
- The practical approach to deleting an individual attribute of a row would be to set that attribute to null via an update statement.

## Role-Based Operation Restrictions

The table below includes all API operations available in Harper and indicates whether or not the operation is restricted to super_user roles.

_Keep in mind that non-super_user roles will also be restricted within the operations they do have access to by the database-level CRUD permissions set for the roles._
_Keep in mind that non-super_user roles will also be restricted within the operations they do have access to by the database-level CRUD permissions set for the roles. Operations marked with X can be selectively granted to non-super_user roles using `operations`._

| Databases and Tables | Restricted to Super_Users |
| -------------------- | :-----------------------: |
Expand Down
Loading
Loading