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

JpaIdentityProvider not working with Reactive Panache entities #19302

Closed
Eng-Fouad opened this issue Aug 9, 2021 · 4 comments
Closed

JpaIdentityProvider not working with Reactive Panache entities #19302

Eng-Fouad opened this issue Aug 9, 2021 · 4 comments
Labels
area/panache area/security env/windows Impacts Windows machines kind/bug Something isn't working

Comments

@Eng-Fouad
Copy link
Contributor

Eng-Fouad commented Aug 9, 2021

Describe the bug

When using jpa-security as described in guides, but replacing normal imperative hibernate entities with reactive Panache entities, I got an exception after calling this code:

@Inject
IdentityProviderManager identityProviderManager;

// ...

var request = new UsernamePasswordAuthenticationRequest(username, passwordCredential);
identityProviderManager.authenticate(request);

The exception:

java.lang.UnsupportedOperationException: Use performReactiveList instead
	at org.hibernate.reactive.session.impl.ReactiveHQLQueryPlan.performList(ReactiveHQLQueryPlan.java:72)
	at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1443)
	at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1649)
	at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1617)
	at org.hibernate.query.Query.getResultList(Query.java:165)
	at io.quarkus.security.jpa.runtime.AbstractJpaIdentityProvider.getSingleUser(AbstractJpaIdentityProvider.java:53)
	at io.fouad.backend.auth.User__JpaIdentityProviderImpl_Subclass.getSingleUser$$superforward1(User__JpaIdentityProviderImpl_Subclass.zig:330)
	at io.fouad.backend.auth.User__JpaIdentityProviderImpl_Subclass$$function$$1.apply(User__JpaIdentityProviderImpl_Subclass$$function$$1.zig:33)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:51)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(InvocationInterceptor_Bean.zig:521)
	at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
	at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
	at io.fouad.backend.auth.User__JpaIdentityProviderImpl_Subclass.getSingleUser(User__JpaIdentityProviderImpl_Subclass.zig:521)
	at io.fouad.backend.auth.User__JpaIdentityProviderImpl.authenticate(User__JpaIdentityProviderImpl.zig:47)
	at io.fouad.backend.auth.User__JpaIdentityProviderImpl_Subclass.authenticate$$superforward1(User__JpaIdentityProviderImpl_Subclass.zig:347)
	at io.fouad.backend.auth.User__JpaIdentityProviderImpl_Subclass$$function$$2.apply(User__JpaIdentityProviderImpl_Subclass$$function$$2.zig:41)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:54)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.proceed(InvocationInterceptor.java:62)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor.monitor(InvocationInterceptor.java:51)
	at io.quarkus.arc.runtime.devconsole.InvocationInterceptor_Bean.intercept(InvocationInterceptor_Bean.zig:521)
	at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:41)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:41)
	at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:32)
	at io.fouad.backend.auth.User__JpaIdentityProviderImpl_Subclass.authenticate(User__JpaIdentityProviderImpl_Subclass.zig:618)
	at io.quarkus.security.jpa.runtime.JpaIdentityProvider$1.get(JpaIdentityProvider.java:42)
	at io.quarkus.security.jpa.runtime.JpaIdentityProvider$1.get(JpaIdentityProvider.java:35)
	at io.quarkus.security.runtime.QuarkusIdentityProviderManagerImpl$1$1$1$1.run(QuarkusIdentityProviderManagerImpl.java:58)
	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:536)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2442)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1476)
	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:831)

I think this can be solved by modifying this snippet:

protected <T> T getSingleUser(Query query) {
@SuppressWarnings("unchecked")
List<T> results = (List<T>) query.getResultList();
if (results.isEmpty())
return null;
if (results.size() > 1)
throw new AuthenticationFailedException();
return results.get(0);
}

and adding something like:

if (query instanceof ReactiveQuery)
    var results = ((ReactiveQuery) query).getReactiveResultList();

I believe getSingleUser method is being invoked by the generated JpaIdentityProvider:

// UserEntity user = getSingleUser(query2);
user = methodCreator.invokeVirtualMethod(
MethodDescriptor.ofMethod(name, "getSingleUser", Object.class, Query.class),
methodCreator.getThis(), query2);

Expected behavior

No response

Actual behavior

No response

How to Reproduce?

No response

Output of uname -a or ver

Windows 10

Output of java -version

16

GraalVM version (if different from Java)

No response

Quarkus version or git rev

2.1.1.Final

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

Gradle 7.1.1

Additional information

No response

@Eng-Fouad Eng-Fouad added the kind/bug Something isn't working label Aug 9, 2021
@quarkus-bot quarkus-bot bot added area/panache env/windows Impacts Windows machines labels Aug 9, 2021
@quarkus-bot
Copy link

quarkus-bot bot commented Aug 9, 2021

/cc @FroMage, @loicmathieu

@MartinX3
Copy link

MartinX3 commented Nov 6, 2021

Same issue with 2.4.1.

@MartinX3
Copy link

MartinX3 commented Nov 7, 2021

I created a workaround

package custom.handler

import custom.model.UserAccount
import io.quarkus.security.AuthenticationFailedException
import io.quarkus.security.ForbiddenException
import io.quarkus.security.identity.request.UsernamePasswordAuthenticationRequest
import io.quarkus.security.jpa.runtime.JpaIdentityProvider
import io.vertx.mutiny.pgclient.PgPool
import io.vertx.mutiny.sqlclient.Tuple.of
import java.time.Duration.ofSeconds
import java.util.*
import javax.annotation.Priority
import javax.enterprise.context.ApplicationScoped
import javax.enterprise.inject.Alternative
import javax.inject.Inject
import javax.persistence.EntityManager

// TODO WORKAROUND FOR https://github.com/quarkusio/quarkus/issues/19302
@Suppress("unused")
@Alternative
@Priority(1)
@ApplicationScoped
class CustomUserEntityIdentityProvider : JpaIdentityProvider() {
    @Inject
    lateinit var client: PgPool

    override fun authenticate(em: EntityManager, request: UsernamePasswordAuthenticationRequest) =
        client.preparedQuery("SELECT id, password, username FROM useraccount WHERE username = $1")
            .execute(of(request.username))
            .onFailure().transform(::AuthenticationFailedException)
            .onItem().ifNotNull().transform { rs ->
                if (rs.count() != 1) throw AuthenticationFailedException() else
                    rs.map {
                        UserAccount(
                            password = it.getString("password"),
                            username = it.getString("username")
                        ).apply { id = it.getUUID("id") }
                    }
            }.await().asOptional().atMost(ofSeconds(10)).orElse(null)?.first()?.run {
                checkPassword(getMcfPassword(password), request).also {
                    addRoles(it, getUserRoles(id))
                }.run { build() }
            }

    private fun getUserRoles(useraccount_id: UUID) =
        client.preparedQuery("SELECT ur.name FROM userrole ur INNER JOIN useraccount_userrole ua_ur ON ua_ur.roles_id = ur.id WHERE ua_ur.users_id = $1")
            .execute(of(useraccount_id))
            .onFailure().recoverWithNull()
            .onItem().ifNull().failWith(::ForbiddenException)
            .onItem().ifNotNull().transform { rs -> rs.map { it.getString("name") } }
            .await().asOptional().atMost(ofSeconds(10)).orElse(emptyList()).joinToString(separator = ",")
}

@sberyozkin
Copy link
Member

Let me close it as not planned for the (non-reactive) security JPA extension, please see an enhancement request at #23553

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/panache area/security env/windows Impacts Windows machines kind/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants