Skip to content

Commit 2d2a567

Browse files
committed
feat: add scopes
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
1 parent c72f25b commit 2d2a567

3 files changed

Lines changed: 95 additions & 56 deletions

File tree

backend/src/api/public/v1/akrites-external/index.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -51,66 +51,66 @@ const contactIngestRateLimiter = envTunableRateLimiter(
5151
export function akritesExternalRouter(): Router {
5252
const router = Router()
5353

54-
// TODO: swap for a dedicated cdp:packages:read scope once Akrites gets its own
55-
// Auth0 M2M scopes (per the akrites-external draft contract) — reusing the
56-
// internal CDP UI scopes for now since that's what's actually issued today.
54+
// Any one of the dedicated Akrites scope or the old Self Serve scopes works for now —
55+
// drop READ_PACKAGES/READ_STEWARDSHIPS once Akrites cuts over.
5756
const packagesSubRouter = Router()
5857
packagesSubRouter.use(rateLimiter)
59-
packagesSubRouter.use(requireScopes([SCOPES.READ_PACKAGES, SCOPES.READ_STEWARDSHIPS], 'all'))
58+
packagesSubRouter.use(
59+
requireScopes(
60+
[SCOPES.READ_AKRITES_PACKAGES, SCOPES.READ_PACKAGES, SCOPES.READ_STEWARDSHIPS],
61+
'any',
62+
),
63+
)
6064
packagesSubRouter.get('/detail', safeWrap(getAkritesExternalPackageDetail))
6165
packagesSubRouter.post(/^\/detail:batch\/?$/, safeWrap(getAkritesExternalPackageDetailBatch))
6266
router.use('/packages', packagesSubRouter)
6367

64-
// TODO: the contract gates advisories behind a dedicated read:advisories scope
65-
// (see the scope-naming note in the akrites-external OpenAPI). That scope isn't
66-
// issued by Auth0 yet, so reuse READ_PACKAGES for now — advisories are package
67-
// security data and, unlike the packages endpoints above, need no stewardship read.
68+
// Dedicated read:akrites-advisories, or Self Serve's read:packages as a
69+
// fallback until Akrites cuts over — drop it then.
70+
const advisoriesScopes = [SCOPES.READ_PACKAGES, SCOPES.READ_AKRITES_ADVISORIES]
6871
const advisoriesSubRouter = Router()
6972
advisoriesSubRouter.use(rateLimiter)
70-
advisoriesSubRouter.use(requireScopes([SCOPES.READ_PACKAGES]))
73+
advisoriesSubRouter.use(requireScopes(advisoriesScopes, 'any'))
7174
advisoriesSubRouter.get('/detail', safeWrap(getAkritesExternalAdvisoryDetail))
7275
advisoriesSubRouter.post(/^\/detail:batch\/?$/, safeWrap(getAkritesExternalAdvisoryDetailBatch))
7376
router.use('/advisories', advisoriesSubRouter)
7477

75-
// Security contacts expose contact PII (e.g. reporter emails), so the contract gates
76-
// them behind a dedicated cdp:maintainers:read scope and explicitly forbids reaching
77-
// them via the packages scope. That scope isn't issued by Auth0 yet, so reuse the
78-
// closest issued one — READ_MAINTAINER_ROLES (maintainer data) — NOT READ_PACKAGES.
79-
// TODO: swap for cdp:maintainers:read once issued.
78+
// Contact PII stays behind a dedicated scope, never the packages scope: dedicated
79+
// read:akrites-maintainers, or Self Serve's read:maintainer-roles as a fallback.
80+
//
8081
// requireScopes is applied per-route (not router-level) so each route can put its own
8182
// rate limiter *before* the scope check — failed-auth requests still count against that
8283
// route's quota — without forcing every route in this subrouter onto the same limiter
8384
// instance. /ingest gets its own dedicated contactIngestRateLimiter instead of sharing
8485
// the read endpoints' quota, matching the blast-radius jobs endpoint below.
85-
const contactsScopes = [SCOPES.READ_MAINTAINER_ROLES]
86+
const contactsScopes = [SCOPES.READ_MAINTAINER_ROLES, SCOPES.READ_AKRITES_MAINTAINERS]
8687
const contactsSubRouter = Router()
8788
contactsSubRouter.get(
8889
'/detail',
8990
rateLimiter,
90-
requireScopes(contactsScopes),
91+
requireScopes(contactsScopes, 'any'),
9192
safeWrap(getAkritesExternalContactDetail),
9293
)
9394
contactsSubRouter.post(
9495
/^\/detail:batch\/?$/,
9596
rateLimiter,
96-
requireScopes(contactsScopes),
97+
requireScopes(contactsScopes, 'any'),
9798
safeWrap(getAkritesExternalContactDetailBatch),
9899
)
99100
// Sync, single-purl on-demand ingest — starts a Temporal workflow and blocks a while,
100101
// so it gets the dedicated contactIngestRateLimiter, not the shared rateLimiter above.
101102
contactsSubRouter.post(
102103
'/ingest',
103104
contactIngestRateLimiter,
104-
requireScopes(contactsScopes),
105+
requireScopes(contactsScopes, 'any'),
105106
safeWrap(ingestAkritesExternalContactDetail),
106107
)
107108
router.use('/contacts', contactsSubRouter)
108109

109-
// TODO: the contract gates blast-radius behind a dedicated read:advisories scope
110-
// (same as advisories above — see the scope-naming note in the akrites-external
111-
// OpenAPI). Not issued by Auth0 yet, so reuse READ_PACKAGES for now.
110+
// Same underlying data as advisories above, same scopes: read:akrites-advisories,
111+
// or Self Serve's read:packages as a fallback until Akrites cuts over.
112112
const blastRadiusSubRouter = Router()
113-
blastRadiusSubRouter.use(requireScopes([SCOPES.READ_PACKAGES]))
113+
blastRadiusSubRouter.use(requireScopes(advisoriesScopes, 'any'))
114114
blastRadiusSubRouter.post('/jobs', blastRadiusRateLimiter, safeWrap(submitBlastRadiusJob))
115115
// Bulk submit multiplies Temporal workflow starts per request (up to
116116
// MAX_BLAST_RADIUS_JOBS_PER_BATCH), so it sits behind the same strict

backend/src/api/public/v1/akrites-external/openapi.yaml

Lines changed: 70 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,38 +16,47 @@ info:
1616
7-day result cache is specced separately and not yet built.
1717
1818
19-
TODO: scopes below (read:packages, read:stewardships) are the existing
20-
internal CDP UI scopes, reused here for now. Swap for a dedicated
21-
cdp:packages:read scope once Akrites gets its own Auth0 M2M scopes per
22-
the akrites-external draft contract.
19+
Auth0 now issues a dedicated Akrites Enclave M2M client with its own
20+
read:akrites-packages / read:akrites-advisories / read:akrites-maintainers
21+
scopes. Until Akrites cuts over to that client's credentials, the Self Serve
22+
client's existing scopes (read:packages, read:stewardships,
23+
read:maintainer-roles) still work on every operation below — each operation
24+
lists both the new dedicated scope(s) and the old fallback scope(s) as
25+
alternatives. Drop the old alternative once the cutover is confirmed.
2326
2427
servers:
2528
- url: https://cm.lfx.dev/api/v1
2629
description: Production
2730

2831
security:
32+
- M2MBearer:
33+
- read:akrites-packages
2934
- M2MBearer:
3035
- read:packages
36+
- M2MBearer:
3137
- read:stewardships
3238

3339
tags:
3440
- name: Packages
35-
description: Package detail — requires read:packages and read:stewardships (see TODO above).
41+
description: >
42+
Package detail — requires read:akrites-packages (dedicated Akrites Enclave
43+
scope), or either read:packages or read:stewardships from the old Self Serve
44+
client as a fallback until the cutover completes.
3645
- name: Advisories
3746
description: >
38-
Security advisories for a package, split out of package detail. The draft
39-
contract gates these behind a dedicated read:advisories scope; until Auth0
40-
issues it, the implementation reuses read:packages (advisories need no
41-
stewardship read). Confirm the final scope name (read:advisories vs
42-
cdp:advisories:read) with Akrites/product.
47+
Security advisories for a package, split out of package detail. Requires
48+
read:akrites-advisories (dedicated Akrites Enclave scope), or read:packages
49+
from the old Self Serve client as a fallback until the cutover completes
50+
(advisories need no stewardship read).
4351
- name: Contacts
4452
description: >
4553
Security contacts for a package — includes contact PII (e.g. reporter
46-
emails). The contract gates these behind a dedicated cdp:maintainers:read
47-
scope and forbids reaching them via the packages scope; until Auth0 issues
48-
it, the implementation requires read:maintainer-roles (NOT read:packages).
49-
The response shape is still under discussion upstream (reportingMethods /
50-
reportingGuidelines / integrationHints are reserved and always null today).
54+
emails). Requires read:akrites-maintainers (dedicated Akrites Enclave
55+
scope), or read:maintainer-roles from the old Self Serve client as a
56+
fallback until the cutover completes — never reachable via the packages
57+
scopes. The response shape is still under discussion upstream
58+
(reportingMethods / reportingGuidelines / integrationHints are reserved
59+
and always null today).
5160
- name: Blast Radius
5261
description: >
5362
Advisory reachability analysis — submit (2a) and poll (2b) are both
@@ -59,14 +68,14 @@ tags:
5968
entry starts its own workflow — and stays behind the same strict rate
6069
limiter as the single-job route; bulk poll
6170
(jobs:batch/poll) is read-only and capped at 100 like the other batch
62-
endpoints. Same interim scope note as Advisories applies (read:packages,
63-
pending a dedicated read:advisories scope).
71+
endpoints. Same scope note as Advisories applies (read:akrites-advisories,
72+
or read:packages as a fallback until the cutover completes).
6473
6574
components:
6675
securitySchemes:
6776
# Modeled as oauth2/clientCredentials (not http/bearer): OpenAPI only allows
6877
# non-empty scope arrays in security requirements for oauth2 / openIdConnect
69-
# schemes, and the operations below require read:packages + read:stewardships.
78+
# schemes.
7079
M2MBearer:
7180
type: oauth2
7281
description: >
@@ -77,9 +86,12 @@ components:
7786
clientCredentials:
7887
tokenUrl: https://linuxfoundation.auth0.com/oauth/token
7988
scopes:
80-
read:packages: Read package detail
81-
read:stewardships: Read package stewardship data
82-
read:maintainer-roles: Read security contacts (interim scope for Contacts; see the Contacts tag)
89+
read:akrites-packages: Read package detail (dedicated Akrites Enclave scope)
90+
read:akrites-advisories: Read security advisories and submit/poll blast-radius jobs (dedicated Akrites Enclave scope)
91+
read:akrites-maintainers: Read security contacts (dedicated Akrites Enclave scope)
92+
read:packages: Read package detail (fallback — old Self Serve client scope, being phased out)
93+
read:stewardships: Read package stewardship data (fallback — old Self Serve client scope, being phased out)
94+
read:maintainer-roles: Read security contacts (fallback — old Self Serve client scope, being phased out)
8395

8496
schemas:
8597
Error:
@@ -853,8 +865,11 @@ paths:
853865
summary: Get package detail by PURL
854866
tags: [Packages]
855867
security:
868+
- M2MBearer:
869+
- read:akrites-packages
856870
- M2MBearer:
857871
- read:packages
872+
- M2MBearer:
858873
- read:stewardships
859874
parameters:
860875
- name: purl
@@ -883,7 +898,7 @@ paths:
883898
schema:
884899
$ref: '#/components/schemas/Error'
885900
'403':
886-
description: Token missing read:packages or read:stewardships scope.
901+
description: Token missing read:akrites-packages, read:packages, or read:stewardships scope.
887902
content:
888903
application/json:
889904
schema:
@@ -901,8 +916,11 @@ paths:
901916
summary: Bulk package detail lookup
902917
tags: [Packages]
903918
security:
919+
- M2MBearer:
920+
- read:akrites-packages
904921
- M2MBearer:
905922
- read:packages
923+
- M2MBearer:
906924
- read:stewardships
907925
requestBody:
908926
required: true
@@ -960,7 +978,7 @@ paths:
960978
schema:
961979
$ref: '#/components/schemas/Error'
962980
'403':
963-
description: Token missing read:packages or read:stewardships scope.
981+
description: Token missing read:akrites-packages, read:packages, or read:stewardships scope.
964982
content:
965983
application/json:
966984
schema:
@@ -972,6 +990,8 @@ paths:
972990
summary: Get advisories for a package by PURL
973991
tags: [Advisories]
974992
security:
993+
- M2MBearer:
994+
- read:akrites-advisories
975995
- M2MBearer:
976996
- read:packages
977997
parameters:
@@ -1001,7 +1021,7 @@ paths:
10011021
schema:
10021022
$ref: '#/components/schemas/Error'
10031023
'403':
1004-
description: Token missing read:packages scope.
1024+
description: Token missing read:akrites-advisories, or read:packages, scope.
10051025
content:
10061026
application/json:
10071027
schema:
@@ -1019,6 +1039,8 @@ paths:
10191039
summary: Bulk advisory lookup
10201040
tags: [Advisories]
10211041
security:
1042+
- M2MBearer:
1043+
- read:akrites-advisories
10221044
- M2MBearer:
10231045
- read:packages
10241046
requestBody:
@@ -1077,7 +1099,7 @@ paths:
10771099
schema:
10781100
$ref: '#/components/schemas/Error'
10791101
'403':
1080-
description: Token missing read:packages scope.
1102+
description: Token missing read:akrites-advisories, or read:packages, scope.
10811103
content:
10821104
application/json:
10831105
schema:
@@ -1099,6 +1121,8 @@ paths:
10991121
Not yet implemented: the 7-day result cache and force-bypass semantics.
11001122
tags: [Blast Radius]
11011123
security:
1124+
- M2MBearer:
1125+
- read:akrites-advisories
11021126
- M2MBearer:
11031127
- read:packages
11041128
requestBody:
@@ -1131,7 +1155,7 @@ paths:
11311155
schema:
11321156
$ref: '#/components/schemas/Error'
11331157
'403':
1134-
description: Token missing read:packages scope.
1158+
description: Token missing read:akrites-advisories, or read:packages, scope.
11351159
content:
11361160
application/json:
11371161
schema:
@@ -1160,6 +1184,8 @@ paths:
11601184
back with status: 'failed' and the rest still submit.
11611185
tags: [Blast Radius]
11621186
security:
1187+
- M2MBearer:
1188+
- read:akrites-advisories
11631189
- M2MBearer:
11641190
- read:packages
11651191
requestBody:
@@ -1188,7 +1214,7 @@ paths:
11881214
schema:
11891215
$ref: '#/components/schemas/Error'
11901216
'403':
1191-
description: Token missing read:packages scope.
1217+
description: Token missing read:akrites-advisories, or read:packages, scope.
11921218
content:
11931219
application/json:
11941220
schema:
@@ -1212,6 +1238,8 @@ paths:
12121238
submit limiter.
12131239
tags: [Blast Radius]
12141240
security:
1241+
- M2MBearer:
1242+
- read:akrites-advisories
12151243
- M2MBearer:
12161244
- read:packages
12171245
requestBody:
@@ -1253,7 +1281,7 @@ paths:
12531281
schema:
12541282
$ref: '#/components/schemas/Error'
12551283
'403':
1256-
description: Token missing read:packages scope.
1284+
description: Token missing read:akrites-advisories, or read:packages, scope.
12571285
content:
12581286
application/json:
12591287
schema:
@@ -1269,6 +1297,8 @@ paths:
12691297
status.
12701298
tags: [Blast Radius]
12711299
security:
1300+
- M2MBearer:
1301+
- read:akrites-advisories
12721302
- M2MBearer:
12731303
- read:packages
12741304
parameters:
@@ -1298,7 +1328,7 @@ paths:
12981328
schema:
12991329
$ref: '#/components/schemas/Error'
13001330
'403':
1301-
description: Token missing read:packages scope.
1331+
description: Token missing read:akrites-advisories, or read:packages, scope.
13021332
content:
13031333
application/json:
13041334
schema:
@@ -1321,10 +1351,12 @@ paths:
13211351
operationId: getContactDetail
13221352
summary: Get security contact detail by PURL
13231353
description: >
1324-
Requires read:maintainer-roles (interim for the contract's cdp:maintainers:read).
1325-
Never reachable with the packages scope.
1354+
Requires read:akrites-maintainers, or read:maintainer-roles from the old Self
1355+
Serve client as a fallback. Never reachable with the packages scope.
13261356
tags: [Contacts]
13271357
security:
1358+
- M2MBearer:
1359+
- read:akrites-maintainers
13281360
- M2MBearer:
13291361
- read:maintainer-roles
13301362
parameters:
@@ -1354,7 +1386,7 @@ paths:
13541386
schema:
13551387
$ref: '#/components/schemas/Error'
13561388
'403':
1357-
description: Token missing read:maintainer-roles scope.
1389+
description: Token missing read:akrites-maintainers, or read:maintainer-roles, scope.
13581390
content:
13591391
application/json:
13601392
schema:
@@ -1372,6 +1404,8 @@ paths:
13721404
summary: Bulk security contact lookup
13731405
tags: [Contacts]
13741406
security:
1407+
- M2MBearer:
1408+
- read:akrites-maintainers
13751409
- M2MBearer:
13761410
- read:maintainer-roles
13771411
requestBody:
@@ -1430,7 +1464,7 @@ paths:
14301464
schema:
14311465
$ref: '#/components/schemas/Error'
14321466
'403':
1433-
description: Token missing read:maintainer-roles scope.
1467+
description: Token missing read:akrites-maintainers, or read:maintainer-roles, scope.
14341468
content:
14351469
application/json:
14361470
schema:
@@ -1472,6 +1506,8 @@ paths:
14721506
so callers aren't held open for the duration of the ingest.
14731507
tags: [Contacts]
14741508
security:
1509+
- M2MBearer:
1510+
- read:akrites-maintainers
14751511
- M2MBearer:
14761512
- read:maintainer-roles
14771513
requestBody:
@@ -1507,7 +1543,7 @@ paths:
15071543
schema:
15081544
$ref: '#/components/schemas/Error'
15091545
'403':
1510-
description: Token missing read:maintainer-roles scope.
1546+
description: Token missing read:akrites-maintainers, or read:maintainer-roles, scope.
15111547
content:
15121548
application/json:
15131549
schema:

0 commit comments

Comments
 (0)