sf-plugin-permission-sets 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -42
- package/lib/adapters/connection-org-client.d.ts +17 -0
- package/lib/adapters/connection-org-client.d.ts.map +1 -0
- package/lib/adapters/connection-org-client.js +248 -0
- package/lib/adapters/connection-org-client.js.map +1 -0
- package/lib/commands/ps/apply.d.ts +27 -0
- package/lib/commands/ps/apply.d.ts.map +1 -0
- package/lib/commands/ps/apply.js +124 -0
- package/lib/commands/ps/apply.js.map +1 -0
- package/lib/commands/ps/check.d.ts +2 -1
- package/lib/commands/ps/check.d.ts.map +1 -0
- package/lib/commands/ps/check.js +5 -4
- package/lib/commands/ps/check.js.map +1 -1
- package/lib/commands/ps/export.d.ts +17 -0
- package/lib/commands/ps/export.d.ts.map +1 -0
- package/lib/commands/ps/export.js +28 -0
- package/lib/commands/ps/export.js.map +1 -0
- package/lib/commands/ps/plan.d.ts +22 -0
- package/lib/commands/ps/plan.d.ts.map +1 -0
- package/lib/commands/ps/plan.js +77 -0
- package/lib/commands/ps/plan.js.map +1 -0
- package/lib/commands/ps/validate.d.ts +19 -0
- package/lib/commands/ps/validate.d.ts.map +1 -0
- package/lib/commands/ps/validate.js +47 -0
- package/lib/commands/ps/validate.js.map +1 -0
- package/lib/core/diff.d.ts +10 -0
- package/lib/core/diff.d.ts.map +1 -0
- package/lib/core/diff.js +53 -0
- package/lib/core/diff.js.map +1 -0
- package/lib/core/finding.d.ts +40 -0
- package/lib/core/finding.d.ts.map +1 -0
- package/lib/core/finding.js +68 -0
- package/lib/core/finding.js.map +1 -0
- package/lib/core/load.d.ts +2 -6
- package/lib/core/load.d.ts.map +1 -0
- package/lib/core/load.js +6 -5
- package/lib/core/load.js.map +1 -1
- package/lib/core/model.d.ts +57 -7
- package/lib/core/model.d.ts.map +1 -0
- package/lib/core/normalize.d.ts +6 -1
- package/lib/core/normalize.d.ts.map +1 -0
- package/lib/core/normalize.js +46 -29
- package/lib/core/normalize.js.map +1 -1
- package/lib/core/parse.d.ts +2 -1
- package/lib/core/parse.d.ts.map +1 -0
- package/lib/core/parse.js +4 -9
- package/lib/core/parse.js.map +1 -1
- package/lib/core/report.d.ts +8 -8
- package/lib/core/report.d.ts.map +1 -0
- package/lib/core/report.js +59 -12
- package/lib/core/report.js.map +1 -1
- package/lib/core/resolve.d.ts +20 -0
- package/lib/core/resolve.d.ts.map +1 -0
- package/lib/core/resolve.js +88 -0
- package/lib/core/resolve.js.map +1 -0
- package/lib/core/schema.d.ts +18 -5
- package/lib/core/schema.d.ts.map +1 -0
- package/lib/core/schema.js +19 -10
- package/lib/core/schema.js.map +1 -1
- package/lib/core/serialize.d.ts +9 -0
- package/lib/core/serialize.d.ts.map +1 -0
- package/lib/core/serialize.js +34 -0
- package/lib/core/serialize.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/services/adapters/org-client.d.ts +22 -0
- package/lib/services/adapters/org-client.d.ts.map +1 -0
- package/lib/services/adapters/org-client.js +2 -0
- package/lib/services/adapters/org-client.js.map +1 -0
- package/lib/services/apply.d.ts +40 -0
- package/lib/services/apply.d.ts.map +1 -0
- package/lib/services/apply.js +94 -0
- package/lib/services/apply.js.map +1 -0
- package/lib/services/check.d.ts +9 -6
- package/lib/services/check.d.ts.map +1 -0
- package/lib/services/check.js +21 -13
- package/lib/services/check.js.map +1 -1
- package/lib/services/export.d.ts +14 -0
- package/lib/services/export.d.ts.map +1 -0
- package/lib/services/export.js +25 -0
- package/lib/services/export.js.map +1 -0
- package/lib/services/plan.d.ts +35 -0
- package/lib/services/plan.d.ts.map +1 -0
- package/lib/services/plan.js +47 -0
- package/lib/services/plan.js.map +1 -0
- package/lib/services/resolution.d.ts +17 -0
- package/lib/services/resolution.d.ts.map +1 -0
- package/lib/services/resolution.js +36 -0
- package/lib/services/resolution.js.map +1 -0
- package/lib/services/validate.d.ts +21 -0
- package/lib/services/validate.d.ts.map +1 -0
- package/lib/services/validate.js +43 -0
- package/lib/services/validate.js.map +1 -0
- package/messages/ps.apply.md +81 -0
- package/messages/ps.export.md +25 -0
- package/messages/ps.plan.md +45 -0
- package/messages/ps.validate.md +29 -0
- package/package.json +27 -42
- package/lib/commands/ps/info.d.ts +0 -11
- package/lib/commands/ps/info.js +0 -17
- package/lib/commands/ps/info.js.map +0 -1
- package/messages/ps.info.md +0 -17
- package/oclif.lock +0 -7888
- package/oclif.manifest.json +0 -113
package/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# sf-plugin-permission-sets
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/sf-plugin-permission-sets) [](https://npmjs.org/package/sf-plugin-permission-sets) [](https://raw.githubusercontent.com/zaclummys/sf-plugin-permission-sets/main/LICENSE.txt)
|
|
3
|
+
[](https://www.npmjs.com/package/sf-plugin-permission-sets) [](https://npmjs.org/package/sf-plugin-permission-sets) [](https://semver.org/#spec-item-4) [](https://raw.githubusercontent.com/zaclummys/sf-plugin-permission-sets/main/LICENSE.txt)
|
|
4
4
|
|
|
5
5
|
> Declarative, GitOps-style management of **permission set assignments** for Salesforce orgs.
|
|
6
6
|
> Define who gets what in version-controlled YAML. The plugin reconciles your org to match it: `plan` then `apply`, just like Terraform.
|
|
7
7
|
|
|
8
|
+
> ⚠️ **Under active development.** This plugin is `0.x`. Per [semver's major-version-zero rule](https://semver.org/#spec-item-4), anything (commands, flags, the YAML schema) may change in a breaking way between `0.x` releases. Pin a version in CI. The public API stabilizes at `v1.0.0`.
|
|
9
|
+
|
|
8
10
|
Stop clicking through Setup to grant access. Commit a YAML file, open a PR, let CI show the diff, and merge to apply. Your git history becomes the audit log of who-had-access-when.
|
|
9
11
|
|
|
10
12
|
---
|
|
@@ -16,8 +18,8 @@ Stop clicking through Setup to grant access. Commit a YAML file, open a PR, let
|
|
|
16
18
|
- [Quick start](#quick-start)
|
|
17
19
|
- [Permission files](#permission-files)
|
|
18
20
|
- [Organizing files](#organizing-files)
|
|
19
|
-
- [Validations](#validations)
|
|
20
21
|
- [Modes](#modes)
|
|
22
|
+
- [Validations](#validations)
|
|
21
23
|
- [Commands](#commands)
|
|
22
24
|
- [Inspiration & equivalents](#inspiration--equivalents)
|
|
23
25
|
- [Versioning](#versioning)
|
|
@@ -57,7 +59,7 @@ Requires Salesforce CLI (`sf`) and Node.js 18+.
|
|
|
57
59
|
|
|
58
60
|
```bash
|
|
59
61
|
# 1. Bootstrap YAML from an existing org (so you don't start from scratch)
|
|
60
|
-
sf ps export --target-org dev --output-
|
|
62
|
+
sf ps export --target-org dev --output-file permissions.yml
|
|
61
63
|
|
|
62
64
|
# 2. Edit the files, commit, open a PR. Validate offline, no org needed:
|
|
63
65
|
sf ps check --file "./permissions/*.yml"
|
|
@@ -112,6 +114,24 @@ users:
|
|
|
112
114
|
- Sales_Manager
|
|
113
115
|
```
|
|
114
116
|
|
|
117
|
+
### Timed access (expiration)
|
|
118
|
+
|
|
119
|
+
A permission set or permission set group entry can be a plain name or an object with an `expiration`. The expiration is an ISO 8601 datetime, and Salesforce removes access automatically when it passes. Plain names never expire.
|
|
120
|
+
|
|
121
|
+
```yaml
|
|
122
|
+
users:
|
|
123
|
+
contractor@acme.com:
|
|
124
|
+
permissionSets:
|
|
125
|
+
- Read_Only # permanent
|
|
126
|
+
- name: Sales_Manager # expires automatically
|
|
127
|
+
expiration: 2026-12-31T23:59:59Z
|
|
128
|
+
permissionSetGroups:
|
|
129
|
+
- name: Project_Phoenix_Bundle
|
|
130
|
+
expiration: 2026-09-30T00:00:00Z
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Expiration is a property of the grant, so `plan` and `apply` treat a changed `expiration` on an already-assigned target as an **update** (the `~` line), not an add or a remove. Updates ride with the additive half: they run in `additive` and `sync` modes and never count against `--max-deletes`. Permission set **licenses** cannot expire (Salesforce has no expiration on `PermissionSetLicenseAssign`), so the object form is rejected there. `export` writes the object form for any assignment that currently has an expiration in the org.
|
|
134
|
+
|
|
115
135
|
The `--file` flag is repeatable and the plugin expands globs itself, so all of these work:
|
|
116
136
|
|
|
117
137
|
```bash
|
|
@@ -157,37 +177,37 @@ sf ps apply -o qa --file "permissions/qa/*.yml"
|
|
|
157
177
|
|
|
158
178
|
The two compose: a directory per environment, each split into functional files.
|
|
159
179
|
|
|
180
|
+
## Modes
|
|
181
|
+
|
|
182
|
+
A run performs three operations: **add** missing assignments, **update** changed expirations on declared ones, and **remove** undeclared ones. Updates ride with the additive half (they touch a declared grant, never revoke access). The mode selects which it actually executes. Set it with `--mode` (default `additive`):
|
|
183
|
+
|
|
184
|
+
| Mode | Adds missing | Updates expirations | Removes undeclared | Use when… |
|
|
185
|
+
| ------------- | :----------: | :-----------------: | :----------------: | --------------------------------------------------------------------- |
|
|
186
|
+
| `sync` | ✅ | ✅ | ✅ | Full reconcile: make the org exactly match the YAML (`sync` = `additive` + `destructive`). |
|
|
187
|
+
| `additive` | ✅ | ✅ | ❌ | **Default.** Grant access, never revoke. Safe rollout. |
|
|
188
|
+
| `destructive` | ❌ | ❌ | ✅ | Prune/revoke access that isn't declared, without granting anything new. |
|
|
189
|
+
|
|
190
|
+
`plan` always shows the *full* picture (adds, expiration updates, **and** would-be removes) regardless of mode, so you can preview the impact before running it. Whatever the chosen mode won't act on is surfaced as **drift**.
|
|
191
|
+
|
|
160
192
|
## Validations
|
|
161
193
|
|
|
162
194
|
Every run checks the files first. `check` runs the offline checks with no org, and `validate` adds the org-side checks. When files merge, most overlaps are unions rather than errors.
|
|
163
195
|
|
|
164
196
|
| Situation | Checked by | Severity | Result |
|
|
165
197
|
| --- | --- | :---: | --- |
|
|
166
|
-
| Same username key appears twice in one file | `check` (offline) | ❌ error | Rejected, the intent is ambiguous |
|
|
167
|
-
| Same target listed twice for a user, like `[Sales_Manager, Sales_Manager]` | `check` (offline) | ⚠️ warning | Deduped |
|
|
168
|
-
| A user with no scopes, or an empty list | `check` (offline) | ⚠️ warning | Ignored as a no-op |
|
|
169
198
|
| Same user in two files with different targets | `check` (offline) | ✅ ok | Merged into one model, the point of slicing |
|
|
199
|
+
| Same target listed twice for a user | `check` (offline) | ⚠️ warning | Deduped |
|
|
200
|
+
| A user with no scopes, or an empty list | `check` (offline) | ⚠️ warning | Ignored as a no-op |
|
|
201
|
+
| Same username key appears twice in one file | `check` (offline) | ❌ error | Rejected, the intent is ambiguous |
|
|
170
202
|
| Declared user, permission set, group, or license missing or not unique | `validate` (online) | ❌ error | Run fails before any change |
|
|
171
203
|
|
|
172
|
-
## Modes
|
|
173
|
-
|
|
174
|
-
A run performs two atomic operations: **add** missing assignments and **remove** undeclared ones. The mode selects which it actually executes. Set it with `--mode` (default `additive`):
|
|
175
|
-
|
|
176
|
-
| Mode | Adds missing | Removes undeclared | Use when… |
|
|
177
|
-
| ------------- | :----------: | :----------------: | --------------------------------------------------------------------- |
|
|
178
|
-
| `additive` | ✅ | ❌ | **Default.** Grant access, never revoke. Safe rollout. |
|
|
179
|
-
| `destructive` | ❌ | ✅ | Prune/revoke access that isn't declared, without granting anything new. |
|
|
180
|
-
| `sync` | ✅ | ✅ | Full reconcile: make the org exactly match the YAML (`sync` = `additive` + `destructive`). |
|
|
181
|
-
|
|
182
|
-
`plan` always shows the *full* picture (both adds **and** would-be removes) regardless of mode, so you can preview the impact before running it. Whatever the chosen mode won't act on is surfaced as **drift**. Gate CI on it with `--fail-on-drift`.
|
|
183
|
-
|
|
184
204
|
## Commands
|
|
185
205
|
|
|
186
206
|
| Command | Purpose |
|
|
187
207
|
| ---------------- | ---------------------------------------------------------------------- |
|
|
188
208
|
| `sf ps check` | Static analysis of the files alone: schema, duplicates, conflicts, identifier shape. No org, no auth. |
|
|
189
209
|
| `sf ps validate` | Everything `check` does, plus resolving every user/permission set against the org. |
|
|
190
|
-
| `sf ps plan` | Compute and display the change set
|
|
210
|
+
| `sf ps plan` | Compute and display the change set: a read-only preview of what `apply` would do. |
|
|
191
211
|
| `sf ps apply` | Reconcile the org. Honors `--mode`, prompts before deletes, enforces guardrails. |
|
|
192
212
|
| `sf ps export` | Generate YAML from the current org state to bootstrap adoption. |
|
|
193
213
|
|
|
@@ -197,12 +217,11 @@ Fully offline: runs in any CI job or pre-commit hook without org credentials.
|
|
|
197
217
|
|
|
198
218
|
```
|
|
199
219
|
USAGE
|
|
200
|
-
$ sf ps check -f <glob>... [--strict] [--
|
|
220
|
+
$ sf ps check -f <glob>... [--strict] [--json]
|
|
201
221
|
|
|
202
222
|
FLAGS
|
|
203
223
|
-f, --file=<glob>... (required) YAML file(s) to read. Repeatable, globs are expanded by the plugin.
|
|
204
224
|
--strict Treat warnings as errors.
|
|
205
|
-
-w, --watch Re-run on every change to a matched file. Stays in the foreground until you stop it (Ctrl-C).
|
|
206
225
|
|
|
207
226
|
CHECKS
|
|
208
227
|
- valid YAML & schema (unknown keys rejected)
|
|
@@ -212,18 +231,15 @@ CHECKS
|
|
|
212
231
|
- internal referential integrity
|
|
213
232
|
```
|
|
214
233
|
|
|
215
|
-
`--watch` is for the local edit loop: leave it running while you tweak the YAML and the findings refresh on every save. It re-expands the globs each run, so files you add or delete are picked up too. It's a foreground, interactive mode, so it can't be combined with `--json` and isn't meant for CI, where a single gated `check` run is what you want.
|
|
216
|
-
|
|
217
234
|
### `sf ps validate`
|
|
218
235
|
|
|
219
236
|
```
|
|
220
237
|
USAGE
|
|
221
|
-
$ sf ps validate -o <org> -f <glob>... [--
|
|
238
|
+
$ sf ps validate -o <org> -f <glob>... [--json]
|
|
222
239
|
|
|
223
240
|
FLAGS
|
|
224
241
|
-o, --target-org=<org> (required) Org to resolve against.
|
|
225
242
|
-f, --file=<glob>... (required) YAML file(s) to read. Repeatable, globs expanded by the plugin.
|
|
226
|
-
-w, --watch Re-run on every change to a matched file. Read-only, but each run queries the org, so point it at a scratch or dev org.
|
|
227
243
|
|
|
228
244
|
Runs all offline checks, then verifies that every user (active), permission set,
|
|
229
245
|
group, and license referenced actually exists and resolves uniquely.
|
|
@@ -233,14 +249,12 @@ group, and license referenced actually exists and resolves uniquely.
|
|
|
233
249
|
|
|
234
250
|
```
|
|
235
251
|
USAGE
|
|
236
|
-
$ sf ps plan -o <org> -f <glob>... [--mode <value>] [--
|
|
252
|
+
$ sf ps plan -o <org> -f <glob>... [--mode <value>] [--json]
|
|
237
253
|
|
|
238
254
|
FLAGS
|
|
239
255
|
-o, --target-org=<org> (required)
|
|
240
256
|
-f, --file=<glob>... (required) YAML file(s) to read. Repeatable, globs expanded by the plugin.
|
|
241
257
|
--mode=<value> additive | destructive | sync [default: additive]
|
|
242
|
-
--fail-on-drift Exit non-zero if any change is pending (for CI gates).
|
|
243
|
-
-w, --watch Re-diff against the org on every change to a matched file. Read-only (never applies), but each run queries the org.
|
|
244
258
|
```
|
|
245
259
|
|
|
246
260
|
Example output:
|
|
@@ -254,6 +268,7 @@ Org: prod (00D5g0000000abcEAA) Mode: sync
|
|
|
254
268
|
permissionSets:
|
|
255
269
|
Sales_Manager
|
|
256
270
|
+ asmith@acme.com
|
|
271
|
+
~ csmith@acme.com (expires 2026-12-31T23:59:59Z)
|
|
257
272
|
- bwayne@acme.com (undeclared, will be removed)
|
|
258
273
|
= jdoe@acme.com (no change)
|
|
259
274
|
Report_Builder
|
|
@@ -262,7 +277,7 @@ permissionSets:
|
|
|
262
277
|
permissionSetGroups:
|
|
263
278
|
Sales_Team_Bundle (no changes)
|
|
264
279
|
|
|
265
|
-
Plan: 2 to add, 1 to remove, 1 unchanged.
|
|
280
|
+
Plan: 2 to add, 1 to update, 1 to remove, 1 unchanged.
|
|
266
281
|
► Review, then run: sf ps apply -o prod --mode sync
|
|
267
282
|
```
|
|
268
283
|
|
|
@@ -286,18 +301,19 @@ Deletions always prompt for confirmation unless `--no-prompt` is set, and are ha
|
|
|
286
301
|
|
|
287
302
|
### `sf ps export`
|
|
288
303
|
|
|
304
|
+
Read-only. Snapshots the org's current assignments into a single YAML file you can commit and then feed back into the other commands.
|
|
305
|
+
|
|
289
306
|
```
|
|
290
307
|
USAGE
|
|
291
|
-
$ sf ps export -o <org>
|
|
292
|
-
[--permission-sets <names>] [--json]
|
|
308
|
+
$ sf ps export -o <org> --output-file <file> [--json]
|
|
293
309
|
|
|
294
310
|
FLAGS
|
|
295
|
-
-o, --target-org=<org>
|
|
296
|
-
--output-
|
|
297
|
-
--layout=<value> by-permission-set | by-user [default: by-permission-set]
|
|
298
|
-
--permission-sets=<names> Comma-separated list to export (default: all assignable).
|
|
311
|
+
-o, --target-org=<org> (required) Org to read assignments from.
|
|
312
|
+
--output-file=<file> (required) Path of the YAML file to write. Parent directories are created; an existing file is overwritten.
|
|
299
313
|
```
|
|
300
314
|
|
|
315
|
+
It exports every assignable permission set, group, and license assignment held by active users, keyed by username, so the result is immediately valid input for `check`, `validate`, `plan`, and `apply`. Profile-owned permission sets and inactive users are skipped.
|
|
316
|
+
|
|
301
317
|
## Inspiration & equivalents
|
|
302
318
|
|
|
303
319
|
This plugin's command surface borrows ideas from tools you already know:
|
|
@@ -321,6 +337,8 @@ Releases follow [semantic versioning](https://semver.org). Snapshots are automat
|
|
|
321
337
|
- Choosing the version bump (patch, minor, or major).
|
|
322
338
|
- Creating the GitHub Release, which is what triggers the publish above.
|
|
323
339
|
|
|
340
|
+
**While on `0.x`:** breaking changes may ship in **any** release, including minor bumps. The plugin is under active development and the public API is not yet stable. The table below describes the contract that takes effect at `v1.0.0`.
|
|
341
|
+
|
|
324
342
|
| Bump | When | Example tag |
|
|
325
343
|
| --- | --- | --- |
|
|
326
344
|
| patch | bug fix, no behavior change | `v0.1.1` |
|
|
@@ -343,22 +361,27 @@ The `next` tag is selected whenever the version contains a hyphen, not by GitHub
|
|
|
343
361
|
|
|
344
362
|
## Architecture
|
|
345
363
|
|
|
346
|
-
The plugin is layered so every command reuses the same core. Commands stay thin, services hold the orchestration,
|
|
364
|
+
The plugin is layered so every command reuses the same core. Commands stay thin, services hold the orchestration, core holds the reusable primitives, and a thin adapter layer isolates the Salesforce SDK.
|
|
347
365
|
|
|
348
|
-
- **Commands** (`src/commands/ps/`): oclif only.
|
|
349
|
-
- **Services** (`src/services/`): one per command (`check
|
|
350
|
-
- **Core** (`src/core/`): the reusable building blocks.
|
|
366
|
+
- **Commands** (`src/commands/ps/`): oclif only. They parse flags, construct the service (wiring in the org adapter when the command needs one), render output, and set the exit code.
|
|
367
|
+
- **Services** (`src/services/`): one per command (`check`, `validate`, `export`, `apply`, and `plan`). Each is a class built from its dependencies and inputs, with a parameterless `run()` that turns the core into a command's behavior. A service also declares the ports it needs from the outside, like the `OrgClient` interface its adapter implements.
|
|
368
|
+
- **Core** (`src/core/`): the reusable building blocks. Pure, with no `@salesforce/*` imports, so every piece is unit-testable on its own.
|
|
369
|
+
- **Adapters** (`src/adapters/`): the boundary to the outside world. `ConnectionOrgClient` implements the `OrgClient` port (declared in services) with a Salesforce `Connection`, and owns all the SOQL and SObject detail. Services depend on the port, not the SDK, so they test against a fake and stay free of connection detail.
|
|
351
370
|
|
|
352
371
|
| Core module | Responsibility |
|
|
353
372
|
| --- | --- |
|
|
354
|
-
| `model` | Shared domain types (assignment,
|
|
373
|
+
| `model` | Shared domain types (assignment, org, diff). |
|
|
374
|
+
| `finding` | The finding type and code vocabulary, plus constructors, formatting, and counting. |
|
|
355
375
|
| `schema` | The zod contract for a file, plus validation. |
|
|
356
376
|
| `parse` | File text to an object, with YAML and duplicate-key errors. |
|
|
357
377
|
| `normalize` | A validated file to canonical `(assignee, kind, target)` tuples, plus structural findings. |
|
|
378
|
+
| `serialize` | Canonical tuples back to a user-keyed YAML document (the inverse of `normalize`). |
|
|
358
379
|
| `load` | Expand globs, run parse then validate then normalize per file, and merge by union. |
|
|
359
|
-
| `
|
|
380
|
+
| `resolve` | Pure rules that turn declared references and the org's answers into findings, plus id lookups for assigning. No SOQL: the adapter owns that. |
|
|
381
|
+
| `diff` | The desired model vs. the org's current state, producing adds, removes, and unchanged. |
|
|
382
|
+
| `report` | Format a diff as a plan. |
|
|
360
383
|
|
|
361
|
-
Commands are slices of one pipeline. `check` runs the offline **load** stage only.
|
|
384
|
+
Commands are slices of one pipeline. `check` runs the offline **load** stage only. `validate` adds **resolve**: it looks the declared references up through the `OrgClient` port (the adapter builds the SOQL) and evaluates the org's answers with resolve's pure rules. `export` runs in the opposite direction: it **fetch**es the org's current assignments through the port and **serialize**s them straight back to YAML, skipping load entirely. `apply` is the full pipeline: load, resolve to ids, **fetch** current state, **diff**, then insert and delete through the Collections API per the mode (guarded by `--max-deletes` and a confirmation). `plan` is that same pipeline stopping before the DML: load, resolve to ids, **fetch** current state, **diff**, and report, the same preview `apply --dry-run` produces.
|
|
362
385
|
|
|
363
386
|
## License
|
|
364
387
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Connection } from '@salesforce/core';
|
|
2
|
+
import { ActualAssignment, AssignmentOutcome, AssignmentUpdate, DesiredAssignment, Kind, OrgTarget, OrgUser, ResolvedAddition, TargetRef } from '../core/model.js';
|
|
3
|
+
import { OrgClient } from '../services/adapters/org-client.js';
|
|
4
|
+
/** Adapter backing OrgClient with a Salesforce Connection. autoFetchQuery pages past 2000 rows. */
|
|
5
|
+
export declare class ConnectionOrgClient implements OrgClient {
|
|
6
|
+
private readonly connection;
|
|
7
|
+
constructor(connection: Connection);
|
|
8
|
+
findUsers(usernames: string[]): Promise<OrgUser[]>;
|
|
9
|
+
findTargets(kind: Kind, names: string[]): Promise<OrgTarget[]>;
|
|
10
|
+
listAssignments(): Promise<DesiredAssignment[]>;
|
|
11
|
+
currentAssignments(targets: TargetRef[]): Promise<ActualAssignment[]>;
|
|
12
|
+
addAssignments(additions: ResolvedAddition[]): Promise<AssignmentOutcome[]>;
|
|
13
|
+
updateAssignments(updates: AssignmentUpdate[]): Promise<AssignmentOutcome[]>;
|
|
14
|
+
removeAssignments(removals: ActualAssignment[]): Promise<AssignmentOutcome[]>;
|
|
15
|
+
private query;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=connection-org-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-org-client.d.ts","sourceRoot":"","sources":["../../src/adapters/connection-org-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EACH,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,IAAI,EACJ,SAAS,EACT,OAAO,EACP,gBAAgB,EAChB,SAAS,EACZ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AA6J/D,mGAAmG;AACnG,qBAAa,mBAAoB,YAAW,SAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,UAAU;IAE7C,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAQlD,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAS9D,eAAe,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IA8C/C,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IA0DrE,cAAc,CAAC,SAAS,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAoB3E,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAoB5E,iBAAiB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;YAuB5E,KAAK;CAItB"}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/** SObject + naming field per kind. The Salesforce schema knowledge lives here, not in core. */
|
|
2
|
+
const targetObjects = {
|
|
3
|
+
permissionSet: { sobject: 'PermissionSet', field: 'Name' },
|
|
4
|
+
permissionSetGroup: { sobject: 'PermissionSetGroup', field: 'DeveloperName' },
|
|
5
|
+
permissionSetLicense: { sobject: 'PermissionSetLicense', field: 'DeveloperName' },
|
|
6
|
+
};
|
|
7
|
+
/** SObject + foreign-key field to set per kind when assigning. */
|
|
8
|
+
const assignmentObjects = {
|
|
9
|
+
permissionSet: { sobject: 'PermissionSetAssignment', idField: 'PermissionSetId' },
|
|
10
|
+
permissionSetGroup: { sobject: 'PermissionSetAssignment', idField: 'PermissionSetGroupId' },
|
|
11
|
+
permissionSetLicense: { sobject: 'PermissionSetLicenseAssign', idField: 'PermissionSetLicenseId' },
|
|
12
|
+
};
|
|
13
|
+
/** The sObject Collections API caps each create/delete call at 200 records. */
|
|
14
|
+
const collectionBatchSize = 200;
|
|
15
|
+
/** Escape a value for safe inclusion in a SOQL string literal. */
|
|
16
|
+
function soqlLiteral(value) {
|
|
17
|
+
return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
18
|
+
}
|
|
19
|
+
/** Build a comma-separated, quoted IN list from the values. */
|
|
20
|
+
function inList(values) {
|
|
21
|
+
return values.map((value) => `'${soqlLiteral(value)}'`).join(', ');
|
|
22
|
+
}
|
|
23
|
+
/** Split items into chunks of at most `size`. */
|
|
24
|
+
function chunk(items, size) {
|
|
25
|
+
const chunks = [];
|
|
26
|
+
for (let index = 0; index < items.length; index += size) {
|
|
27
|
+
chunks.push(items.slice(index, index + size));
|
|
28
|
+
}
|
|
29
|
+
return chunks;
|
|
30
|
+
}
|
|
31
|
+
/** Turn a per-record DML result into a domain outcome, capturing the error message on failure. */
|
|
32
|
+
function outcomeOf(assignment, operation, result) {
|
|
33
|
+
const success = result?.success ?? false;
|
|
34
|
+
const message = result && !result.success ? result.errors.map((error) => error.message).join('; ') : undefined;
|
|
35
|
+
return {
|
|
36
|
+
assignee: assignment.assignee,
|
|
37
|
+
kind: assignment.kind,
|
|
38
|
+
target: assignment.target,
|
|
39
|
+
operation,
|
|
40
|
+
success,
|
|
41
|
+
message,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Group additions by sObject and chunk them for the Collections API, keeping each record's source. */
|
|
45
|
+
function additionBatches(additions) {
|
|
46
|
+
const bySobject = new Map();
|
|
47
|
+
for (const addition of additions) {
|
|
48
|
+
const { sobject } = assignmentObjects[addition.kind];
|
|
49
|
+
const grouped = bySobject.get(sobject) ?? [];
|
|
50
|
+
grouped.push(addition);
|
|
51
|
+
bySobject.set(sobject, grouped);
|
|
52
|
+
}
|
|
53
|
+
const batches = [];
|
|
54
|
+
for (const [sobject, grouped] of bySobject) {
|
|
55
|
+
for (const batch of chunk(grouped, collectionBatchSize)) {
|
|
56
|
+
const records = batch.map((addition) => ({
|
|
57
|
+
AssigneeId: addition.assigneeId,
|
|
58
|
+
[assignmentObjects[addition.kind].idField]: addition.targetId,
|
|
59
|
+
...(addition.expiration ? { ExpirationDate: addition.expiration } : {}),
|
|
60
|
+
}));
|
|
61
|
+
batches.push({ sobject, additions: batch, records });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return batches;
|
|
65
|
+
}
|
|
66
|
+
/** Group expiration updates by sObject and chunk them, building the Id + ExpirationDate records. */
|
|
67
|
+
function updateBatches(updates) {
|
|
68
|
+
const bySobject = new Map();
|
|
69
|
+
for (const update of updates) {
|
|
70
|
+
const { sobject } = assignmentObjects[update.kind];
|
|
71
|
+
const grouped = bySobject.get(sobject) ?? [];
|
|
72
|
+
grouped.push(update);
|
|
73
|
+
bySobject.set(sobject, grouped);
|
|
74
|
+
}
|
|
75
|
+
const batches = [];
|
|
76
|
+
for (const [sobject, grouped] of bySobject) {
|
|
77
|
+
for (const batch of chunk(grouped, collectionBatchSize)) {
|
|
78
|
+
const records = batch.map((update) => ({
|
|
79
|
+
Id: update.recordId,
|
|
80
|
+
ExpirationDate: update.expiration ?? null,
|
|
81
|
+
}));
|
|
82
|
+
batches.push({ sobject, updates: batch, records });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return batches;
|
|
86
|
+
}
|
|
87
|
+
/** Group removals by sObject and chunk them for the Collections API. */
|
|
88
|
+
function removalBatches(removals) {
|
|
89
|
+
const bySobject = new Map();
|
|
90
|
+
for (const removal of removals) {
|
|
91
|
+
const { sobject } = assignmentObjects[removal.kind];
|
|
92
|
+
const grouped = bySobject.get(sobject) ?? [];
|
|
93
|
+
grouped.push(removal);
|
|
94
|
+
bySobject.set(sobject, grouped);
|
|
95
|
+
}
|
|
96
|
+
const batches = [];
|
|
97
|
+
for (const [sobject, grouped] of bySobject) {
|
|
98
|
+
for (const batch of chunk(grouped, collectionBatchSize)) {
|
|
99
|
+
batches.push({ sobject, removals: batch });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return batches;
|
|
103
|
+
}
|
|
104
|
+
/** Adapter backing OrgClient with a Salesforce Connection. autoFetchQuery pages past 2000 rows. */
|
|
105
|
+
export class ConnectionOrgClient {
|
|
106
|
+
connection;
|
|
107
|
+
constructor(connection) {
|
|
108
|
+
this.connection = connection;
|
|
109
|
+
}
|
|
110
|
+
async findUsers(usernames) {
|
|
111
|
+
const records = await this.query(`SELECT Id, Username, IsActive FROM User WHERE Username IN (${inList(usernames)})`);
|
|
112
|
+
return records.map((record) => ({ id: record.Id, username: record.Username, isActive: record.IsActive }));
|
|
113
|
+
}
|
|
114
|
+
async findTargets(kind, names) {
|
|
115
|
+
const { sobject, field } = targetObjects[kind];
|
|
116
|
+
const records = await this.query(`SELECT Id, ${field} FROM ${sobject} WHERE ${field} IN (${inList(names)})`);
|
|
117
|
+
return records.map((record) => ({ id: record.Id, name: record[field] }));
|
|
118
|
+
}
|
|
119
|
+
async listAssignments() {
|
|
120
|
+
const [memberships, licenses] = await Promise.all([
|
|
121
|
+
this.query('SELECT Id, Assignee.Username, PermissionSet.Name, PermissionSetGroup.DeveloperName, PermissionSetGroupId, ExpirationDate ' +
|
|
122
|
+
'FROM PermissionSetAssignment ' +
|
|
123
|
+
'WHERE Assignee.IsActive = true AND PermissionSet.IsOwnedByProfile = false'),
|
|
124
|
+
this.query('SELECT Id, Assignee.Username, PermissionSetLicense.DeveloperName ' +
|
|
125
|
+
'FROM PermissionSetLicenseAssign ' +
|
|
126
|
+
'WHERE Assignee.IsActive = true'),
|
|
127
|
+
]);
|
|
128
|
+
const assignments = [];
|
|
129
|
+
for (const record of memberships) {
|
|
130
|
+
const expiration = record.ExpirationDate ? { expiration: record.ExpirationDate } : {};
|
|
131
|
+
if (record.PermissionSetGroupId && record.PermissionSetGroup) {
|
|
132
|
+
assignments.push({
|
|
133
|
+
assignee: record.Assignee.Username,
|
|
134
|
+
kind: 'permissionSetGroup',
|
|
135
|
+
target: record.PermissionSetGroup.DeveloperName,
|
|
136
|
+
...expiration,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
assignments.push({
|
|
141
|
+
assignee: record.Assignee.Username,
|
|
142
|
+
kind: 'permissionSet',
|
|
143
|
+
target: record.PermissionSet.Name,
|
|
144
|
+
...expiration,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
for (const record of licenses) {
|
|
149
|
+
assignments.push({
|
|
150
|
+
assignee: record.Assignee.Username,
|
|
151
|
+
kind: 'permissionSetLicense',
|
|
152
|
+
target: record.PermissionSetLicense.DeveloperName,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return assignments;
|
|
156
|
+
}
|
|
157
|
+
async currentAssignments(targets) {
|
|
158
|
+
const permissionSetIds = targets.filter((ref) => ref.kind === 'permissionSet').map((ref) => ref.id);
|
|
159
|
+
const groupIds = targets.filter((ref) => ref.kind === 'permissionSetGroup').map((ref) => ref.id);
|
|
160
|
+
const licenseIds = targets.filter((ref) => ref.kind === 'permissionSetLicense').map((ref) => ref.id);
|
|
161
|
+
const tasks = [];
|
|
162
|
+
const memberClauses = [];
|
|
163
|
+
if (permissionSetIds.length > 0)
|
|
164
|
+
memberClauses.push(`PermissionSetId IN (${inList(permissionSetIds)})`);
|
|
165
|
+
if (groupIds.length > 0)
|
|
166
|
+
memberClauses.push(`PermissionSetGroupId IN (${inList(groupIds)})`);
|
|
167
|
+
if (memberClauses.length > 0) {
|
|
168
|
+
const soql = 'SELECT Id, Assignee.Username, PermissionSet.Name, PermissionSetGroup.DeveloperName, PermissionSetGroupId, ExpirationDate ' +
|
|
169
|
+
`FROM PermissionSetAssignment WHERE ${memberClauses.join(' OR ')}`;
|
|
170
|
+
tasks.push(this.query(soql).then((records) => records.map((record) => {
|
|
171
|
+
const expiration = record.ExpirationDate ? { expiration: record.ExpirationDate } : {};
|
|
172
|
+
return record.PermissionSetGroupId && record.PermissionSetGroup
|
|
173
|
+
? {
|
|
174
|
+
recordId: record.Id,
|
|
175
|
+
assignee: record.Assignee.Username,
|
|
176
|
+
kind: 'permissionSetGroup',
|
|
177
|
+
target: record.PermissionSetGroup.DeveloperName,
|
|
178
|
+
...expiration,
|
|
179
|
+
}
|
|
180
|
+
: {
|
|
181
|
+
recordId: record.Id,
|
|
182
|
+
assignee: record.Assignee.Username,
|
|
183
|
+
kind: 'permissionSet',
|
|
184
|
+
target: record.PermissionSet.Name,
|
|
185
|
+
...expiration,
|
|
186
|
+
};
|
|
187
|
+
})));
|
|
188
|
+
}
|
|
189
|
+
if (licenseIds.length > 0) {
|
|
190
|
+
const soql = 'SELECT Id, Assignee.Username, PermissionSetLicense.DeveloperName ' +
|
|
191
|
+
`FROM PermissionSetLicenseAssign WHERE PermissionSetLicenseId IN (${inList(licenseIds)})`;
|
|
192
|
+
tasks.push(this.query(soql).then((records) => records.map((record) => ({
|
|
193
|
+
recordId: record.Id,
|
|
194
|
+
assignee: record.Assignee.Username,
|
|
195
|
+
kind: 'permissionSetLicense',
|
|
196
|
+
target: record.PermissionSetLicense.DeveloperName,
|
|
197
|
+
}))));
|
|
198
|
+
}
|
|
199
|
+
const results = await Promise.all(tasks);
|
|
200
|
+
return results.flat();
|
|
201
|
+
}
|
|
202
|
+
async addAssignments(additions) {
|
|
203
|
+
const batches = additionBatches(additions);
|
|
204
|
+
const settled = await Promise.all(batches.map((batch) => this.connection.create(batch.sobject, batch.records, { allOrNone: false }).then((results) => ({
|
|
205
|
+
batch,
|
|
206
|
+
results: results,
|
|
207
|
+
}))));
|
|
208
|
+
const outcomes = [];
|
|
209
|
+
for (const { batch, results } of settled) {
|
|
210
|
+
batch.additions.forEach((addition, index) => {
|
|
211
|
+
outcomes.push(outcomeOf(addition, 'add', results[index]));
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return outcomes;
|
|
215
|
+
}
|
|
216
|
+
async updateAssignments(updates) {
|
|
217
|
+
const batches = updateBatches(updates);
|
|
218
|
+
const settled = await Promise.all(batches.map((batch) => this.connection.update(batch.sobject, batch.records, { allOrNone: false }).then((results) => ({
|
|
219
|
+
batch,
|
|
220
|
+
results: results,
|
|
221
|
+
}))));
|
|
222
|
+
const outcomes = [];
|
|
223
|
+
for (const { batch, results } of settled) {
|
|
224
|
+
batch.updates.forEach((update, index) => {
|
|
225
|
+
outcomes.push(outcomeOf(update, 'update', results[index]));
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
return outcomes;
|
|
229
|
+
}
|
|
230
|
+
async removeAssignments(removals) {
|
|
231
|
+
const batches = removalBatches(removals);
|
|
232
|
+
const settled = await Promise.all(batches.map((batch) => this.connection
|
|
233
|
+
.destroy(batch.sobject, batch.removals.map((removal) => removal.recordId), { allOrNone: false })
|
|
234
|
+
.then((results) => ({ batch, results: results }))));
|
|
235
|
+
const outcomes = [];
|
|
236
|
+
for (const { batch, results } of settled) {
|
|
237
|
+
batch.removals.forEach((removal, index) => {
|
|
238
|
+
outcomes.push(outcomeOf(removal, 'remove', results[index]));
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
return outcomes;
|
|
242
|
+
}
|
|
243
|
+
async query(soql) {
|
|
244
|
+
const result = await this.connection.autoFetchQuery(soql);
|
|
245
|
+
return result.records;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=connection-org-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-org-client.js","sourceRoot":"","sources":["../../src/adapters/connection-org-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SfCommand } from '@salesforce/sf-plugins-core';
|
|
2
|
+
export type PsApplyResult = {
|
|
3
|
+
status: string;
|
|
4
|
+
toAdd: number;
|
|
5
|
+
toUpdate: number;
|
|
6
|
+
toRemove: number;
|
|
7
|
+
added: number;
|
|
8
|
+
updated: number;
|
|
9
|
+
removed: number;
|
|
10
|
+
failures: number;
|
|
11
|
+
};
|
|
12
|
+
export default class Apply extends SfCommand<PsApplyResult> {
|
|
13
|
+
static readonly summary: string;
|
|
14
|
+
static readonly description: string;
|
|
15
|
+
static readonly examples: string[];
|
|
16
|
+
static readonly flags: {
|
|
17
|
+
'target-org': import("@oclif/core/interfaces").OptionFlag<import("@salesforce/core").Org, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
file: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
mode: import("@oclif/core/interfaces").OptionFlag<"additive" | "destructive" | "sync", import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
'max-deletes': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
22
|
+
'no-prompt': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
23
|
+
};
|
|
24
|
+
run(): Promise<PsApplyResult>;
|
|
25
|
+
private reportDrift;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=apply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../src/commands/ps/apply.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAS,MAAM,6BAA6B,CAAC;AAU/D,MAAM,MAAM,aAAa,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,SAAS,CAAC,aAAa,CAAC;IACvD,gBAAuB,OAAO,SAAkC;IAChE,gBAAuB,WAAW,SAAsC;IACxE,gBAAuB,QAAQ,WAAoC;IAEnE,gBAAuB,KAAK;;;;;;;MAwB1B;IAEW,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC;IAiG1C,OAAO,CAAC,WAAW;CAKtB"}
|