Rely on per-entity filtering for listing instead of list-level gates#2523
Conversation
7dd1ed5 to
10dfc9a
Compare
10dfc9a to
82ca796
Compare
|
I have concerns about The second issue is that the tag management stuff that was under admin:tags got mapped over to read:platform. I don't think that's correct. Admin doesn't necessarily mean you have god level permissions to things: in our environment we would allow users to create their own tags and assign them to things they own. But that may be a restricted operation (as, only certain users shoudl be able to assign and create tags). Therefore, we put admin:tags and operations related to tags requires that permission. I have to check further, but it may be the case that owners can assign tags to their own assets without admin:tag.... |
0fcdcb2 to
d0eaf61
Compare
| -- | ||
| -- Placed above the 3.0.0 baseline so it runs on fresh installs (migrations below the baseline | ||
| -- are folded into it and skipped). | ||
| INSERT INTO ${ohdsiSchema}.sec_permission (value, description) |
There was a problem hiding this comment.
This migration should be put as V2.99.0005 so that it will be considred 'transitional migration leading up to the 3.0 version. The version you have specified here makes it look like there is a V3.0.1 of webapi, but there's not.
The idea for making all the migrations leading up the the 3.0 baseline is everything we're cahing from 2.15 to the 3.0 release goes in migration scripts V2.99.xxxx so that it will be present when we do the 3.0 baseline script. So please put this script into V2.99.0005, and add this permission to the 3.0 baseline script.
Future migrations leading up to the 3.0 release should be put into V2.99
| -- Placed above the 3.0.0 baseline so it runs on fresh installs (migrations below the baseline | ||
| -- are folded into it and skipped). | ||
| INSERT INTO ${ohdsiSchema}.sec_permission (value, description) | ||
| SELECT 'list', 'List platform reference data (users, roles, permissions, jobs, tools) and use DDL/SqlRender utilities' |
There was a problem hiding this comment.
IN addition, the other examples of inserting permissoins is like this:
insert into ${ohdsiSchema}.sec_permission(id, value, description)
select nextval('${ohdsiSchema}.sec_permission_sequence'), value, description
FROM (
VALUES
('*', 'All Permissions'),
('admin', 'All Admin Permissions'),
('admin:source', 'Manage Sources'),
This example above is used to insert a bunch of permissions, but the point is we use nextval() to use the sequence, and also, we shoudl not be using 'where not exists'. If the permission actually exists, we want to throw an error in the migration because it is not expected.
| # false – default-deny: every endpoint outside the login allow-list | ||
| # requires authentication. Use when every client logs in. | ||
| anonymousAccess: | ||
| enabled: true |
There was a problem hiding this comment.
I think that it's not a bad option to disable anonymous access to webapi, so if you want, you can restore this setting, and we can put in the JWT servlet filter chain that if this is not enabled, we reject the request if it doesn't have a valid JWT.
I wonder if this is better under security.xxx configuration options vs putting this at the top-level of the application.yaml.
WebAPI authorizes every request through a security principal (authenticated user or the built-in anonymous principal), deciding access from permission grants + per-entity filtering. #2521 hardened authorization and kept that model. It gated list endpoints behind read/write permissions, when listing should stay open and have its contents filtered per principal (which several lists already did). This change removes those list gates and relies on filtering, and removes a handful of isAuthenticated() checks that cut against the model. #2521's genuine endpoint protections are kept. - Lists: drop the read/write @PreAuthorize gates on artifact lists (cohort, concept set, characterization, pathway, feature-analysis, IR, reusable) and the four /byTags queries. Listing is open; contents are filtered per-entity. - Filtering: implement the missing per-entity filtering on the IR, feature-analysis and reusable lists, and filter /source/sources by source access. - 'list' permission: add a 'list' permission (transitional migration V2.99.0005, also added to the B3.0.0 baseline) granted to the built-in public role, gating the surfaces with no per-entity scope to filter -- the user/role/permission registries, jobs, tools, and the DDL/SqlRender utilities. Authenticated users (public) can list; anonymous/ungranted -> 403. - Source data gates kept from #2521: vocabulary, evidence, cdmresults stay on read:source / hasSourceAccess. DDL and SqlRender read no source data, so they move off read:source to 'list'. Feature-extraction stays on read:feature-analysis. - Anonymous-access toggle: rename security.anonymousAccess.enabled to security.allowAnonymousAccess (default true serves token-less requests as the anonymous principal; false requires a valid JWT for every endpoint outside the bootstrap allow-list). SecurityIT covers the disabled path. - Remove the isAuthenticated() gates (getCurrentUser, CC/pathway generation reads, notifications); generation reads keep their in-body entity+source checks. - Cohort-sample person data: require both cohort and source access (was source-only) and bind the sample to the cohort/source in the path (closes an IDOR). - BouncyCastle provider: register it once on the JVM via java.security.Security (guarded by getProvider) in EncryptorUtils.buildStringEncryptor, instead of constructing a fresh BouncyCastleProvider per call. - Guardrail tests: allow-list the open-listing endpoints; assert anonymous is denied on the gated surfaces and a reader is allowed; repurpose AnonymousAccessIT for the enabled path and SecurityIT for the disabled path. Verified: SecurityIT, EndpointAuthCoverageIT, AuthorizationAccessIT, AnonymousAccessIT, AuthorizationPermissionStringsIT and SourceAccessIT all pass.
d0eaf61 to
fb971b8
Compare
|
@p-hoffmann , thank you for updates, I think this is good enough to merge in. We can always revisit the list functions if it needs to be more refined, and I appreciate granting it to the public role by default. I have work on a api-key branch so when this gets merged in, i will be able to merge my branch and add that function next. |
Why
WebAPI authorizes every request through a security principal (authenticated user or the built-in anonymous principal), deciding access from permission grants + per-entity filtering. #2521 hardened authorization and kept that model. #2521 gated list endpoints behind
read/writepermissions, when listing should stay open and have its contents filtered per principal (which several lists already did). This PR removes those list gates and relies on filtering, and removes a handful ofisAuthenticated()checks that cut against the model. #2521's genuine endpoint protections are kept.Kept from #2521 (these endpoints were genuinely open before — leave gated)
read:source/hasSourceAccessread:feature-analysisadmin:*The fix: listing is open, contents are filtered
Content is protected one of two ways:
/source/sources. 4 already did this (cohort-definition, concept-set, cohort-characterization, pathway); this PR adds IR, feature-analysis, reusable, and the source list./byTagslist-by-tag queries are opened too.listpermission (migrationV2.99.0005, also in theB3.0.0baseline), granted to the built-inpublicrole, where there's no per-entity scope to filter — user/role/permission registries, jobs, tools, and the DDL/SqlRender utilities (which read no source data). Authenticated users can list; anonymous → 403.Secondary cleanups
isAuthenticated()gates (getCurrentUser, generation reads, notifications) — generation reads keep their in-body entity+source checks.security.anonymousAccess.enabled→security.allowAnonymousAccess(default true serves token-less requests as the anonymous principal; false requires a valid JWT for every endpoint outside the bootstrap allow-list).Other fixes