---
term: 'Class-Level Permissions (CLPs) & Schema Security'
seoTitle: 'What are Class-Level Permissions (CLPs)? Schema Security'
headline: 'What are Class-Level Permissions (CLPs)?'
slug: class-level-permissions-clp
category: database
shortDefinition: 'A class-level permission is a rule on a class schema that controls which users or roles may run each operation on that class at all.'
relatedTerms:
  - access-control-lists-acl
  - row-level-security
  - role-based-access-control-rbac
  - data-layer-vs-application-layer-security
  - visual-database-management
contrastsWith:
  - access-control-lists-acl
aboutTerms:
  - 'Class-Level Permissions (CLPs)'
  - 'Schema Security'
faq:
  - question: 'What are class-level permissions?'
    answer: 'Rules attached to a class (table/collection) schema that control which users or roles may perform each operation — find, get, count, create, update, delete, add field — on any object in that class. They are the coarsest access gate and the first one checked: if the class-level rule denies an operation, no per-object permission is ever consulted.'
  - question: 'What is the difference between CLPs and ACLs?'
    answer: 'Scope and order. A CLP answers "who may touch this class at all" — one rule for the whole table. An ACL answers "who may touch this specific row" — data carried by each object. A request must pass both gates: the class gate first, then the object gate, and either can deny. Broad strokes at the class level, fine grain at the object level.'
  - question: 'What is requiresAuthentication?'
    answer: 'The middle setting between public and enumerated roles: it restricts an operation to any logged-in user with a valid session, without naming specific users or roles. It is the right default for most app data — anonymous requests are rejected, while every authenticated user passes the class gate and proceeds to per-object ACL checks.'
  - question: 'What are pointer permissions?'
    answer: 'Class-level rules keyed to a user-pointer field on the object — for example "only the user in the owner field may read or write." They act as a virtual ACL: enforcement is per-object, but the rule is declared once on the schema instead of stored on every row. They intersect with real ACLs; both must allow the action.'
  - question: 'What are protected fields?'
    answer: 'Field-level rules layered on the class gate: specific columns — an email address, an internal score — hidden from some requesters while the rest of the object stays readable. They turn the permission ladder into three rungs on one schema: class-wide operations, per-object access, and per-field visibility.'
  - question: 'What is the SQL equivalent of class-level permissions?'
    answer: 'Table and schema privileges: GRANT SELECT, INSERT, UPDATE, DELETE ON a table to a role, REVOKE to remove, plus schema-level USAGE and CREATE rights. The concept maps one-to-one — a table-level GRANT is a class-level permission with different syntax — and document databases mirror it with roles scoped to collection-level actions.'
  - question: 'Should clients be allowed to add fields or create classes in production?'
    answer: 'No — this is the consensus hardening step. Schema flexibility is a development convenience; in production, disable the add-field permission on every class and turn off client class creation entirely, freezing the schema against clients. Schema changes then flow through the dashboard or server-side code, where they belong.'
  - question: 'Are class-level permissions enough to secure an app?'
    answer: 'They are the first layer, not the whole defense. The standard stack: CLPs gate operations per class, ACLs or pointer permissions scope rows, protected fields hide sensitive columns, and server-side triggers validate writes. And one rule above all: the master key — which bypasses every gate — never ships in client code.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'Backend security guide'
    url: 'https://docs.parseplatform.org/parse-server/guide/#security'
  - name: 'PostgreSQL GRANT reference'
    url: 'https://www.postgresql.org/docs/current/sql-grant.html'
  - name: 'MongoDB collection-level access control'
    url: 'https://www.mongodb.com/docs/manual/core/collection-level-access-control/'
  - name: 'Back4app app security guidelines'
    url: 'https://www.back4app.com/docs/security/parse-security'
cta:
  title: 'Schema security with toggles, not policies'
  text: 'On Back4app, class-level permissions are checkboxes in the dashboard: lock a class, require authentication, scope operations to roles, protect fields — enforced server-side on every request, layered with per-object ACLs underneath.'
  linkText: 'Start Free'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-07-25'
translationKey: class-level-permissions-clp
---

**A class-level permission is a rule on a class schema that controls which users or roles may run each operation on that class at all.** It's the outermost gate of data-layer security: before any per-row check happens, the class itself decides whether this caller may find, create, update, or delete here — one rule, whole table, checked first.

## Key takeaways

| Question | Answer |
| --- | --- |
| What it is | Per-operation access rules on the class (table) itself |
| vs. ACLs | CLP gates the class; ACLs gate each row — requests must pass both |
| The operations | Find, get, count, create, update, delete, add field — each set separately |
| The golden defaults | Require authentication, lock add-field, never ship the master key |
| SQL cousin | `GRANT`/`REVOKE` on tables and schemas — same idea, different syntax |

## The class gate, declared and felt

A CLP is schema configuration — here as the REST payload that locks a `Config` class to public-read, server-only-write:

```text
PUT /schemas/Config
{
  "classLevelPermissions": {
    "find":   { "*": true },              // anyone may query
    "get":    { "*": true },
    "create": {},                         // nobody from the client side
    "update": { "role:admin": true },     // admins only
    "delete": {},
    "addField": {}                        // schema frozen
  }
}
```

What clients experience is the gate doing its job — reads flow, writes die at the class boundary before any data is touched:

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// The class gate in action: Config is read-only for clients (CLP),
// so reads succeed and writes never reach the data
const config = await new Parse.Query('Config').first(); // ✓ public read

const c = new Parse.Object('Config');
c.set('flag', true);
try {
  await c.save(); // ✗ CLP blocks client writes to this class entirely
} catch (e) {
  console.log(e.code); // 119: operation forbidden by class-level permissions
}
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// The class gate in action: reads allowed, client writes blocked by CLP
final config = ParseObject('Config')..set('flag', true);
final response = await config.save();
if (!response.success) {
  print(response.error?.code); // 119: forbidden by class-level permissions
}
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// The class gate in action: reads allowed, client writes blocked by CLP
var config = Config()
config.flag = true
config.save { result in
  if case .failure(let error) = result {
    print(error) // operation forbidden by class-level permissions
  }
}
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// The class gate in action: reads allowed, client writes blocked by CLP
val config = ParseObject("Config").apply { put("flag", true) }
config.saveInBackground { e ->
  if (e != null) {
    Log.d("CLP", "blocked: ${e.code}") // forbidden by class-level permissions
  }
}
```

## How Class-Level Permissions and ACLs Work Together

```mermaid
flowchart LR
  accTitle: How class-level permissions and ACLs combine
  accDescr: Every request first passes the class-level permission gate for its operation; only if the class allows it is the per-object ACL consulted, and either gate can deny the request.
  R["Request:<br/>update object X in class C"] --> G1{"Class gate (CLP):<br/>may this caller<br/>update class C?"}
  G1 -- no --> D["Denied — 119"]
  G1 -- yes --> G2{"Object gate (ACL):<br/>may this caller<br/>write object X?"}
  G2 -- no --> D2["Denied — object hidden"]
  G2 -- yes --> A["Allowed"]
```

## The granularity ladder: CLP vs. ACL vs. pointer permissions vs. protected fields

| Mechanism | Scope | Declared where | Checked | Typical job |
| --- | --- | --- | --- | --- |
| Class-level permission | Whole class, per operation | Schema | First | "Clients never write `Config`" |
| Pointer permission | Per object, via a schema rule | Schema (keyed to a field) | Second | "Only the `owner` touches their rows" |
| ACL | Per object | On each row, as data | Second | "This note: its author only" |
| Protected fields | Per column | Schema | On read | "Hide `email` from other users" |

The design idiom that falls out: **broad strokes on the schema, fine grain on the rows.** Classes whose access rule is uniform (config, catalogs, logs) need only the class gate; classes with per-user data add ACLs or pointer permissions beneath it.

## The same gate in other engines

```sql
-- PostgreSQL: table-level privileges are CLPs by another name
GRANT SELECT ON films TO PUBLIC;
GRANT INSERT, UPDATE ON films TO role_editor;
REVOKE ALL ON launch_codes FROM PUBLIC;
```

Document databases mirror it with [roles scoped to collection-level actions](https://www.mongodb.com/docs/manual/core/collection-level-access-control/) — a role granted find-and-insert on exactly one collection. The concept is universal; what differs is ergonomics: SQL grants and role documents are administered in code, while BaaS platforms surface the same matrix as dashboard toggles.

## Recommended settings by content type

| Content type | Find/Get | Create | Update/Delete | Add field |
| --- | --- | --- | --- | --- |
| Public content (catalog, posts) | Public | Roles/server | Roles/server | Off |
| User-owned data (notes, orders) | Authenticated + ACLs | Authenticated | ACL-scoped | Off |
| Config and flags | Public or authenticated | Nobody (server only) | Admin role | Off |
| Logs and analytics | Nobody (server only) | Authenticated (write-only) | Nobody | Off |
| Admin-only data | Admin role | Admin role | Admin role | Off |

That table is the checklist most launches actually need — note the constant in the last column, and its sibling rule: client-side class creation off in production, so the schema only changes on purpose.

## Common use cases

- **Freezing production schemas.** Add-field off everywhere; classes stop appearing and mutating from client traffic.
- **Read-only reference data.** Catalogs and settings publicly readable, writable only by server code — the `Config` pattern above.
- **Write-only inboxes.** Feedback and telemetry classes clients may create into but never read — the inverse gate app-layer code often forgets.
- **Role-tiered admin surfaces.** Update and delete reserved to an admin role while the app reads freely.
- **Defense against the classic dump.** The infamous one-liner — a curl with an app ID querying an open `User` class — dies at the find gate when the class requires authentication.

## Should the class gate or server code enforce it? A decision matrix

| Enforce with CLPs (+ ACLs) when… | Route through server code when… |
| --- | --- |
| The rule is "who may do what, where" | The rule needs business logic ("only before the order ships") |
| It's uniform per class or per owner | It spans multiple objects or classes |
| You want it enforced on every path, including new clients | You want one audited chokepoint for a sensitive workflow |
| Declarative beats imperative for review | Validation, enrichment, or side effects ride along |
| The dashboard toggle is the whole spec | The spec is a paragraph of conditions |

They compose: the hardened pattern for genuinely sensitive classes locks the CLPs entirely and exposes server-side functions as the only doorway — the class gate guarantees nobody walks around the chokepoint.

## Limitations and trade-offs

- **Class granularity is coarse by definition.** Anything per-row belongs to ACLs and pointer permissions; anything conditional belongs in server-side validation — CLPs gate, they don't reason.
- **Defaults are permissive for development.** New classes are born open so prototypes fly; the pre-launch pass that flips the table above is a discipline, not an automatism.
- **The master key ignores everything.** Every gate in this article is void wherever that key travels — which is why it lives server-side only, used per-operation, never shipped.
- **Enforcement surface must be complete.** Real-time subscriptions and special endpoints have historically had enforcement gaps — keep the platform updated, and test the gates from a real client, not just the dashboard.
- **Toggles need review too.** Declarative security is auditable security only if someone audits it — the settings matrix belongs in your launch checklist, not in tribal memory.

## CLPs on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. Class-level permissions are its schema-security surface made visual: every class in the dashboard carries the operations matrix as checkboxes — public, requires-authentication, per-role — plus pointer permissions and protected fields, [enforced server-side on every request](https://docs.parseplatform.org/parse-server/guide/#security) across SDKs, REST, and GraphQL alike. Underneath sit per-object ACLs; above sit Cloud Code triggers for the rules that need logic. The [security guidelines](https://www.back4app.com/docs/security/parse-security) walk the full pre-launch pass — the settings matrix in this article, applied click by click.
