src-auth-perms-sync 0.3.0__tar.gz → 0.3.1__tar.gz
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.
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/PKG-INFO +1 -1
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/dev/TODO.md +0 -16
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/dev/memory-efficiency-generate.py +1 -1
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/dev/memory-efficiency.md +40 -1
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/dev/test-end-to-end.py +9 -4
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/cli.py +154 -16
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/orgs/sync.py +129 -109
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/apply.py +119 -149
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/command.py +736 -110
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/full_set.py +180 -13
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/mapping.py +29 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/maps.py +1 -1
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/queries.py +95 -26
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/restore.py +13 -12
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/snapshot.py +261 -164
- src_auth_perms_sync-0.3.1/src/src_auth_perms_sync/permissions/sourcegraph.py +897 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/types.py +6 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/workflow.py +129 -6
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/shared/queries.py +17 -9
- src_auth_perms_sync-0.3.1/src/src_auth_perms_sync/shared/run_context.py +265 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/shared/saml_groups.py +2 -2
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/shared/site_config.py +10 -7
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/shared/sourcegraph.py +10 -2
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/tests/integration/test_cli_entrypoint.py +1 -1
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/tests/unit/test_cli_config.py +214 -13
- src_auth_perms_sync-0.3.1/tests/unit/test_command_additive.py +280 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/tests/unit/test_maps.py +72 -0
- src_auth_perms_sync-0.3.1/tests/unit/test_permissions_sourcegraph.py +272 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/tests/unit/test_restore.py +4 -8
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/tests/unit/test_snapshot.py +84 -18
- src_auth_perms_sync-0.3.0/src/src_auth_perms_sync/permissions/sourcegraph.py +0 -405
- src_auth_perms_sync-0.3.0/src/src_auth_perms_sync/shared/run_context.py +0 -34
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/.env.example +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/.github/CODEOWNERS +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/.github/workflows/ci.yml +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/.github/workflows/release.yml +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/.github/workflows/validate.yml +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/.gitignore +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/.markdownlint-cli2.yaml +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/.python-version +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/AGENTS.md +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/LICENSE +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/README.md +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/SECURITY.md +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/dev/audit-dead-code.md +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/dev/hooks/pre-commit +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/dev/mapping-efficiency.md +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/dev/memory-efficiency-analyze.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/dev/memory-efficiency-monitor-sourcegraph.sh +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/dev/update-python-versions.md +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/maps-example.yaml +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/pyproject.toml +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/renovate.json +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/__init__.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/__main__.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/orgs/__init__.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/orgs/command.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/orgs/queries.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/orgs/types.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/permissions/__init__.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/shared/__init__.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/shared/backups.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/src/src_auth_perms_sync/shared/types.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/tests/__init__.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/tests/integration/__init__.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/tests/unit/__init__.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/tests/unit/test_apply.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/tests/unit/test_backups.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/tests/unit/test_saml_groups.py +0 -0
- {src_auth_perms_sync-0.3.0 → src_auth_perms_sync-0.3.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: src-auth-perms-sync
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Set Sourcegraph permissions from authentication provider data
|
|
5
5
|
Project-URL: Homepage, https://github.com/sourcegraph/src-auth-perms-sync
|
|
6
6
|
Project-URL: Issues, https://github.com/sourcegraph/src-auth-perms-sync/issues
|
|
@@ -1,21 +1,5 @@
|
|
|
1
1
|
# TODO
|
|
2
2
|
|
|
3
|
-
## High priority: Sync modes
|
|
4
|
-
|
|
5
|
-
### Fast
|
|
6
|
-
|
|
7
|
-
- Additive modes, to add new users’ perms quickly,
|
|
8
|
-
without the extraneous load on the database of a full sync
|
|
9
|
-
- Take a list of usernames and/or email addresses as input,
|
|
10
|
-
query users on the instance for these,
|
|
11
|
-
then trigger a perms sync for found users
|
|
12
|
-
- Query the instance for all new users, which do not yet have explicit perms
|
|
13
|
-
- Query the instance for all new repos, which do not yet have explicit perms
|
|
14
|
-
|
|
15
|
-
### Full: Overwrite all perms
|
|
16
|
-
|
|
17
|
-
- Separate full sync mode with an arg
|
|
18
|
-
|
|
19
3
|
## High priority: Remote trigger on demand
|
|
20
4
|
|
|
21
5
|
- Sourcegraph webhook for new user coming in v7.4.0
|
|
@@ -551,7 +551,7 @@ def list_external_services(client: src.SourcegraphClient) -> list[ExternalServic
|
|
|
551
551
|
)
|
|
552
552
|
)
|
|
553
553
|
if not services:
|
|
554
|
-
raise SystemExit("No
|
|
554
|
+
raise SystemExit("No code hosts found on the Sourcegraph instance")
|
|
555
555
|
return services
|
|
556
556
|
|
|
557
557
|
|
|
@@ -298,6 +298,39 @@ Important requirements:
|
|
|
298
298
|
Expected benefit: replace hundreds or thousands of per-repo resolver SQL spans
|
|
299
299
|
per request with one indexed `user_repo_permissions` join per user batch.
|
|
300
300
|
|
|
301
|
+
The `get --users-without-explicit-perms` path also needs a cheaper presence
|
|
302
|
+
check. Today it has to ask
|
|
303
|
+
`User.permissionsInfo.repositories(source: API, first: 1)` for every candidate
|
|
304
|
+
user, in aliased batches. Recent test runs show the client can parallelize
|
|
305
|
+
those batches, but the Sourcegraph frontend / load balancer can still return
|
|
306
|
+
502/503s under that resolver load. Add one or both direct APIs:
|
|
307
|
+
|
|
308
|
+
```graphql
|
|
309
|
+
type ExplicitRepositoryPermissionPresence {
|
|
310
|
+
userID: ID!
|
|
311
|
+
hasExplicitRepositoryPermissions: Boolean!
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
extend type Query {
|
|
315
|
+
explicitRepositoryPermissionPresenceForUsers(
|
|
316
|
+
userIDs: [ID!]!
|
|
317
|
+
source: PermissionSource = API
|
|
318
|
+
): [ExplicitRepositoryPermissionPresence!]!
|
|
319
|
+
|
|
320
|
+
usersWithoutExplicitRepositoryPermissions(
|
|
321
|
+
createdAt: DateTimeFilter
|
|
322
|
+
source: PermissionSource = API
|
|
323
|
+
first: Int
|
|
324
|
+
after: String
|
|
325
|
+
): UserConnection!
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Expected benefit: `src-auth-perms-sync get --users-without-explicit-perms`
|
|
330
|
+
can either check explicit-permission presence for candidate users in one indexed
|
|
331
|
+
batch query, or ask Sourcegraph for the filtered user set directly instead of
|
|
332
|
+
probing every user through the expensive permissions connection resolver.
|
|
333
|
+
|
|
301
334
|
The stress profile also needs attention on the write path. A purpose-built
|
|
302
335
|
bulk overwrite API that accepts many repo/user edges at once, streams or stages
|
|
303
336
|
the input server-side, and avoids repeated per-repo permission reconciliation
|
|
@@ -324,7 +357,10 @@ Request: add a bulk explicit-permissions read API that accepts many user IDs and
|
|
|
324
357
|
returns compact permission edges (`userID`, `repositoryID`, `repositoryName`,
|
|
325
358
|
`updatedAt`) for `source: API`, without resolving full `Repository` GraphQL
|
|
326
359
|
objects. A single indexed query over `user_repo_permissions` joined to `repo`
|
|
327
|
-
should be enough for each user batch.
|
|
360
|
+
should be enough for each user batch. Also add a cheaper presence/filter path
|
|
361
|
+
for `get --users-without-explicit-perms`: either `userID -> has explicit API
|
|
362
|
+
repo permissions` for many users, or a direct query for users without explicit
|
|
363
|
+
API repo permissions, optionally filtered by `createdAt`.
|
|
328
364
|
|
|
329
365
|
Acceptance criteria:
|
|
330
366
|
|
|
@@ -336,6 +372,9 @@ Acceptance criteria:
|
|
|
336
372
|
latency visible.
|
|
337
373
|
- `src-auth-perms-sync` can replace its aliased
|
|
338
374
|
`User.permissionsInfo.repositories(source: API)` calls with this API.
|
|
375
|
+
- `src-auth-perms-sync get --users-without-explicit-perms` can stop probing
|
|
376
|
+
every candidate user through `User.permissionsInfo.repositories(source: API,
|
|
377
|
+
first: 1)`.
|
|
339
378
|
- Follow-up: evaluate a bulk overwrite API for large full-set applies. The
|
|
340
379
|
stress run planned roughly 10 million grants and observed
|
|
341
380
|
`permsStore.upsertUserRepoPermissions-range1` averaging about 2.5s per call.
|
|
@@ -1575,6 +1575,12 @@ def invalid_configuration_cases(config: EndToEndConfig) -> list[CommandCase]:
|
|
|
1575
1575
|
expected_exit_code=2,
|
|
1576
1576
|
must_contain=("choose at most one",),
|
|
1577
1577
|
),
|
|
1578
|
+
CommandCase(
|
|
1579
|
+
name="invalid-set-full-and-created-after",
|
|
1580
|
+
arguments=("set", "--full", "--created-after", config.future_date),
|
|
1581
|
+
expected_exit_code=2,
|
|
1582
|
+
must_contain=("--full cannot be combined with --created-after",),
|
|
1583
|
+
),
|
|
1578
1584
|
CommandCase(
|
|
1579
1585
|
name="invalid-user-filter-conflict",
|
|
1580
1586
|
arguments=("get", "--users", config.user, "--users-without-explicit-perms"),
|
|
@@ -1682,10 +1688,9 @@ def read_only_cases(config: EndToEndConfig) -> list[CommandCase]:
|
|
|
1682
1688
|
def run_safe_set_cases(config: EndToEndConfig, runner: CommandPermutationRunner) -> None:
|
|
1683
1689
|
runner.run(
|
|
1684
1690
|
CommandCase(
|
|
1685
|
-
name="set-
|
|
1691
|
+
name="set-created-after-no-op-apply",
|
|
1686
1692
|
arguments=(
|
|
1687
1693
|
"set",
|
|
1688
|
-
"--full",
|
|
1689
1694
|
"--created-after",
|
|
1690
1695
|
config.future_date,
|
|
1691
1696
|
"--apply",
|
|
@@ -1693,8 +1698,8 @@ def run_safe_set_cases(config: EndToEndConfig, runner: CommandPermutationRunner)
|
|
|
1693
1698
|
"--parallelism",
|
|
1694
1699
|
str(config.parallelism),
|
|
1695
1700
|
),
|
|
1696
|
-
expected_log_command="
|
|
1697
|
-
must_contain=("No
|
|
1701
|
+
expected_log_command="set_created_after",
|
|
1702
|
+
must_contain=("No users selected",),
|
|
1698
1703
|
)
|
|
1699
1704
|
)
|
|
1700
1705
|
|
|
@@ -47,6 +47,10 @@ GET_CONFIG_FIELDS = src.config_field_names(
|
|
|
47
47
|
"users",
|
|
48
48
|
"users_without_explicit_perms",
|
|
49
49
|
"created_after",
|
|
50
|
+
"repos",
|
|
51
|
+
"repos_without_explicit_perms",
|
|
52
|
+
"repos_created_after",
|
|
53
|
+
"no_backup",
|
|
50
54
|
"explicit_permissions_batch_size",
|
|
51
55
|
*COMMON_CONFIG_FIELDS,
|
|
52
56
|
)
|
|
@@ -56,6 +60,9 @@ SET_CONFIG_FIELDS = src.config_field_names(
|
|
|
56
60
|
"users",
|
|
57
61
|
"users_without_explicit_perms",
|
|
58
62
|
"created_after",
|
|
63
|
+
"repos",
|
|
64
|
+
"repos_without_explicit_perms",
|
|
65
|
+
"repos_created_after",
|
|
59
66
|
"sync_saml_organizations",
|
|
60
67
|
"apply",
|
|
61
68
|
"no_backup",
|
|
@@ -79,27 +86,47 @@ LogCommandName: TypeAlias = Literal[
|
|
|
79
86
|
"set_full",
|
|
80
87
|
"set_users",
|
|
81
88
|
"set_users_without_explicit_perms",
|
|
89
|
+
"set_created_after",
|
|
90
|
+
"set_repos",
|
|
91
|
+
"set_repos_without_explicit_perms",
|
|
92
|
+
"set_repos_created_after",
|
|
82
93
|
"restore",
|
|
83
94
|
"sync_saml_orgs",
|
|
84
95
|
"set_full_sync_saml_orgs",
|
|
85
96
|
"set_users_sync_saml_orgs",
|
|
86
97
|
"set_users_without_explicit_perms_sync_saml_orgs",
|
|
98
|
+
"set_created_after_sync_saml_orgs",
|
|
99
|
+
"set_repos_sync_saml_orgs",
|
|
100
|
+
"set_repos_without_explicit_perms_sync_saml_orgs",
|
|
101
|
+
"set_repos_created_after_sync_saml_orgs",
|
|
87
102
|
]
|
|
88
103
|
|
|
89
104
|
SET_COMMAND_LOG_NAMES: dict[permission_types.SetCommandMode, LogCommandName] = {
|
|
90
105
|
"full": "set_full",
|
|
91
106
|
"users": "set_users",
|
|
92
107
|
"users_without_explicit_perms": "set_users_without_explicit_perms",
|
|
108
|
+
"created_after": "set_created_after",
|
|
109
|
+
"repos": "set_repos",
|
|
110
|
+
"repos_without_explicit_perms": "set_repos_without_explicit_perms",
|
|
111
|
+
"repos_created_after": "set_repos_created_after",
|
|
93
112
|
}
|
|
94
113
|
SET_COMMAND_ARTIFACT_NAMES: dict[permission_types.SetCommandMode, str] = {
|
|
95
114
|
"full": "set-{run_mode}",
|
|
96
115
|
"users": "set-add-users-{run_mode}",
|
|
97
116
|
"users_without_explicit_perms": "set-add-users-without-explicit-perms-{run_mode}",
|
|
117
|
+
"created_after": "set-add-users-created-after-{run_mode}",
|
|
118
|
+
"repos": "set-repos-{run_mode}",
|
|
119
|
+
"repos_without_explicit_perms": "set-repos-without-explicit-perms-{run_mode}",
|
|
120
|
+
"repos_created_after": "set-repos-created-after-{run_mode}",
|
|
98
121
|
}
|
|
99
122
|
SYNC_SET_COMMAND_LOG_NAMES: dict[permission_types.SetCommandMode, LogCommandName] = {
|
|
100
123
|
"full": "set_full_sync_saml_orgs",
|
|
101
124
|
"users": "set_users_sync_saml_orgs",
|
|
102
125
|
"users_without_explicit_perms": "set_users_without_explicit_perms_sync_saml_orgs",
|
|
126
|
+
"created_after": "set_created_after_sync_saml_orgs",
|
|
127
|
+
"repos": "set_repos_sync_saml_orgs",
|
|
128
|
+
"repos_without_explicit_perms": "set_repos_without_explicit_perms_sync_saml_orgs",
|
|
129
|
+
"repos_created_after": "set_repos_created_after_sync_saml_orgs",
|
|
103
130
|
}
|
|
104
131
|
SYNC_SET_COMMAND_ARTIFACT_NAMES: dict[permission_types.SetCommandMode, str] = {
|
|
105
132
|
"full": "set-sync-saml-orgs-{run_mode}",
|
|
@@ -107,6 +134,10 @@ SYNC_SET_COMMAND_ARTIFACT_NAMES: dict[permission_types.SetCommandMode, str] = {
|
|
|
107
134
|
"users_without_explicit_perms": (
|
|
108
135
|
"set-add-users-without-explicit-perms-sync-saml-orgs-{run_mode}"
|
|
109
136
|
),
|
|
137
|
+
"created_after": "set-add-users-created-after-sync-saml-orgs-{run_mode}",
|
|
138
|
+
"repos": "set-repos-sync-saml-orgs-{run_mode}",
|
|
139
|
+
"repos_without_explicit_perms": ("set-repos-without-explicit-perms-sync-saml-orgs-{run_mode}"),
|
|
140
|
+
"repos_created_after": "set-repos-created-after-sync-saml-orgs-{run_mode}",
|
|
110
141
|
}
|
|
111
142
|
|
|
112
143
|
|
|
@@ -178,7 +209,10 @@ class Config(src.SourcegraphClientConfig, src.LoggingConfig, src.OpenTelemetryCo
|
|
|
178
209
|
env_var="SRC_AUTH_PERMS_SYNC_FULL",
|
|
179
210
|
cli_flag="--full",
|
|
180
211
|
cli_action="store_true",
|
|
181
|
-
help=
|
|
212
|
+
help=(
|
|
213
|
+
"With the set command: run full overwrite reconciliation "
|
|
214
|
+
"(default only when no user filter is set)"
|
|
215
|
+
),
|
|
182
216
|
help_group="Permission sync",
|
|
183
217
|
)
|
|
184
218
|
users: tuple[str, ...] = src.config_field(
|
|
@@ -206,6 +240,31 @@ class Config(src.SourcegraphClientConfig, src.LoggingConfig, src.OpenTelemetryCo
|
|
|
206
240
|
help="Process Sourcegraph users created on or after this date",
|
|
207
241
|
help_group="User filters",
|
|
208
242
|
)
|
|
243
|
+
repos: tuple[str, ...] = src.config_field(
|
|
244
|
+
default=(),
|
|
245
|
+
env_var="SRC_AUTH_PERMS_SYNC_REPOS",
|
|
246
|
+
cli_flag="--repos",
|
|
247
|
+
metavar="REPOS",
|
|
248
|
+
help="Process comma-delimited Sourcegraph repository names",
|
|
249
|
+
help_group="Repo filters",
|
|
250
|
+
)
|
|
251
|
+
repos_without_explicit_perms: bool = src.config_field(
|
|
252
|
+
default=False,
|
|
253
|
+
env_var="SRC_AUTH_PERMS_SYNC_REPOS_WITHOUT_EXPLICIT_PERMS",
|
|
254
|
+
cli_flag="--repos-without-explicit-perms",
|
|
255
|
+
cli_action="store_true",
|
|
256
|
+
help="Process Sourcegraph repositories without explicit permissions",
|
|
257
|
+
help_group="Repo filters",
|
|
258
|
+
)
|
|
259
|
+
repos_created_after: str | None = src.config_field(
|
|
260
|
+
default=None,
|
|
261
|
+
env_var="SRC_AUTH_PERMS_SYNC_REPOS_CREATED_AFTER",
|
|
262
|
+
cli_flag="--repos-created-after",
|
|
263
|
+
metavar="YYYY-MM-DD",
|
|
264
|
+
pattern=r"^\d{4}-\d{2}-\d{2}$",
|
|
265
|
+
help="Process Sourcegraph repositories created on or after this date",
|
|
266
|
+
help_group="Repo filters",
|
|
267
|
+
)
|
|
209
268
|
sync_saml_organizations: bool = src.config_field(
|
|
210
269
|
default=False,
|
|
211
270
|
env_var="SRC_AUTH_PERMS_SYNC_SYNC_SAML_ORGS",
|
|
@@ -227,7 +286,7 @@ class Config(src.SourcegraphClientConfig, src.LoggingConfig, src.OpenTelemetryCo
|
|
|
227
286
|
env_var="SRC_AUTH_PERMS_SYNC_NO_BACKUP",
|
|
228
287
|
cli_flag="--no-backup",
|
|
229
288
|
cli_action="store_true",
|
|
230
|
-
help="
|
|
289
|
+
help="Skip before/after snapshot artifacts and validation where supported",
|
|
231
290
|
help_group="Mutation",
|
|
232
291
|
)
|
|
233
292
|
parallelism: int = src.config_field(
|
|
@@ -329,6 +388,7 @@ def validate_config(command_name: CommandName, config: Config) -> None:
|
|
|
329
388
|
"""Validate cross-field CLI/config constraints."""
|
|
330
389
|
validate_command_options(command_name, config)
|
|
331
390
|
validate_user_filter_selection(command_name, config)
|
|
391
|
+
validate_repository_filter_selection(command_name, config)
|
|
332
392
|
validate_set_mode_selection(command_name, config)
|
|
333
393
|
|
|
334
394
|
|
|
@@ -336,8 +396,6 @@ def validate_command_options(command_name: CommandName, config: Config) -> None:
|
|
|
336
396
|
"""Validate options that only make sense with specific commands."""
|
|
337
397
|
if command_name == "get" and config.apply:
|
|
338
398
|
config_error("--apply cannot be used with the read-only get command")
|
|
339
|
-
if command_name == "get" and config.no_backup:
|
|
340
|
-
config_error("--no-backup cannot be used with the read-only get command")
|
|
341
399
|
if config.sync_saml_organizations and command_name != "set":
|
|
342
400
|
config_error("--sync-saml-orgs can only be combined with set")
|
|
343
401
|
if command_name == "restore" and config.restore_path is None:
|
|
@@ -360,6 +418,34 @@ def validate_user_filter_selection(command_name: CommandName, config: Config) ->
|
|
|
360
418
|
)
|
|
361
419
|
|
|
362
420
|
|
|
421
|
+
def validate_repository_filter_selection(command_name: CommandName, config: Config) -> None:
|
|
422
|
+
"""Validate repo-scope filters and their compatible commands."""
|
|
423
|
+
repository_filter_count = sum(
|
|
424
|
+
(
|
|
425
|
+
bool(config.repos),
|
|
426
|
+
config.repos_without_explicit_perms,
|
|
427
|
+
config.repos_created_after is not None,
|
|
428
|
+
)
|
|
429
|
+
)
|
|
430
|
+
if repository_filter_count > 1:
|
|
431
|
+
config_error(
|
|
432
|
+
"choose only one of --repos, --repos-without-explicit-perms, or --repos-created-after"
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
repository_filter_selected = repository_filter_count > 0
|
|
436
|
+
repository_filter_allowed = command_name in {"get", "set"}
|
|
437
|
+
if repository_filter_selected and not repository_filter_allowed:
|
|
438
|
+
config_error(
|
|
439
|
+
"--repos, --repos-without-explicit-perms, and --repos-created-after require get or set"
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
user_filter_selected = any(
|
|
443
|
+
(bool(config.users), config.users_without_explicit_perms, config.created_after is not None)
|
|
444
|
+
)
|
|
445
|
+
if repository_filter_selected and user_filter_selected:
|
|
446
|
+
config_error("choose either user filters or repo filters, not both")
|
|
447
|
+
|
|
448
|
+
|
|
363
449
|
def validate_set_mode_selection(command_name: CommandName, config: Config) -> None:
|
|
364
450
|
"""Validate set command mode flags."""
|
|
365
451
|
if config.full and command_name != "set":
|
|
@@ -368,9 +454,29 @@ def validate_set_mode_selection(command_name: CommandName, config: Config) -> No
|
|
|
368
454
|
if command_name != "set":
|
|
369
455
|
return
|
|
370
456
|
|
|
371
|
-
if
|
|
457
|
+
if config.full and config.created_after is not None:
|
|
458
|
+
config_error(
|
|
459
|
+
"--full cannot be combined with --created-after because full mode "
|
|
460
|
+
"overwrites mapped repos; omit --full to add grants for new users"
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
if (
|
|
464
|
+
sum(
|
|
465
|
+
(
|
|
466
|
+
config.full,
|
|
467
|
+
bool(config.users),
|
|
468
|
+
config.users_without_explicit_perms,
|
|
469
|
+
bool(config.repos),
|
|
470
|
+
config.repos_without_explicit_perms,
|
|
471
|
+
config.repos_created_after is not None,
|
|
472
|
+
)
|
|
473
|
+
)
|
|
474
|
+
> 1
|
|
475
|
+
):
|
|
372
476
|
config_error(
|
|
373
|
-
"with set, choose at most one of --full, --users,
|
|
477
|
+
"with set, choose at most one of --full, --users, "
|
|
478
|
+
"--users-without-explicit-perms, --repos, "
|
|
479
|
+
"--repos-without-explicit-perms, or --repos-created-after"
|
|
374
480
|
)
|
|
375
481
|
|
|
376
482
|
|
|
@@ -387,9 +493,27 @@ def set_command_options(config: Config) -> permission_types.SetCommandOptions:
|
|
|
387
493
|
mode="users_without_explicit_perms",
|
|
388
494
|
user_created_after=config.created_after,
|
|
389
495
|
)
|
|
496
|
+
if config.created_after is not None:
|
|
497
|
+
return permission_types.SetCommandOptions(
|
|
498
|
+
mode="created_after",
|
|
499
|
+
user_created_after=config.created_after,
|
|
500
|
+
)
|
|
501
|
+
if config.repos:
|
|
502
|
+
return permission_types.SetCommandOptions(
|
|
503
|
+
mode="repos",
|
|
504
|
+
repository_names=config.repos,
|
|
505
|
+
)
|
|
506
|
+
if config.repos_without_explicit_perms:
|
|
507
|
+
return permission_types.SetCommandOptions(
|
|
508
|
+
mode="repos_without_explicit_perms",
|
|
509
|
+
)
|
|
510
|
+
if config.repos_created_after is not None:
|
|
511
|
+
return permission_types.SetCommandOptions(
|
|
512
|
+
mode="repos_created_after",
|
|
513
|
+
repository_created_after=config.repos_created_after,
|
|
514
|
+
)
|
|
390
515
|
return permission_types.SetCommandOptions(
|
|
391
516
|
mode="full",
|
|
392
|
-
user_created_after=config.created_after,
|
|
393
517
|
)
|
|
394
518
|
|
|
395
519
|
|
|
@@ -507,12 +631,7 @@ def require_set_input_file(maps_path: Path) -> None:
|
|
|
507
631
|
|
|
508
632
|
def run_fields(config: Config, command: ResolvedCommand, endpoint: str) -> dict[str, object]:
|
|
509
633
|
"""Return run-level fields for structured logging."""
|
|
510
|
-
|
|
511
|
-
"cli_cmd": command.log_name,
|
|
512
|
-
"base_cmd": command.name,
|
|
513
|
-
"set_mode": command.set_mode,
|
|
514
|
-
"sync_saml_orgs_flag": command.sync_saml_organizations,
|
|
515
|
-
"apply_flag": config.apply,
|
|
634
|
+
fields: dict[str, object] = {
|
|
516
635
|
"endpoint": endpoint,
|
|
517
636
|
"parallelism": config.parallelism,
|
|
518
637
|
"explicit_permissions_batch_size": config.explicit_permissions_batch_size,
|
|
@@ -520,13 +639,28 @@ def run_fields(config: Config, command: ResolvedCommand, endpoint: str) -> dict[
|
|
|
520
639
|
"open_telemetry": config.open_telemetry,
|
|
521
640
|
"max_attempts": config.max_attempts,
|
|
522
641
|
"http_timeout_seconds": config.http_timeout_seconds,
|
|
523
|
-
"no_backup": config.no_backup,
|
|
524
642
|
"sample_interval": config.sample_interval,
|
|
525
|
-
"user_created_after": config.created_after,
|
|
526
643
|
"artifacts_dir": str(backups.endpoint_artifacts_directory(endpoint)),
|
|
527
644
|
"python_version": sys.version.split()[0],
|
|
528
645
|
"pid": os.getpid(),
|
|
529
646
|
}
|
|
647
|
+
if command.name != "get":
|
|
648
|
+
fields["apply"] = config.apply
|
|
649
|
+
if config.no_backup:
|
|
650
|
+
fields["no_backup"] = True
|
|
651
|
+
if command.set_mode is not None:
|
|
652
|
+
fields["set_mode"] = command.set_mode
|
|
653
|
+
if command.sync_saml_organizations:
|
|
654
|
+
fields["sync_saml_orgs"] = True
|
|
655
|
+
if config.created_after is not None:
|
|
656
|
+
fields["created_after"] = config.created_after
|
|
657
|
+
if config.repos:
|
|
658
|
+
fields["repos"] = config.repos
|
|
659
|
+
if config.repos_without_explicit_perms:
|
|
660
|
+
fields["repos_without_explicit_perms"] = True
|
|
661
|
+
if config.repos_created_after is not None:
|
|
662
|
+
fields["repos_created_after"] = config.repos_created_after
|
|
663
|
+
return fields
|
|
530
664
|
|
|
531
665
|
|
|
532
666
|
def run_with_client(
|
|
@@ -683,6 +817,9 @@ def run_get(
|
|
|
683
817
|
user_identifiers=config.users,
|
|
684
818
|
users_without_explicit_perms=config.users_without_explicit_perms,
|
|
685
819
|
user_created_after=config.created_after,
|
|
820
|
+
repository_names=config.repos,
|
|
821
|
+
repositories_without_explicit_perms=config.repos_without_explicit_perms,
|
|
822
|
+
repository_created_after=config.repos_created_after,
|
|
686
823
|
parallelism=config.parallelism,
|
|
687
824
|
explicit_permissions_batch_size=config.explicit_permissions_batch_size,
|
|
688
825
|
bind_id_mode=sourcegraph_site_config.bind_id_mode,
|
|
@@ -690,6 +827,7 @@ def run_get(
|
|
|
690
827
|
sourcegraph_site_config.saml_groups_attribute_name_by_config_id
|
|
691
828
|
),
|
|
692
829
|
auth_providers_by_config_id=sourcegraph_site_config.auth_providers_by_config_id,
|
|
830
|
+
do_backup=not config.no_backup,
|
|
693
831
|
retain_saml_group_users=False,
|
|
694
832
|
worker_pool=worker_pool,
|
|
695
833
|
)
|
|
@@ -767,7 +905,7 @@ def _run_or_raise(command_name: CommandName, config: Config) -> None:
|
|
|
767
905
|
backups.run_artifacts_context(run_directory, run_timestamp),
|
|
768
906
|
src.logging(
|
|
769
907
|
config,
|
|
770
|
-
command=command.
|
|
908
|
+
command=command.name,
|
|
771
909
|
git_cwd=__file__,
|
|
772
910
|
logging_config=logging_settings,
|
|
773
911
|
run_fields=run_fields(config, command, endpoint),
|