Skip to content

Commit

Permalink
[#181889780] Fix scope only auth + update token validator, security c…
Browse files Browse the repository at this point in the history
…onfig and oauth client to handle revoked tokens properly
  • Loading branch information
jstromsky committed Apr 20, 2022
1 parent 3216e12 commit 6cc4099
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 16 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@
<version.bouncy.castle>1.67</version.bouncy.castle>
<feign.version>10.2.0</feign.version>
<feign-form.version>3.8.0</feign-form.version>
<dnastack-token-validator.version>1.0.2</dnastack-token-validator.version>
<dnastack-token-validator.version>1.0-64-g8876a6f</dnastack-token-validator.version>
<audit-event-logger.version>1.0.3</audit-event-logger.version>
<oauth-client-factory.version>0.1.0</oauth-client-factory.version>
<oauth-client-factory.version>0.1-21-g86b54b3</oauth-client-factory.version>
<logback-extensions.version>1.0.0</logback-extensions.version>
<logback.version>1.2.10</logback.version>
</properties>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.dnastack.ga4gh.dataconnect;

import brave.Tracing;
import com.dnastack.auth.JwtTokenParser;
import com.dnastack.auth.JwtTokenParserFactory;
import com.dnastack.auth.PermissionChecker;
import com.dnastack.auth.PermissionCheckerFactory;
import com.dnastack.auth.client.TokenActionsHttpClientFactory;
import com.dnastack.auth.keyresolver.CachingIssuerPubKeyJwksResolver;
import com.dnastack.auth.keyresolver.IssuerPubKeyStaticResolver;
import com.dnastack.auth.model.IssuerInfo;
Expand All @@ -13,6 +16,10 @@
import com.dnastack.ga4gh.dataconnect.adapter.telemetry.TrinoTelemetryClient;
import com.dnastack.ga4gh.dataconnect.adapter.trino.TrinoClient;
import com.dnastack.ga4gh.dataconnect.adapter.trino.TrinoHttpClient;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.JwtException;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -208,14 +215,19 @@ private String stripTrailingSlashes(String url) {
}

@Bean
public JwtDecoder jwtDecoder(AuthConfig authConfig) {
List<AuthConfig.IssuerConfig> issuers = authConfig.getTokenIssuers();

if (issuers == null || issuers.isEmpty()) {
throw new IllegalArgumentException("At least one token issuer must be defined");
}
issuers = new ArrayList<>(issuers);
return new DelegatingJwtDecoder(issuers);
public JwtDecoder jwtDecoder(List<IssuerInfo> allowedIssuers, PermissionChecker permissionChecker, Tracing tracing) {
final JwtTokenParser jwtTokenParser = JwtTokenParserFactory.create(allowedIssuers, TokenActionsHttpClientFactory.create(tracing));
return (jwtToken) -> {
try {
permissionChecker.checkPermissions(jwtToken);
final Jws<Claims> jws = jwtTokenParser.parseTokenClaims(jwtToken);
final JwsHeader headers = jws.getHeader();
final Claims claims = jws.getBody();
return new Jwt(jwtToken, claims.getIssuedAt().toInstant(), claims.getExpiration().toInstant(), headers, claims);
} catch (JwtException e) {
throw new org.springframework.security.oauth2.jwt.BadJwtException(e.getMessage(), e);
}
};
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public DataConnectController(TrinoDataConnectAdapter trinoDataConnectAdapter) {
@AuditActionUri("data-connect:search")
@AuditIgnoreHeaders("GA4GH-Search-Authorization")
@AuditEventCustomize(QueryJobAppenderAuditEventCustomizer.class)
@PreAuthorize("@accessEvaluator.canAccessResource('/search', {'data-connect:query', 'data-connect:data'}, {'data-connect:query', 'data-connect:data'})")
@PreAuthorize("hasAuthority('SCOPE_data-connect:query') && hasAuthority('SCOPE_data-connect:data') && @accessEvaluator.canAccessResource('/search', {'data-connect:query', 'data-connect:data'}, {'data-connect:query', 'data-connect:data'})")
@PostMapping(value = "/search")
public TableData search(@RequestBody DataConnectRequest dataConnectRequest,
HttpServletRequest request,
Expand All @@ -55,7 +55,7 @@ public TableData search(@RequestBody DataConnectRequest dataConnectRequest,
@AuditActionUri("data-connect:next-page")
@AuditIgnoreHeaders("GA4GH-Search-Authorization")
@AuditEventCustomize(QueryJobAppenderAuditEventCustomizer.class)
@PreAuthorize("@accessEvaluator.canAccessResource('/search/', {'data-connect:query', 'data-connect:data'}, {'data-connect:query', 'data-connect:data'})")
@PreAuthorize("hasAuthority('SCOPE_data-connect:query') && hasAuthority('SCOPE_data-connect:data') && @accessEvaluator.canAccessResource('/search/', {'data-connect:query', 'data-connect:data'}, {'data-connect:query', 'data-connect:data'})")
@GetMapping(value = "/search/**")
public TableData getNextPaginatedResponse(@RequestParam("queryJobId") String queryJobId,
HttpServletRequest request,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public TablesController(TrinoDataConnectAdapter trinoDataConnectAdapter) {

@AuditActionUri("data-connect:info")
@AuditIgnoreHeaders("GA4GH-Search-Authorization")
@PreAuthorize("@accessEvaluator.canAccessResource('/tables', 'data-connect:info', 'data-connect:info')")
@PreAuthorize("hasAuthority('SCOPE_data-connect:info') && @accessEvaluator.canAccessResource('/tables', 'data-connect:info', 'data-connect:info')")
@GetMapping(value = "/tables")
public ResponseEntity<TablesList> getTables(HttpServletRequest request,
@AuditIgnore @RequestHeader(value = "GA4GH-Search-Authorization", defaultValue = "") List<String> clientSuppliedCredentials) {
Expand All @@ -55,7 +55,7 @@ public ResponseEntity<TablesList> getTables(HttpServletRequest request,
// This endpoint is in addition to GET /tables to allow random-access to pages in the GET /tables result
@AuditActionUri("data-connect:get-tables-in-catalog")
@AuditIgnoreHeaders("GA4GH-Search-Authorization")
@PreAuthorize("@accessEvaluator.canAccessResource('/tables/catalog/' + #catalogName, 'data-connect:info', 'data-connect:info')")
@PreAuthorize("hasAuthority('SCOPE_data-connect:info') && @accessEvaluator.canAccessResource('/tables/catalog/' + #catalogName, 'data-connect:info', 'data-connect:info')")
@GetMapping(value = "/tables/catalog/{catalogName}")
public ResponseEntity<TablesList> getTablesByCatalog(@PathVariable("catalogName") String catalogName,
HttpServletRequest request,
Expand All @@ -76,7 +76,7 @@ public ResponseEntity<TablesList> getTablesByCatalog(@PathVariable("catalogName"

@AuditActionUri("data-connect:get-table-info")
@AuditIgnoreHeaders("GA4GH-Search-Authorization")
@PreAuthorize("@accessEvaluator.canAccessResource('/table/' + #table_name + '/info', 'data-connect:info', 'data-connect:info')")
@PreAuthorize("hasAuthority('SCOPE_data-connect:info') && @accessEvaluator.canAccessResource('/table/' + #table_name + '/info', 'data-connect:info', 'data-connect:info')")
@GetMapping(value = "/table/{table_name}/info")
public TableInfo getTableInfo(@PathVariable("table_name") String tableName,
HttpServletRequest request,
Expand All @@ -98,7 +98,7 @@ public TableInfo getTableInfo(@PathVariable("table_name") String tableName,
@AuditActionUri("data-connect:get-table-data")
@AuditIgnoreHeaders("GA4GH-Search-Authorization")
@AuditEventCustomize(QueryJobAppenderAuditEventCustomizer.class)
@PreAuthorize("@accessEvaluator.canAccessResource('/table/' + #table_name + '/data', 'data-connect:data', 'data-connect:data')")
@PreAuthorize("hasAuthority('SCOPE_data-connect:data') && @accessEvaluator.canAccessResource('/table/' + #table_name + '/data', 'data-connect:data', 'data-connect:data')")
@GetMapping(value = "/table/{table_name}/data")
public TableData getTableData(@PathVariable("table_name") String tableName,
HttpServletRequest request,
Expand Down

0 comments on commit 6cc4099

Please sign in to comment.