Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0b027a9aa
|
||
|
|
4d5139cab6 | ||
|
|
3ca94f7da5 | ||
|
|
cfb176e3ac | ||
|
|
e156a710e3
|
No files matched your search
+6
-3
@@ -17,10 +17,13 @@ All notable changes to this project are documented here. The format follows Keep
|
||||
- **Names follow the owner rule: lowercase snake_case, prefixed with the qbit's name (metadata only, no DDL).** Tables `sql_workbench_notebook`, `sql_workbench_cell`, `sql_workbench_notebook_version`, `sql_workbench_result_snapshot`, `sql_workbench_notebook_share`, `sql_workbench_execution` (they now equal their backend table names); possible-value source `sql_workbench_share_audience_type`; permissions `sql_workbench.*`, `sql_workbench_admin.hasAccess` and `<table>.read` etc.; security key types `sql_workbench_user` (all-access `sql_workbench_all_access`) and `sql_workbench_audience`; joins `sql_workbench_notebook_join_sql_workbench_*`.
|
||||
- `tableNamePrefix` (and the QBit namespace) is normalized to lowercase snake_case and joined with one `_`.
|
||||
|
||||
### Deprecated
|
||||
- `LEGACY_TABLE_NAME` on each entity, `ShareAudienceType.LEGACY_NAME` and `SqlWorkbenchQBitConfig.LEGACY_*` hold the pre-RC.3 names, for migrating stored references only.
|
||||
### Fixed
|
||||
- **Pinning a result works with QQQ core permission enforcement on.** `getUserPermissionNames()` now includes `sql_workbench_result_snapshot.insert`: pinning is a top-level insert, which core enforcement checks, so hosts no longer need to grant it by hand. Record locks still limit snapshots to the user's own notebooks.
|
||||
|
||||
### Upgrade from RC.2
|
||||
### Deprecated
|
||||
- `LEGACY_TABLE_NAME` on each entity, `ShareAudienceType.LEGACY_NAME` and `SqlWorkbenchQBitConfig.LEGACY_*` hold the pre-RC.4 names, for migrating stored references only.
|
||||
|
||||
### Upgrade from RC.2 (or RC.3, a copy of RC.2)
|
||||
- Follow [UPGRADE.md](UPGRADE.md): grant the renamed permissions and rename stored table names (audit, saved views and reports). No DDL.
|
||||
|
||||
## [1.0.0-RC.3] - 2026-10-12
|
||||
|
||||
@@ -114,7 +114,7 @@ Everything is denied by default. Two standard QQQ layers apply:
|
||||
|
||||
| Role | Grant | What it allows |
|
||||
|---|---|---|
|
||||
| User | `config.getUserPermissionNames()`: `sql_workbench.hasAccess`, plus read/insert/edit/delete on notebooks and cells, read on versions, shares and the audit, and read/edit/delete on pinned results | The app, widget and endpoints (read-only SQL, EXPLAIN, schema). Their own notebooks, read-only access to notebooks shared with them or in the library, and their own execution history. |
|
||||
| User | `config.getUserPermissionNames()`: `sql_workbench.hasAccess`, plus read/insert/edit/delete on notebooks and cells, read on versions, shares and the audit, and read/insert/edit/delete on pinned results | The app, widget and endpoints (read-only SQL, EXPLAIN, schema). Their own notebooks, read-only access to notebooks shared with them or in the library, and their own execution history. |
|
||||
| Writer | `config.getWritePermissionName()` (`sql_workbench.write`) | Write mode (preview and commit) and EXPLAIN ANALYZE. |
|
||||
| Sharer | `config.getSharePermissionNames()`: `sql_workbench.share`, plus insert/edit/delete on shares | Sharing their own notebooks with users, groups, or everyone (publishing to the library). |
|
||||
| Admin | `config.getAdminPermissionNames()`: the user permissions plus `sql_workbench_admin.hasAccess` | The all-access key: every user's notebooks, cells, versions, pinned results (real query data), shares, and the whole execution audit, and editing any notebook. |
|
||||
@@ -244,7 +244,7 @@ A checklist for adding the workbench to a QQQ application. Each row points to th
|
||||
| ESB and search | Optional: wire `SqlWorkbenchIntegrations.esbTableNames` and `quickSearchTables` into your ESB and Quick Search config ([ESB and Quick Search](#esb-and-quick-search)). |
|
||||
| Turning writes off | `withWriteModeEnabled(false)` disables write mode for everyone. |
|
||||
| Known limitations | Raw SQL bypasses QQQ security, audits, listeners and automations ([Safety model](#safety-model)). H2 and generic backends have no enforced read-only transaction. Only PostgreSQL DDL ships. Shares are read-only (no shared editing). |
|
||||
| Upgrading | From RC.2: the QQQ names became snake_case (`sql_workbench_*`; no DDL). See [UPGRADE.md](UPGRADE.md) for permissions and stored table names. From RC.1: also run the share-table DDL and backfill, grant the new permission lists (the tables no longer rely on the admin `hasAccess` permission alone), call `applyToSession`, and use QQQ `4.1.0-RC.4` or later. |
|
||||
| Upgrading | From RC.2 (or RC.3, a copy of RC.2): the QQQ names became snake_case (`sql_workbench_*`; no DDL). See [UPGRADE.md](UPGRADE.md) for permissions and stored table names. From RC.1: also run the share-table DDL and backfill, grant the new permission lists (the tables no longer rely on the admin `hasAccess` permission alone), call `applyToSession`, and use QQQ `4.1.0-RC.4` or later. |
|
||||
|
||||
Nothing in the qbit is specific to one host application.
|
||||
|
||||
|
||||
+5
-3
@@ -1,10 +1,12 @@
|
||||
# Upgrading
|
||||
|
||||
## To 1.0.0-RC.3: snake_case names (metadata only)
|
||||
## To 1.0.0-RC.4: snake_case names (metadata only)
|
||||
|
||||
The owner naming rule now applies to every QRun-IO qbit: table names are lowercase snake_case, prefixed with the qbit's name. In 1.0.0-RC.3 the SQL Workbench's QQQ names change to match. **The database does not change**: the physical tables were already `sql_workbench_*` (`docs/ddl/postgres.sql`), so no DDL runs. What changes is the names QQQ uses, and anything your app stored under the old names.
|
||||
The rename shipped in 1.0.0-RC.4. 1.0.0-RC.3 on Maven Central is an accidental republish of the RC.2 code (see CHANGELOG.md), so upgrading from RC.3 is the same as upgrading from RC.2.
|
||||
|
||||
| Kind | Before (RC.1, RC.2) | From RC.3 |
|
||||
The owner naming rule now applies to every QRun-IO qbit: table names are lowercase snake_case, prefixed with the qbit's name. In 1.0.0-RC.4 the SQL Workbench's QQQ names change to match. **The database does not change**: the physical tables were already `sql_workbench_*` (`docs/ddl/postgres.sql`), so no DDL runs. What changes is the names QQQ uses, and anything your app stored under the old names.
|
||||
|
||||
| Kind | Before (RC.1, RC.2, RC.3) | From RC.4 |
|
||||
|---|---|---|
|
||||
| Tables (and the notebook possible-value source) | `sqlWorkbenchNotebook`, `sqlWorkbenchCell`, `sqlWorkbenchNotebookVersion`, `sqlWorkbenchResultSnapshot`, `sqlWorkbenchNotebookShare`, `sqlWorkbenchExecution` | `sql_workbench_notebook`, `sql_workbench_cell`, `sql_workbench_notebook_version`, `sql_workbench_result_snapshot`, `sql_workbench_notebook_share`, `sql_workbench_execution` |
|
||||
| Enum possible-value source | `sqlWorkbenchShareAudienceType` | `sql_workbench_share_audience_type` |
|
||||
|
||||
@@ -71,23 +71,23 @@ public class SqlWorkbenchQBitConfig implements QBitConfig
|
||||
public static final String ALL_ACCESS_SECURITY_KEY_NAME = "sql_workbench_all_access";
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// names before 1.0.0-RC.3, when the owner rule (lowercase snake_case, //
|
||||
// names before 1.0.0-RC.4, when the owner rule (lowercase snake_case, //
|
||||
// prefixed with the qbit name) was applied; kept only so hosts can //
|
||||
// migrate stored references (see UPGRADE.md) //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@Deprecated(since = "1.0.0-RC.3")
|
||||
@Deprecated(since = "1.0.0-RC.4")
|
||||
public static final String LEGACY_ACCESS_PERMISSION_BASE_NAME = "sqlWorkbench";
|
||||
@Deprecated(since = "1.0.0-RC.3")
|
||||
@Deprecated(since = "1.0.0-RC.4")
|
||||
public static final String LEGACY_WRITE_PERMISSION_NAME = "sqlWorkbench.write";
|
||||
@Deprecated(since = "1.0.0-RC.3")
|
||||
@Deprecated(since = "1.0.0-RC.4")
|
||||
public static final String LEGACY_SHARE_PERMISSION_NAME = "sqlWorkbench.share";
|
||||
@Deprecated(since = "1.0.0-RC.3")
|
||||
@Deprecated(since = "1.0.0-RC.4")
|
||||
public static final String LEGACY_ADMIN_PERMISSION_BASE_NAME = "sqlWorkbenchAdmin";
|
||||
@Deprecated(since = "1.0.0-RC.3")
|
||||
@Deprecated(since = "1.0.0-RC.4")
|
||||
public static final String LEGACY_USER_SECURITY_KEY_TYPE_NAME = "sqlWorkbenchUser";
|
||||
@Deprecated(since = "1.0.0-RC.3")
|
||||
@Deprecated(since = "1.0.0-RC.4")
|
||||
public static final String LEGACY_AUDIENCE_SECURITY_KEY_TYPE_NAME = "sqlWorkbenchAudience";
|
||||
@Deprecated(since = "1.0.0-RC.3")
|
||||
@Deprecated(since = "1.0.0-RC.4")
|
||||
public static final String LEGACY_ALL_ACCESS_SECURITY_KEY_NAME = "sqlWorkbenchAllAccess";
|
||||
|
||||
private String backendName;
|
||||
@@ -377,8 +377,8 @@ public class SqlWorkbenchQBitConfig implements QBitConfig
|
||||
/*******************************************************************************
|
||||
** Every permission an ordinary workbench user needs: the access
|
||||
** permission, read / insert / edit / delete on notebooks and cells, read
|
||||
** on versions, shares and the execution audit, and read / edit (label) /
|
||||
** delete on pinned snapshots. Record locks narrow every one of these to
|
||||
** on versions, shares and the execution audit, and read / insert (pin) /
|
||||
** edit (label) / delete on pinned snapshots. Record locks narrow every one of these to
|
||||
** the user's own (or shared-with-them, for reads) rows.
|
||||
*******************************************************************************/
|
||||
@JsonIgnore
|
||||
@@ -389,7 +389,7 @@ public class SqlWorkbenchQBitConfig implements QBitConfig
|
||||
addTablePermissions(names, SqlWorkbenchNotebook.TABLE_NAME, "read", "insert", "edit", "delete");
|
||||
addTablePermissions(names, SqlWorkbenchCell.TABLE_NAME, "read", "insert", "edit", "delete");
|
||||
addTablePermissions(names, SqlWorkbenchNotebookVersion.TABLE_NAME, "read");
|
||||
addTablePermissions(names, SqlWorkbenchResultSnapshot.TABLE_NAME, "read", "edit", "delete");
|
||||
addTablePermissions(names, SqlWorkbenchResultSnapshot.TABLE_NAME, "read", "insert", "edit", "delete");
|
||||
addTablePermissions(names, SqlWorkbenchNotebookShare.TABLE_NAME, "read");
|
||||
addTablePermissions(names, SqlWorkbenchExecution.TABLE_NAME, "read");
|
||||
return (names);
|
||||
|
||||
@@ -33,9 +33,9 @@ public enum ShareAudienceType implements PossibleValueEnum<String>
|
||||
public static final String NAME = "sql_workbench_share_audience_type";
|
||||
|
||||
/*******************************************************************************
|
||||
** The possible-value source name before 1.0.0-RC.3.
|
||||
** The possible-value source name before 1.0.0-RC.4.
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "1.0.0-RC.3")
|
||||
@Deprecated(since = "1.0.0-RC.4")
|
||||
public static final String LEGACY_NAME = "sqlWorkbenchShareAudienceType";
|
||||
|
||||
private final String label;
|
||||
|
||||
@@ -32,10 +32,10 @@ public class SqlWorkbenchCell extends QRecordEntity
|
||||
public static final String TABLE_NAME = "sql_workbench_cell";
|
||||
|
||||
/*******************************************************************************
|
||||
** The table name before 1.0.0-RC.3 (owner rule: lowercase snake_case,
|
||||
** The table name before 1.0.0-RC.4 (owner rule: lowercase snake_case,
|
||||
** prefixed with the qbit name). Kept for migrating stored references only.
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "1.0.0-RC.3")
|
||||
@Deprecated(since = "1.0.0-RC.4")
|
||||
public static final String LEGACY_TABLE_NAME = "sqlWorkbenchCell";
|
||||
|
||||
@QField(isEditable = false, isPrimaryKey = true)
|
||||
|
||||
@@ -34,10 +34,10 @@ public class SqlWorkbenchExecution extends QRecordEntity
|
||||
public static final String TABLE_NAME = "sql_workbench_execution";
|
||||
|
||||
/*******************************************************************************
|
||||
** The table name before 1.0.0-RC.3 (owner rule: lowercase snake_case,
|
||||
** The table name before 1.0.0-RC.4 (owner rule: lowercase snake_case,
|
||||
** prefixed with the qbit name). Kept for migrating stored references only.
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "1.0.0-RC.3")
|
||||
@Deprecated(since = "1.0.0-RC.4")
|
||||
public static final String LEGACY_TABLE_NAME = "sqlWorkbenchExecution";
|
||||
|
||||
@QField(isEditable = false, isPrimaryKey = true)
|
||||
|
||||
@@ -33,10 +33,10 @@ public class SqlWorkbenchNotebook extends QRecordEntity
|
||||
public static final String TABLE_NAME = "sql_workbench_notebook";
|
||||
|
||||
/*******************************************************************************
|
||||
** The table name before 1.0.0-RC.3 (owner rule: lowercase snake_case,
|
||||
** The table name before 1.0.0-RC.4 (owner rule: lowercase snake_case,
|
||||
** prefixed with the qbit name). Kept for migrating stored references only.
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "1.0.0-RC.3")
|
||||
@Deprecated(since = "1.0.0-RC.4")
|
||||
public static final String LEGACY_TABLE_NAME = "sqlWorkbenchNotebook";
|
||||
|
||||
@QField(isEditable = false, isPrimaryKey = true)
|
||||
|
||||
@@ -36,10 +36,10 @@ public class SqlWorkbenchNotebookShare extends QRecordEntity
|
||||
public static final String TABLE_NAME = "sql_workbench_notebook_share";
|
||||
|
||||
/*******************************************************************************
|
||||
** The table name before 1.0.0-RC.3 (owner rule: lowercase snake_case,
|
||||
** The table name before 1.0.0-RC.4 (owner rule: lowercase snake_case,
|
||||
** prefixed with the qbit name). Kept for migrating stored references only.
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "1.0.0-RC.3")
|
||||
@Deprecated(since = "1.0.0-RC.4")
|
||||
public static final String LEGACY_TABLE_NAME = "sqlWorkbenchNotebookShare";
|
||||
|
||||
@QField(isEditable = false, isPrimaryKey = true)
|
||||
|
||||
+2
-2
@@ -32,10 +32,10 @@ public class SqlWorkbenchNotebookVersion extends QRecordEntity
|
||||
public static final String TABLE_NAME = "sql_workbench_notebook_version";
|
||||
|
||||
/*******************************************************************************
|
||||
** The table name before 1.0.0-RC.3 (owner rule: lowercase snake_case,
|
||||
** The table name before 1.0.0-RC.4 (owner rule: lowercase snake_case,
|
||||
** prefixed with the qbit name). Kept for migrating stored references only.
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "1.0.0-RC.3")
|
||||
@Deprecated(since = "1.0.0-RC.4")
|
||||
public static final String LEGACY_TABLE_NAME = "sqlWorkbenchNotebookVersion";
|
||||
|
||||
@QField(isEditable = false, isPrimaryKey = true)
|
||||
|
||||
+2
-2
@@ -33,10 +33,10 @@ public class SqlWorkbenchResultSnapshot extends QRecordEntity
|
||||
public static final String TABLE_NAME = "sql_workbench_result_snapshot";
|
||||
|
||||
/*******************************************************************************
|
||||
** The table name before 1.0.0-RC.3 (owner rule: lowercase snake_case,
|
||||
** The table name before 1.0.0-RC.4 (owner rule: lowercase snake_case,
|
||||
** prefixed with the qbit name). Kept for migrating stored references only.
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "1.0.0-RC.3")
|
||||
@Deprecated(since = "1.0.0-RC.4")
|
||||
public static final String LEGACY_TABLE_NAME = "sqlWorkbenchResultSnapshot";
|
||||
|
||||
@QField(isEditable = false, isPrimaryKey = true)
|
||||
|
||||
@@ -178,9 +178,9 @@ class SqlWorkbenchQBitProducerTest extends BaseSqlWorkbenchTest
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Owner naming rule (1.0.0-RC.3): table, possible-value source, permission
|
||||
** Owner naming rule (1.0.0-RC.4): table, possible-value source, permission
|
||||
** and security key names are lowercase snake_case prefixed with the qbit's
|
||||
** name; the pre-RC.3 names stay available only as deprecated constants.
|
||||
** name; the pre-RC.4 names stay available only as deprecated constants.
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
|
||||
@@ -219,6 +219,36 @@ class NotebookServiceTest extends BaseSqlWorkbenchTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** With QQQ core permission enforcement on, an ordinary user's permission set
|
||||
** (getUserPermissionNames) is enough for the whole notebook flow, including
|
||||
** pinning a result (a top-level snapshot insert).
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testUserPermissionsSufficeWithCoreEnforcement() throws Exception
|
||||
{
|
||||
qInstance.setEnforcePermissionsInCoreActions(true);
|
||||
try
|
||||
{
|
||||
Integer id = (Integer) service.create(body("Enforced", List.of(cell("c1", "SQL", "SELECT * FROM person"))), as(READER)).get("id");
|
||||
service.update(id, Map.of("title", "Enforced v2"), as(READER));
|
||||
|
||||
SqlExecutionEngine engine = new SqlExecutionEngine(qInstance, SqlWorkbenchRuntime.get(qInstance));
|
||||
Map<String, Object> response = engine.execute(ExecutionRequest.readOnly(BACKEND_NAME, "SELECT * FROM person", Map.of()), as(READER));
|
||||
|
||||
Map<String, Object> snapshot = service.createSnapshot(id, Map.of("executionUuid", response.get("executionUuid"), "cellKey", "c1"), as(READER), "SELECT * FROM person");
|
||||
assertThat(snapshot.get("id")).isNotNull();
|
||||
service.deleteSnapshot(id, (Integer) snapshot.get("id"), as(READER));
|
||||
service.delete(id, as(READER));
|
||||
}
|
||||
finally
|
||||
{
|
||||
qInstance.setEnforcePermissionsInCoreActions(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
||||
Reference in new issue
Block a user