You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
## [Unreleased]
9
9
10
10
### Added
11
-
- **`apps/objectos` honors `OS_DATABASE_URL` / `OS_DATABASE_DRIVER` for the project DB** — `packages/services/service-cloud/src/runtime-stack.ts` (single-project local mode) now selects the project data driver from the same env vars that the CLI's `objectstack serve` uses, with URL-scheme inference (`mongodb://` → mongodb, `postgres://` → pg, `mysql://` → mysql, `libsql://` / `https://*.turso.*` → turso, `file:` / `*.db` / `*.sqlite` / `:memory:` → sqlite). To pin the **control-plane** DB independently from the project DB, a new `OS_CONTROL_DATABASE_URL` env var is introduced and resolved in `cloud-stack.ts`; for backward compatibility the legacy `OS_DATABASE_URL` is still accepted as a fallback for the control DB when neither `OS_CONTROL_DATABASE_URL` nor an explicit `controlDriverUrl` is supplied. Verified end-to-end: `OS_DATABASE_URL=mongodb://localhost:27017/objectstack_objectos_test pnpm dev` boots `apps/objectos`, control DB stays on local sqlite (`apps/objectos/.objectstack/data/control.db`), and project records (CRUD on `/api/v1/data/...`) land in MongoDB.
11
+
- **Phase-1 RBAC end-to-end enforcement (multi-tenant isolation)** — Every authenticated REST request now arrives at the SecurityPlugin middleware with a populated `ExecutionContext`, so RLS/FLS/CRUD checks actually fire. Three previously-silent context-drop sites were closed: (1) `@objectstack/objectql` `protocol.{find,get,create,update,delete}Data` now forward `request.context` into the engine call options; (2) `@objectstack/rest` `RestServer` gained `resolveExecCtx()` plus an `authServiceProvider` constructor hook (wired in `RestApiPlugin` from `ctx.getService('auth')`) that resolves the better-auth session for both single-kernel and multi-kernel deployments and threads `context` into all five CRUD handlers; (3) `@objectstack/plugin-hono-server` raw `/data/:object` fallback handlers now resolve the same context inline and map `PermissionDeniedError` → HTTP 403. `@objectstack/runtime` `resolveExecutionContext()` wraps plain header objects as Web `Headers` so better-auth's cookie lookup works. New seed link tables `sys_user_permission_set` / `sys_role_permission_set` (in `@objectstack/platform-objects`) plus default permission sets `admin_full_access` / `member_default` / `viewer_readonly`; `member_default` carries a wildcard `object: '*'` RLS policy (`tenant_id = current_user.tenant_id`) that SecurityPlugin rewrites onto the configured `tenantField` (default `organization_id`) and skips for tables that lack the field. Two further fixes landed alongside the wiring work: (a) `@objectstack/objectql` lost its legacy `registerTenantMiddleware` (a hardcoded `where.tenant_id = ctx.tenantId` injection that pre-dated SecurityPlugin, masked RLS bugs in older snapshots, and silently broke any table without a `tenant_id` column); SecurityPlugin is now the sole authority for tenant isolation. (b) The `tenantField` rewrite in SecurityPlugin (`tenant_id` → `organization_id` on the LHS column reference) was over-eager and was also rewriting the `current_user.tenant_id` placeholder, producing `current_user.organization_id` which `RLSUserContext` doesn't expose — silently dropping every wildcard tenant policy. The regex now anchors on a non-`.` boundary so only the column reference is rewritten. `member_default` and `viewer_readonly` also gained explicit per-object overrides for the two global tables that lack `organization_id`: `sys_organization_self` (`id = current_user.tenant_id`) and `sys_user_self` (`id = current_user.id`). Verified end-to-end on `pnpm dev:crm`: two users in different organizations each create records and only see their own org's rows on subsequent LISTs across `sys_organization`, `sys_member`, `sys_user`, and the new `sys_user_permission_set` / `sys_role_permission_set` link tables. **Known follow-up:** anonymous REST traffic still bypasses enforcement (SecurityPlugin short-circuits when `userId` is absent) — default-deny tightening, Sharing Rule evaluator, Studio RLS visual editor, per-user×org permission cache, and audit UI / denied-access logging remain queued.
12
12
- **`@objectstack/driver-mongodb` — first-class MongoDB driver (`packages/plugins/driver-mongodb`)** — A new built-in driver that implements the full `IDataDriver` contract on top of the official `mongodb@^6` Node.js driver. Highlights: per-collection `id` strategy (16-char nanoid stored as a top-level string field with a unique index; the internal `_id` is **never** exposed to consumers — every `find`/`findOne`/`findStream` uses `projection: { _id: 0 }`); pluggable filter translator (`mongodb-filter.ts`) that maps every ObjectStack operator (`$eq`/`$ne`/`$gt`/`$gte`/`$lt`/`$lte`/`$in`/`$nin`/`$exists`/`$contains`/`$startsWith`/`$endsWith`/`$null`/`$between`/`$not`/`$and`/`$or`/`$nor`) plus the legacy `[field, op, value]` tuple form into a native MongoDB query; aggregation pipeline builder (`mongodb-aggregation.ts`) supporting `count`/`sum`/`avg`/`min`/`max`/`count_distinct`/`string_agg` with `$group + $project` flattening; declarative schema sync that creates collections + unique/lookup/text/compound indexes (`mongodb-schema.ts`); cursor-based async streaming via `findStream`; `bulkCreate`/`bulkUpdate`/`bulkDelete` powered by `bulkWrite`; multi-document transactions (requires a replica set) via `MongoClient.startSession()` + `session.withTransaction()`. Ships with **75 unit tests** (filter, aggregation, full driver) running against `mongodb-memory-server` (`onlyBuiltDependencies` whitelist updated in the root `package.json` so the postinstall is allowed). Plugin entry exports a default `onEnable` hook so it can be registered as `kernel.use(mongodbPlugin, { url })` or instantiated directly via `new MongoDBDriver({ url })` and wrapped in `DriverPlugin`.
13
13
- **`OS_DATABASE_DRIVER` / `OS_DATABASE_URL` recognised by the CLI, with URL-scheme inference** — `packages/cli/src/commands/serve.ts` now selects the storage driver from `OS_DATABASE_URL` automatically: `mongodb://` / `mongodb+srv://` → `MongoDBDriver`, `postgres://` / `postgresql://` → `SqlDriver(client:'pg')`, `mysql://` / `mysql2://` → `SqlDriver(client:'mysql2')`, `libsql://` or `https://*.turso.…` → `TursoDriver`, `file:` / `sqlite:` / `:memory:` / `*.db` / `*.sqlite` → `SqlDriver(client:'better-sqlite3')`. `OS_DATABASE_DRIVER` remains an explicit override (`mongodb`/`mongo`/`postgres`/`pg`/`mysql`/`sqlite`/`turso`/`libsql`). The same selection logic is mirrored in `packages/services/service-cloud/src/{artifact-environment-registry,environment-registry,project-kernel-factory}.ts` so cloud-runtime nodes can also pin a project to MongoDB. `apps/objectos`, `packages/cli`, and `packages/services/service-cloud` now declare `@objectstack/driver-mongodb` (and the CLI now also declares `@objectstack/driver-turso`) as workspace dependencies so the dynamic `await import()` resolves.
14
14
-**MongoDB end-to-end test for the CRM example** — `examples/app-crm/playwright.config.ts` + `examples/app-crm/e2e/mongodb-driver.spec.ts` boot `pnpm dev:crm` with `OS_DATABASE_URL=mongodb://localhost:27017/objectstack_crm_test` against a local mongod (driver auto-inferred from the URL scheme — no `OS_DATABASE_DRIVER` needed) and verify (1) the Studio UI responds, (2) seed data is queryable through `GET /api/v1/data/account`, and (3) a full create → read → patch → re-read → delete round-trip lands records in MongoDB. New `pnpm --filter @example/app-crm test:e2e` script wires the suite into the workspace.
- All security protocols (identity + permission) are delivered by a single `auth` plugin — matching `CoreServiceName`
273
273
- Client SDK supports bearer token header — but token validation requires the auth plugin
274
274
- Auth route (`/auth/*`) only appears in Discovery when the auth plugin is registered
275
275
- Fine-grained authorization (RLS, sharing, territory) is internal to the auth plugin
276
+
-**Phase-1 RBAC enforcement is live end-to-end**: REST → ObjectQL → SecurityPlugin middleware now receives a populated `ExecutionContext` (userId, tenantId, roles, permissions). Default `member_default` permission set ships a wildcard RLS rule `tenant_id = current_user.tenant_id` (rewritten to `organization_id` — only the LHS column is rewritten, the `current_user.tenant_id` placeholder is preserved) plus per-object overrides `sys_organization_self` and `sys_user_self` for the global tables that lack an `organization_id` column. The legacy `objectql.registerTenantMiddleware` (hardcoded `where.tenant_id` injection that pre-dated SecurityPlugin) has been removed; SecurityPlugin is now the sole authority for tenant isolation. Verified cross-organization isolation on `pnpm dev:crm` across `sys_organization`, `sys_member`, `sys_user`, `sys_user_permission_set`, `sys_role_permission_set`. **Anonymous traffic still bypasses enforcement** until a default-deny pass lands.
276
277
277
278
---
278
279
@@ -386,11 +387,17 @@ The `auth` service in `CoreServiceName` covers both **authentication** (identity
-[x] Authorization Plugin — `@objectstack/plugin-security` enforces CRUD/FLS/RLS in the ObjectQL middleware chain; REST → ObjectQL now propagates `ExecutionContext` end-to-end (Phase-1)
393
+
-[x] Row-Level Security — default `member_default` permission set applies a wildcard `tenant_id = current_user.tenant_id` rule (only the LHS column is rewritten to `organization_id`; the `current_user.tenant_id` placeholder is preserved) plus per-object overrides `sys_organization_self` / `sys_user_self`
394
+
-[x] Multi-tenancy — verified cross-organization isolation on `pnpm dev:crm` (Alice@OrgAlpha vs. Bob@OrgBeta only see their own records across `sys_organization`, `sys_member`, `sys_user`, and `sys_*_permission_set` link tables)
395
+
-[x] Legacy `objectql.registerTenantMiddleware` removed — SecurityPlugin is now the sole tenant-isolation authority
Copy file name to clipboardExpand all lines: content/docs/guides/security.mdx
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,8 @@ description: "Complete guide to implementing enterprise-grade security in Object
7
7
8
8
Complete guide to implementing enterprise-grade security in ObjectStack with fine-grained permissions and data access controls.
9
9
10
+
> **Implementation status — Phase-1 RBAC is live.** REST → ObjectQL now propagates a populated `ExecutionContext` (userId, tenantId, roles, permissions) into the SecurityPlugin middleware, so CRUD / FLS / RLS checks actually fire on every authenticated request. The default `member_default` permission set ships a wildcard RLS rule `tenant_id = current_user.tenant_id` (rewritten onto the configured `tenantField`, default `organization_id` — only the LHS column is rewritten, the `current_user.tenant_id` placeholder is preserved) plus explicit per-object overrides `sys_organization_self` (`id = current_user.tenant_id`) and `sys_user_self` (`id = current_user.id`) for the two global tables that lack an `organization_id` column. The legacy `objectql.registerTenantMiddleware` (a hardcoded `where.tenant_id` injection that pre-dated SecurityPlugin) has been removed; SecurityPlugin is now the sole authority for tenant isolation. End-to-end verified on `pnpm dev:crm` across `sys_organization`, `sys_member`, `sys_user`, `sys_user_permission_set`, `sys_role_permission_set`. **Anonymous traffic still bypasses enforcement** until a default-deny pass lands; Sharing Rules, Studio RLS visual editor, per-user×org permission cache, and audit UI for denied access are queued. See `CHANGELOG.md` and `concepts/implementation-status.mdx` for the latest matrix.
0 commit comments