Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reorder filters and let RealmIdResolver throw UnresolvableRealmException #848

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

Conversation

adutra
Copy link
Contributor

@adutra adutra commented Jan 22, 2025

Fixes #819.

Summary of changes:

  • Vert.x route filters were converted to JAX-RS ContainerRequestFilter
  • New RealmIdFilter introduced
  • RealmIdResolver now can throw UnresolvableRealmException => maps to HTTP 404
  • Added tests in PolarisApplicationIntegrationTest
  • Fixed default realm name in tests
  • Priorities were revisited, new order of execution is shown below:
Filter Order Priority
RealmIdFilter 1 900
QuarkusLoggingMDCFilter 2 901
QuarkusTracingFilter 3 902
PolarisPrincipalAuthenticatorFilter 4 1000
PolarisPrincipalRolesProviderFilter 5 1001
RateLimiterFilter 6 5000

@adutra adutra force-pushed the filter-order branch 2 times, most recently from e542f5c to 9c45494 Compare January 22, 2025 17:50
@adutra adutra changed the title [WIP] Reorder filters and let RealmIdResolver throw NotAuthorized Reorder filters and let RealmIdResolver throw NotAuthorized Jan 24, 2025
@adutra adutra marked this pull request as ready for review January 24, 2025 10:29
@adutra
Copy link
Contributor Author

adutra commented Jan 24, 2025

This is now ready for review.

* The header name for the realm ID. Tests must make sure that Polaris is configured with this
* header name.
*/
public static String REALM_HEADER = "Polaris-Realm";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind moving this to a (new) method on PolarisServerManager or Server so that it is possible to customize?

assertThat(response.getStatus()).isEqualTo(Status.OK.getStatusCode());
Catalogs roles = response.readEntity(Catalogs.class);
if ("POLARIS".equals(realmId)) {
assertThat(roles.getCatalogs()).extracting(Catalog::getName).contains(internalCatalogName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe make internalCatalogName a parameter to this test?

try {
realmId = resolveRealmContext(rc);
} catch (NotAuthorizedException e) {
rc.abortWith(Response.status(Status.UNAUTHORIZED).build());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about 401 in this context. I believe it generally means your credentials are not valid, which is not applicable to failures in realm resolution.

I'm thinking that a 404 may fit better. For example, GH gives 404 on accessing repositories (i.e. realms) to which the user has no access.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, i will update the PR title and description accordingly.

String requestURL, String method, String path, Map<String, String> headers) {

String realm;

if (headers.containsKey(configuration.headerName())) {
realm = headers.get(configuration.headerName());
if (!configuration.realms().contains(realm)) {
throw new IllegalArgumentException("Unknown realm: " + realm);
throw new NotAuthorizedException("Unknown realm: " + realm);
}
} else {
realm = configuration.defaultRealm();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it could be valuable to cover this with an explicit config flag. If realms are used in a particular deployment, it is probably not wise to allow potentially malicious clients to try sending requests at the default realm without knowing its ID.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely agree, I think that having a default realm is a potential security issue.

@adutra adutra changed the title Reorder filters and let RealmIdResolver throw NotAuthorized Reorder filters and let RealmIdResolver throw UnresolvableRealmException Jan 27, 2025
Copy link
Contributor

@dimas-b dimas-b left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice improvement to request filters! Thanks, @adutra !

As far as I can tell this change is backward compatible at the REST API / HTTP level.

@@ -49,4 +49,8 @@ public URI managementApiEndpoint() {
public String realm() {
return realm;
}

public String realmHeader() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rename to realmHeaderName() + realmId() (latter for consistency w/ Server)?

@@ -36,4 +39,8 @@ public interface Server extends AutoCloseable {
URI baseUri();

ClientPrincipal adminCredentials();

default String realmHeader() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
default String realmHeader() {
default String realmHeaderName() {

Comment on lines 41 to 42
* <p>Note: this is actually only enforced when using {@link DefaultRealmIdResolver}. When using
* {@link TestRealmIdResolver}, this setting is ignored.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* <p>Note: this is actually only enforced when using {@link DefaultRealmIdResolver}. When using
* {@link TestRealmIdResolver}, this setting is ignored.
* <p>Note: this setting is only enforced in production setups.

* <p>Note: this is actually only enforced when using {@link DefaultRealmIdResolver}. When using
* {@link TestRealmIdResolver}, this setting is ignored.
*/
boolean requireHeader();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be true by default?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Defaulting to true would be a behaviour change at the REST/HTTP level. I'd prefer to keep old behaviour in this PR and change defaults later.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WFM

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah changing this to true would break downstreams. But we can mention this in the "Configuring for production" docs.

Comment on lines 50 to 54
rc.getHeaders().entrySet().stream()
.collect(
HashMap::new,
(m, e) -> m.put(e.getKey(), e.getValue().getFirst()),
Map::putAll));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if it makes more sense to change RealmIdResolver to expect a Function<String, String> rather than a "full blown map" (and use request::getHeader here).

@adutra adutra requested a review from MonkeyCanCode as a code owner January 27, 2025 17:58
@dimas-b
Copy link
Contributor

dimas-b commented Jan 28, 2025

@collado-mike : any concerns from your POV?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Rethink order of JAX-RS filters in Polaris
3 participants