Skip to content

Commit

Permalink
Merge pull request quarkusio#36728 from michalvavrik/feature/security…
Browse files Browse the repository at this point in the history
…-jpa-named-unit

JPA Security: allow pointing to a named persistence unit
  • Loading branch information
sberyozkin authored Oct 27, 2023
2 parents fe07266 + f8725da commit a5c429c
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 0 deletions.
2 changes: 2 additions & 0 deletions docs/src/main/asciidoc/security-jpa.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ For applications running in a production environment, do not store passwords as
However, it is possible to store passwords as plain text with the `@Password(PasswordType.CLEAR)` annotation when operating in a test environment.
====

include::{generated-dir}/config/quarkus-security-jpa.adoc[opts=optional, leveloffset=+2]

== References

* xref:security-getting-started-tutorial.adoc[Getting Started with Security using Basic authentication and Jakarta Persistence]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.security.jpa.deployment;

import static io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME;
import static io.quarkus.security.jpa.common.deployment.JpaSecurityIdentityUtil.buildIdentity;
import static io.quarkus.security.jpa.common.deployment.JpaSecurityIdentityUtil.buildTrustedIdentity;

Expand All @@ -8,21 +9,27 @@
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.BooleanSupplier;

import jakarta.inject.Singleton;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Query;

import org.hibernate.Session;
import org.hibernate.SimpleNaturalIdLoadAccess;
import org.hibernate.annotations.NaturalId;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Index;
import org.jboss.jandex.Type;

import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor;
import io.quarkus.arc.deployment.InjectionPointTransformerBuildItem;
import io.quarkus.arc.processor.InjectionPointsTransformer;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
Expand All @@ -34,6 +41,7 @@
import io.quarkus.gizmo.MethodCreator;
import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.hibernate.orm.PersistenceUnit;
import io.quarkus.panache.common.deployment.PanacheEntityClassesBuildItem;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.identity.request.TrustedAuthenticationRequest;
Expand All @@ -48,6 +56,11 @@
class QuarkusSecurityJpaProcessor {

private static final DotName DOTNAME_NATURAL_ID = DotName.createSimple(NaturalId.class.getName());
private static final DotName ENTITY_MANAGER_FACTORY_FACTORY = DotName.createSimple(EntityManagerFactory.class.getName());
private static final DotName JPA_IDENTITY_PROVIDER_NAME = DotName.createSimple(JpaIdentityProvider.class.getName());
private static final DotName JPA_TRUSTED_IDENTITY_PROVIDER_NAME = DotName
.createSimple(JpaTrustedIdentityProvider.class.getName());
private static final DotName PERSISTENCE_UNIT_NAME = DotName.createSimple(PersistenceUnit.class.getName());

@BuildStep
FeatureBuildItem feature() {
Expand All @@ -71,6 +84,30 @@ void configureJpaAuthConfig(ApplicationIndexBuildItem index,
}
}

@BuildStep(onlyIf = EnabledIfNonDefaultPersistenceUnit.class)
InjectionPointTransformerBuildItem transformer(SecurityJpaBuildTimeConfig config) {
return new InjectionPointTransformerBuildItem(new InjectionPointsTransformer() {

@Override
public boolean appliesTo(Type requiredType) {
return requiredType.name().equals(ENTITY_MANAGER_FACTORY_FACTORY);
}

public void transform(TransformationContext context) {
if (context.getTarget().kind() == AnnotationTarget.Kind.FIELD) {
var declaringClassName = context.getTarget().asField().declaringClass().name();
if (JPA_IDENTITY_PROVIDER_NAME.equals(declaringClassName)
|| JPA_TRUSTED_IDENTITY_PROVIDER_NAME.equals(declaringClassName)) {
context.transform()
.add(PERSISTENCE_UNIT_NAME,
AnnotationValue.createStringValue("value", config.persistenceUnitName()))
.done();
}
}
}
});
}

@BuildStep
PanacheEntityPredicateBuildItem panacheEntityPredicate(List<PanacheEntityClassesBuildItem> panacheEntityClasses) {
return new PanacheEntityPredicateBuildItem(collectPanacheEntities(panacheEntityClasses));
Expand Down Expand Up @@ -197,4 +234,18 @@ private ResultHandle lookupUserById(JpaSecurityDefinition jpaSecurityDefinition,
return user;
}

static final class EnabledIfNonDefaultPersistenceUnit implements BooleanSupplier {

private final boolean useNonDefaultPersistenceUnit;

public EnabledIfNonDefaultPersistenceUnit(SecurityJpaBuildTimeConfig config) {
this.useNonDefaultPersistenceUnit = !DEFAULT_PERSISTENCE_UNIT_NAME.equals(config.persistenceUnitName());
}

@Override
public boolean getAsBoolean() {
return useNonDefaultPersistenceUnit;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.quarkus.security.jpa.deployment;

import static io.quarkus.hibernate.orm.runtime.PersistenceUnitUtil.DEFAULT_PERSISTENCE_UNIT_NAME;

import io.quarkus.runtime.annotations.ConfigRoot;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;

@ConfigRoot
@ConfigMapping(prefix = "quarkus.security-jpa")
public interface SecurityJpaBuildTimeConfig {

/**
* Selects the Hibernate ORM persistence unit. Default persistence unit is used when no value is specified.
*/
@WithDefault(DEFAULT_PERSISTENCE_UNIT_NAME)
String persistenceUnitName();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.quarkus.security.jpa;

import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;

public class NamedPersistenceUnitTest extends JpaSecurityRealmTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(testClasses)
.addClass(MinimalUserEntity.class)
.addAsResource("named-persistence-unit/import.sql", "import.sql")
.addAsResource("named-persistence-unit/application.properties", "application.properties"));

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
quarkus.datasource.heureka.db-kind=h2
quarkus.datasource.heureka.username=sa
quarkus.datasource.heureka.password=sa
quarkus.datasource.heureka.jdbc.url=jdbc:h2:mem:named-pu'
quarkus.hibernate-orm.heureka.datasource=heureka
quarkus.hibernate-orm.heureka.sql-load-script=import.sql
quarkus.hibernate-orm.heureka.database.generation=drop-and-create
quarkus.hibernate-orm.heureka.packages=io.quarkus.security.jpa
quarkus.security-jpa.persistence-unit-name=heureka
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
INSERT INTO test_user (id, username, password, role) VALUES (1, 'admin', 'admin', 'admin');
INSERT INTO test_user (id, username, password, role) VALUES (2, 'user','user', 'user');
INSERT INTO test_user (id, username, password, role) VALUES (3, 'noRoleUser','noRoleUser', '');

0 comments on commit a5c429c

Please sign in to comment.