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

AuthorizationController is not working after 2.2.0.Final #20144

Closed
andrewlunin opened this issue Sep 14, 2021 · 26 comments · Fixed by #20439
Closed

AuthorizationController is not working after 2.2.0.Final #20144

andrewlunin opened this issue Sep 14, 2021 · 26 comments · Fixed by #20439
Labels
env/windows Impacts Windows machines kind/bug Something isn't working
Milestone

Comments

@andrewlunin
Copy link

andrewlunin commented Sep 14, 2021

Describe the bug

AuthorizationController is not working any more after 2.2.0.Final.

@Alternative
@Priority(Interceptor.Priority.LIBRARY_AFTER)
@ApplicationScoped
public class DisabledAuthController extends AuthorizationController {

    ....

    @Override
    public boolean isAuthorizationEnabled() {
        return !disableAuthorization;
    }
}

Expected behavior

Authorization is skipped.

Actual behavior

401Unauthorized returned if token not provided or invalid.

How to Reproduce?

No response

Output of uname -a or ver

Microsoft Windows [Version 10.0.18363.1734]

Output of java -version

java version "11.0.10" 2021-01-19 LTS

GraalVM version (if different from Java)

No response

Quarkus version or git rev

2.2.2.Final

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.6.3

Additional information

This commit could probably cause the issue

@andrewlunin andrewlunin added the kind/bug Something isn't working label Sep 14, 2021
@quarkus-bot quarkus-bot bot added env/windows Impacts Windows machines triage/needs-triage labels Sep 14, 2021
@sberyozkin
Copy link
Member

@cemnura Hi Cem, do you still have a branch where you were looking at a similar issue ? Have a look please if yes

@cemnura
Copy link
Contributor

cemnura commented Sep 14, 2021

@sberyozkin Hi Sergey, I still have the branch #16852. Let me update it to the latest Quarkus V2 and see what I can do for this.

@sberyozkin
Copy link
Member

Hi @cemnura #16852 is addressing a different problem :-), I recall you started looking at the issue similar to what is described in this issue and then I asked you to look at #16852, since we now have @TestSecurity where one can easily disable authorization . So I wonder if you still have that branch

@cemnura
Copy link
Contributor

cemnura commented Sep 16, 2021

I will have to search. But I can try to quickly create a reproducer.

@cemnura
Copy link
Contributor

cemnura commented Sep 16, 2021

I created a reproducer(here) to see that the DisabledAuthController will work.

I tried with the security-keycloak-authorization-quickstart. Both in dev mode and test the authorization endpoint always returned 2XX status codes.

Could the configuration property be incorrect? Added the following line to application.yaml.

%test.disable.authorization=true

Also, adding the DisabledAuthController did not completely disable keycloak. Quarkus still tries to connect to keycloak regardless.

 2021-09-16 20:56:01,070 INFO  [io.qua.dep.dev.IsolatedDevModeMain] (main) Attempting to start live reload endpoint to recover from previous Quarkus startup failure
2021-09-16 20:56:01,070 INFO  [io.qua.dep.dev.IsolatedDevModeMain] (main) Attempting to start live reload endpoint to recover from previous Quarkus startup failure
2021-09-16 20:56:01,070 ERROR [io.qua.run.Application] (Quarkus Main Thread) Failed to start application (with profile dev): java.net.ConnectException: Connection refused (Connection refused)
        at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:399)
        at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:242)
        at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:224)
        at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.base/java.net.Socket.connect(Socket.java:609)
        at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:543)
        at org.keycloak.adapters.SniSSLSocketFactory.connectSocket(SniSSLSocketFactory.java:114)
        at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:415)
        at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)
        at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:144)
        at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:134)
        at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:605)
        at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:440)
        at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:835)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
        at org.keycloak.authorization.client.util.HttpMethod.execute(HttpMethod.java:84)
        at org.keycloak.authorization.client.util.HttpMethodResponse$2.execute(HttpMethodResponse.java:50)
        at org.keycloak.authorization.client.AuthzClient.<init>(AuthzClient.java:264)
        at org.keycloak.authorization.client.AuthzClient.create(AuthzClient.java:105)
        at org.keycloak.adapters.authorization.PolicyEnforcer.<init>(PolicyEnforcer.java:65)
        at io.quarkus.keycloak.pep.runtime.KeycloakPolicyEnforcerRecorder.createPolicyEnforcer(KeycloakPolicyEnforcerRecorder.java:98)
        at io.quarkus.keycloak.pep.runtime.KeycloakPolicyEnforcerRecorder.setup(KeycloakPolicyEnforcerRecorder.java:31)
        at io.quarkus.deployment.steps.KeycloakPolicyEnforcerBuildStep$setup558956564.deploy_0(KeycloakPolicyEnforcerBuildStep$setup558956564.zig:119)
        at io.quarkus.deployment.steps.KeycloakPolicyEnforcerBuildStep$setup558956564.deploy(KeycloakPolicyEnforcerBuildStep$setup558956564.zig:40)
        at io.quarkus.runner.ApplicationImpl.doStart(ApplicationImpl.zig:557)
        at io.quarkus.runtime.Application.start(Application.java:101)
        at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:101)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:66)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:42)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:119)
        at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:29)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at io.quarkus.runner.bootstrap.StartupActionImpl$1.run(StartupActionImpl.java:98)
        at java.base/java.lang.Thread.run(Thread.java:836)

2021-09-16 20:56:01,070 ERROR [io.qua.run.Application] (Quarkus Main Thread) Failed to start application (with profile dev): java.net.ConnectException: Connection refused (Connection refused)
        at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:399)
        at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:242)
        at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:224)
        at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
        at java.base/java.net.Socket.connect(Socket.java:609)
        at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:543)
        at org.keycloak.adapters.SniSSLSocketFactory.connectSocket(SniSSLSocketFactory.java:114)
        at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:415)
        at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)
        at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:144)
        at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:134)
        at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:605)
        at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:440)
        at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:835)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
        at org.keycloak.authorization.client.util.HttpMethod.execute(HttpMethod.java:84)
        at org.keycloak.authorization.client.util.HttpMethodResponse$2.execute(HttpMethodResponse.java:50)
        at org.keycloak.authorization.client.AuthzClient.<init>(AuthzClient.java:264)
        at org.keycloak.authorization.client.AuthzClient.create(AuthzClient.java:105)
        at org.keycloak.adapters.authorization.PolicyEnforcer.<init>(PolicyEnforcer.java:65)
        at io.quarkus.keycloak.pep.runtime.KeycloakPolicyEnforcerRecorder.createPolicyEnforcer(KeycloakPolicyEnforcerRecorder.java:98)
        at io.quarkus.keycloak.pep.runtime.KeycloakPolicyEnforcerRecorder.setup(KeycloakPolicyEnforcerRecorder.java:31)
        at io.quarkus.deployment.steps.KeycloakPolicyEnforcerBuildStep$setup558956564.deploy_0(KeycloakPolicyEnforcerBuildStep$setup558956564.zig:119)
        at io.quarkus.deployment.steps.KeycloakPolicyEnforcerBuildStep$setup558956564.deploy(KeycloakPolicyEnforcerBuildStep$setup558956564.zig:40)
        at io.quarkus.runner.ApplicationImpl.doStart(ApplicationImpl.zig:557)
        at io.quarkus.runtime.Application.start(Application.java:101)
        at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:101)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:66)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:42)
        at io.quarkus.runtime.Quarkus.run(Quarkus.java:119)
        at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:29)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at io.quarkus.runner.bootstrap.StartupActionImpl$1.run(StartupActionImpl.java:98)
        at java.base/java.lang.Thread.run(Thread.java:836)

2021-09-16 20:56:01,153 INFO  [org.jbo.threads] (main) JBoss Threads version 3.4.2.Final

@cemnura
Copy link
Contributor

cemnura commented Sep 16, 2021

Testing with @TestSecurity now

@cemnura
Copy link
Contributor

cemnura commented Sep 16, 2021

I messed around a little with @TestSecurity (here)

It seems to be working just fine. I removed the oauth tokens from the test.

Tests such as

    @Test
    @TestSecurity(authorizationEnabled = false)
    public void testAccessUserResource() {
        RestAssured.given()
                .when().get("/api/users/me")
                .then()
                .statusCode(200);
    }

Worked perfectly. However, I could get the following test to pass despite adding @TestSecurity with identity.

    @Test
    @Disabled // Not Working
    @TestSecurity(authorizationEnabled = true, user = "alice", roles = {"user"})
    public void testAccessAdminResource() {
        RestAssured.given()
                .when().get("/api/admin")
                .then()
                .statusCode(403);
    }

My intent was to test the endpoint with a user that does not have the admin role and get 403 error. However, the status code 200 is returned. I may have understand incorrectly.


I also tried to remove Keycloak from booting up during the test by removing @QuarkusTestResource(KeycloakServer.class) since Keycloak is not needed during testing. However, Quarkus is still invoking endpoints during initialization.

@andrewlunin
Copy link
Author

Hi @cemnura, @sberyozkin. Thanks for help with that issue.
Actually your reproducer indeed works fine, but the problem appears only if you annotate your method with @RolesAllowed("some.role"). If I set @permitAll or set nothing, everything works fine - authentication being skipped completely.
The fact is that in version 2.1.4.Final DisabledAuthController successfully disabled all authentication for methods having @RolesAllowed but when we upgraded to 2.2.2 we started to receive 401 for requests without a token. Of course roles itself can not affect anything since authorization doesn't even have a chance to happen.

I have the following Quarkus authentication related content in config file:

quarkus:
  smallrye-jwt:
    enabled: true
mp:
  jwt:
    verify:
      publickey:
        location: ${public-key.location}
      issuer: ${public-key-issuer}

@andrewlunin
Copy link
Author

And what I discovered as well is that the problem occurs for Smallrye JWT RBAC, but not for OIDC.

@andrewlunin
Copy link
Author

btw, you get this java.net.ConnectException in your reproducer most probably due to wrong quarkus.oidc.auth-server-url option in application.properties. If you set up your keycloak server according to README file, then the option should be:

quarkus.oidc.auth-server-url=http://localhost:8180/auth/realms/quarkus

@cemnura
Copy link
Contributor

cemnura commented Sep 18, 2021

Hello @andrewlunin,

I created a reproducer here to have a look.

I can disable and enable the Authorization with a custom AuthorizationController.

@Alternative
@Priority(Interceptor.Priority.LIBRARY_BEFORE)
@ApplicationScoped
public class DisabledAuthController extends AuthorizationController {

    @ConfigProperty(name = "disable.authorization", defaultValue = "false")
    boolean disableAuthorization;

    @Override
    public boolean isAuthorizationEnabled() {
        return !disableAuthorization;
    }
}

and by adjusting the configuration

%dev.disable.authorization=true

Then I run

./mvnw clean quarkus:dev

and curl works

curl localhost:8080/secured/roles-allowed-admin -v                                                                                  
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /secured/roles-allowed-admin HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Length: 11
< Content-Type: text/plain;charset=UTF-8
<
* Connection #0 to host localhost left intact
hello admin* Closing connection 0

When I change the configuration back to

%dev.disable.authorization=false

I can't curl to the endpoint

curl localhost:8080/secured/roles-allowed-admin -v      ✔  microk8s ⎈ 
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /secured/roles-allowed-admin HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
> 
< HTTP/1.1 401 Unauthorized
< www-authenticate: Bearer
< Content-Length: 0
< 
* Connection #0 to host localhost left intact
* Closing connection 0

Could you please check the difference between yours and the reproducer? Could there be a missing configuration?

@cemnura
Copy link
Contributor

cemnura commented Sep 18, 2021

@sberyozkin I am trying out @TestSecurity as you said to see whether it will disable the authentication or not. Shouldn't the following test pass?

    @Test
    @TestSecurity(authorizationEnabled = false)
    public void testDisabledHelloDenyAll() {
        Response response = given()
                .when()
                .get("/secured/deny-all").andReturn();

        response.then().statusCode(200); // actually returns 401
    }

@andrewlunin
Copy link
Author

Hello @cemnura. I finally understood what is the reason. Actually it doesn't work with JWT RBAC and quarkus-resteasy-reactive. This combination stopped working. I made reproducer here. It works again after I set Quarkus version to 2.1.4 here
But if you checkout parent commit it doesn't work.

@cemnura
Copy link
Contributor

cemnura commented Sep 21, 2021

I will take a look @andrewlunin. Thanks for the reproducer seams interesting.

@cemnura
Copy link
Contributor

cemnura commented Sep 22, 2021

I can see what you mean now. Yes, indeed I am getting 401 Authorization errors when using resteasy-reactive despite disabling. @sberyozkin any ideas were I can focus on? Checking @andrewlunin comment on possible commit being #19598.

@cemnura
Copy link
Contributor

cemnura commented Sep 24, 2021

I traced the execution and found out that the 401 Unauthorized was occuring due to the EagerSecurityHandler

theCheck.apply(securityIdentity, requestContext.getTarget().getLazyMethod().getMethod(),
requestContext.getParameters());

throws io.quarkus.security.UnauthorizedException exception.

This is neglecting the DisabledAuthController.

@sberyozkin could we inject the AuthorizationController into this and check if authorization is enabled?

@sberyozkin
Copy link
Member

sberyozkin commented Sep 27, 2021

@cemnura Sorry, seeing it only now - I don't know why but I'm not always seeing the pings, strangely enough, I see it every time quarkus-bot is pinging me :-)

Thanks for spending your time on this issue,

Can you please check if it works with quarkus-resteasy ?

@geoand - do you agree it would make sense for resteasy-reactive to have AuthorizationController checked as well ?

@geoand
Copy link
Contributor

geoand commented Sep 27, 2021

I'd be surprised if it's not fixed with 2.3.0.CR1. Can it be checked and also compared with the quarkus-resteasy behavior?
If it doesn't work, then I totally agree it needs to be fixed

@sberyozkin
Copy link
Member

@andrewlunin Can you also please check 2.3.0.CR1

@cemnura
Copy link
Contributor

cemnura commented Sep 28, 2021

@geoand I checked out quarkus-resteasy with version 2.2.3.Final and I can close the authentication controller based on the the following config

%dev.authorization.disable=true

Then the curl response is 200 OK

curl localhost:8080/secured/roles-allowed-admin -v
Trying 127.0.0.1...
TCP_NODELAY set
Connected to localhost (127.0.0.1) port 8080 (#0)
GET /secured/roles-allowed-admin HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.1
Accept: */*

HTTP/1.1 200 OK
Content-Length: 83
Content-Type: text/plain;charset=UTF-8

Connection #0 to host localhost left intact
hello + anonymous, isHttps: false, authScheme: null, hasJWT: false, birthdate: null* Closing connection 0

But when

%dev.authorization.disable=false

then we get the expected 401 Unauthorized.

curl localhost:8080/secured/roles-allowed-admin -v
Trying 127.0.0.1...
TCP_NODELAY set
Connected to localhost (127.0.0.1) port 8080 (#0)
GET /secured/roles-allowed-admin HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.1
Accept: */*

HTTP/1.1 401 Unauthorized
www-authenticate: Bearer
Content-Length: 0

But when I change from quarkus-resteasy to quarkus-resteasy-reactive the DisabledAuthController is neglected and the response is always 401 Unauthorized despite the %dev.authorization.disable=true configuration.

Reproducer can be found here

@cemnura
Copy link
Contributor

cemnura commented Sep 28, 2021

I quickly updated the quarkus version to 2.3.0.CR1 and tried to disable the authentication via DisabledAuthController with quarkus-resteasy-reactive yet I am still receiving 401 Unauthorized error.

quarkus-resteasy works the same as 2.2.3.Final. I can turn authentication via DisabledAuthController depending on my configuration.

@geoand
Copy link
Contributor

geoand commented Sep 28, 2021

Thanks for the information. I'll take a look

@geoand
Copy link
Contributor

geoand commented Sep 28, 2021

It seems that in order to solve this would would have to push the AuthorizationController into the implementations of SecurityCheck as well, because with RESTEasy Reactive the interceptors that use it don't get called in the eager security flow (unless Arc gives a way to "extract" the intereptors from a bean).

WDYT @stuartwdouglas ?

@andrewlunin
Copy link
Author

I quickly updated the quarkus version to 2.3.0.CR1 and tried to disable the authentication via DisabledAuthController with quarkus-resteasy-reactive yet I am still receiving 401 Unauthorized error.

quarkus-resteasy works the same as 2.2.3.Final. I can turn authentication via DisabledAuthController depending on my configuration.

Confirm - 2.3.0.CR1 is not working for me as well.

@cemnura
Copy link
Contributor

cemnura commented Sep 28, 2021

@geoand is there anything I can look into?

@geoand
Copy link
Contributor

geoand commented Sep 29, 2021

Not at the moment. I'll let you know :)

stuartwdouglas added a commit to stuartwdouglas/quarkus that referenced this issue Sep 29, 2021
@quarkus-bot quarkus-bot bot added this to the 2.4 - main milestone Sep 29, 2021
@geoand geoand modified the milestones: 2.4 - main, 2.3.0.Final Sep 29, 2021
geoand pushed a commit to geoand/quarkus that referenced this issue Sep 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
env/windows Impacts Windows machines kind/bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants