Make it possible to set a custom user and cache providers when using legacy store

Closes #15008
This commit is contained in:
Pedro Igor 2022-12-14 16:53:06 -03:00 committed by Václav Muzikář
parent 09da4a211e
commit f32e012c11
11 changed files with 177 additions and 10 deletions

View file

@ -18,9 +18,11 @@
package org.keycloak.quarkus.runtime.configuration.mappers; package org.keycloak.quarkus.runtime.configuration.mappers;
import static java.util.Optional.of; import static java.util.Optional.of;
import static java.util.function.Predicate.not;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream;
import org.keycloak.config.StorageOptions; import org.keycloak.config.StorageOptions;
import org.keycloak.config.StorageOptions.StorageType; import org.keycloak.config.StorageOptions.StorageType;
@ -301,11 +303,27 @@ final class StoragePropertyMappers {
} }
private static Optional<String> getAreaStorage(Optional<String> storage, ConfigSourceInterceptorContext context) { private static Optional<String> getAreaStorage(Optional<String> storage, ConfigSourceInterceptorContext context) {
return of(storage.isEmpty() ? "jpa" : "map"); if (storage.isEmpty()) {
return of("jpa");
}
if (Stream.of(StorageType.values()).map(Enum::name).anyMatch(storage.get()::equals)) {
return of("map");
}
return storage;
} }
private static Optional<String> getCacheStorage(Optional<String> storage, ConfigSourceInterceptorContext context) { private static Optional<String> getCacheStorage(Optional<String> storage, ConfigSourceInterceptorContext context) {
return of(storage.isEmpty() ? "infinispan" : "map"); if (storage.isEmpty()) {
return of("infinispan");
}
if (Stream.of(StorageType.values()).map(Enum::name).anyMatch(storage.get()::equals)) {
return of("map");
}
return storage;
} }
private static Optional<String> getGlobalLockProvider(Optional<String> storage, ConfigSourceInterceptorContext context) { private static Optional<String> getGlobalLockProvider(Optional<String> storage, ConfigSourceInterceptorContext context) {

View file

@ -15,13 +15,13 @@
* limitations under the License. * limitations under the License.
*/ */
package com.acme.provider.user; package com.acme.provider.legacy.jpa.entity;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import org.keycloak.it.TestProvider; import org.keycloak.it.TestProvider;
public class CustomUserProvider implements TestProvider { public class CustomLegacyJpaEntityProvider implements TestProvider {
@Override @Override
public Class[] getClasses() { public Class[] getClasses() {

View file

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.acme.provider.user; package com.acme.provider.legacy.jpa.entity;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Id; import javax.persistence.Id;

View file

@ -0,0 +1,36 @@
/*
* Copyright 2022 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.acme.provider.legacy.jpa.user;
import java.util.Collections;
import java.util.Map;
import org.keycloak.it.TestProvider;
import com.acme.provider.legacy.jpa.entity.Realm;
public class CustomLegacyUserProvider implements TestProvider {
@Override
public Class[] getClasses() {
return new Class[] { MyUserProvider.class, MyUserProviderFactory.class };
}
@Override
public Map<String, String> getManifestResources() {
return Collections.singletonMap("org.keycloak.models.UserProviderFactory", "services/org.keycloak.models.UserProviderFactory");
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright 2022 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.acme.provider.legacy.jpa.user;
import javax.persistence.EntityManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.jpa.JpaUserProvider;
public class MyUserProvider extends JpaUserProvider {
public MyUserProvider(KeycloakSession session, EntityManager em) {
super(session, em);
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright 2022 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.acme.provider.legacy.jpa.user;
import javax.persistence.EntityManager;
import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserProvider;
import org.keycloak.models.jpa.JpaUserProviderFactory;
public class MyUserProviderFactory extends JpaUserProviderFactory {
@Override
public UserProvider create(KeycloakSession session) {
EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityManager();
return new MyUserProvider(session, em);
}
@Override
public String getId() {
return "custom_jpa";
}
}

View file

@ -22,7 +22,7 @@
http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="user-store" transaction-type="JTA"> <persistence-unit name="user-store" transaction-type="JTA">
<class>com.acme.provider.user.Realm</class> <class>com.acme.provider.legacy.jpa.entity.Realm</class>
<properties> <properties>
<property name="hibernate.dialect" value="io.quarkus.hibernate.orm.runtime.dialect.QuarkusH2Dialect" /> <property name="hibernate.dialect" value="io.quarkus.hibernate.orm.runtime.dialect.QuarkusH2Dialect" />
<!-- Sets the name of the datasource to be the same as the datasource name in quarkus.properties--> <!-- Sets the name of the datasource to be the same as the datasource name in quarkus.properties-->

View file

@ -0,0 +1 @@
com.acme.provider.legacy.jpa.user.MyUserProviderFactory

View file

@ -23,7 +23,7 @@ import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.LegacyStore; import org.keycloak.it.junit5.extension.LegacyStore;
import org.keycloak.it.junit5.extension.RawDistOnly; import org.keycloak.it.junit5.extension.RawDistOnly;
import org.keycloak.it.junit5.extension.TestProvider; import org.keycloak.it.junit5.extension.TestProvider;
import com.acme.provider.user.CustomUserProvider; import com.acme.provider.legacy.jpa.entity.CustomLegacyJpaEntityProvider;
import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult; import io.quarkus.test.junit.main.LaunchResult;
@ -31,15 +31,15 @@ import io.quarkus.test.junit.main.LaunchResult;
@DistributionTest @DistributionTest
@RawDistOnly(reason = "Containers are immutable") @RawDistOnly(reason = "Containers are immutable")
@LegacyStore @LegacyStore
public class CustomJpaUserProviderDistTest { public class CustomLegacyJpaEntityProviderDistTest {
@Test @Test
@TestProvider(CustomUserProvider.class) @TestProvider(CustomLegacyJpaEntityProvider.class)
@Launch({ "start-dev", "--log-level=org.hibernate.jpa.internal.util.LogHelper:debug" }) @Launch({ "start-dev", "--log-level=org.hibernate.jpa.internal.util.LogHelper:debug" })
void testUserManagedEntityNotAddedToDefaultPU(LaunchResult result) { void testUserManagedEntityNotAddedToDefaultPU(LaunchResult result) {
CLIResult cliResult = (CLIResult) result; CLIResult cliResult = (CLIResult) result;
cliResult.assertStringCount("name: user-store", 1); cliResult.assertStringCount("name: user-store", 1);
cliResult.assertStringCount("com.acme.provider.user.Realm", 1); cliResult.assertStringCount("com.acme.provider.legacy.jpa.entity.Realm", 1);
cliResult.assertStartedDevMode(); cliResult.assertStartedDevMode();
} }
} }

View file

@ -0,0 +1,45 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.it.cli.dist;
import org.junit.jupiter.api.Test;
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.LegacyStore;
import org.keycloak.it.junit5.extension.RawDistOnly;
import org.keycloak.it.junit5.extension.TestProvider;
import com.acme.provider.legacy.jpa.entity.CustomLegacyJpaEntityProvider;
import com.acme.provider.legacy.jpa.user.CustomLegacyUserProvider;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
@DistributionTest
@RawDistOnly(reason = "Containers are immutable")
@LegacyStore
public class CustomLegacyUserProviderDistTest {
@Test
@TestProvider(CustomLegacyUserProvider.class)
@Launch({ "start-dev", "--spi-user-provider=custom_jpa --spi-user-jpa-enabled=false" })
void testUserManagedEntityNotAddedToDefaultPU(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertMessage("KC-SERVICES0047: custom_jpa (com.acme.provider.legacy.jpa.user.MyUserProviderFactory) is implementing the internal SPI user. This SPI is internal and may change without notice");
cliResult.assertStartedDevMode();
}
}