Embedded Keycloak springboot application, upgrading keycloak from 12 to 14 - spring-boot

Hello I have an SpringBoot application with embedded Keyclock created using the below example
https://www.baeldung.com/keycloak-embedded-in-spring-boot-app
I am trying to upgrade Keycloak from 12 to 14 and also upgraded resteasy to 3.15.1.Final, infinispan to 11.0.9.Final. The application fails to start and there is not much in the logs except for a NullPointerException. I am trying to see if any one tried the same and has any suggestions as there are not much in logs. Thanks
2021-07-07 10:02:05 [main] INFO org.keycloak.services - KC-SERVICES0050: Initializing master realm
2021-07-07 10:02:07 [main] DEBUG org.keycloak.keys.GeneratedRsaKeyProviderFactory - Generated keys for master
2021-07-07 10:02:07 [main] DEBUG org.keycloak.keys.GeneratedHmacKeyProviderFactory - Generated secret for master
2021-07-07 10:02:07 [main] DEBUG org.keycloak.keys.GeneratedAesKeyProviderFactory - Generated secret for master
2021-07-07 10:02:07 [main] DEBUG org.keycloak.connections.jpa.updater.liquibase.lock.LiquibaseDBLockProvider - Going to release database lock namespace=KEYCLOAK_BOOT
2021-07-07 10:02:07 [main] DEBUG org.keycloak.connections.jpa.updater.liquibase.lock.CustomLockService - Going to release database lock
2021-07-07 10:02:07 [main] DEBUG org.keycloak.models.cache.infinispan.InfinispanUserCacheProviderFactory - Registered cluster listeners
2021-07-07 10:02:07 [main] DEBUG org.keycloak.executors.DefaultExecutorsProviderFactory - We are in managed environment. Executor 'java:jboss/ee/concurrency/executor/default' was available.
2021-07-07 10:02:07 [main] DEBUG org.keycloak.executors.DefaultExecutorsProviderFactory - Found executor for 'storage-provider-threads' under JNDI name 'java:jboss/ee/concurrency/executor/storage-provider-threads'
2021-07-07 10:02:07 [main] FATAL org.keycloak.services - java.lang.NullPointerException
2021-07-07 10:02:07 [main] DEBUG org.keycloak.executors.DefaultExecutorsProviderFactory - Found executor for 'storage-provider-threads' under JNDI name 'java:jboss/ee/concurrency/executor/storage-provider-threads'
$$$$$$$$$$$$$$$$Stachtrace$$$$$$$$$$$$$ : java.lang.NullPointerException
at java.base/java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1011)
at java.base/java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006)
at org.keycloak.executors.DefaultExecutorsProviderFactory.getExecutor(DefaultExecutorsProviderFactory.java:113)
at org.keycloak.executors.DefaultExecutorsProviderFactory$1.getExecutor(DefaultExecutorsProviderFactory.java:68)
at org.keycloak.utils.ServicesUtils.timeBoundOne(ServicesUtils.java:71)
at org.keycloak.storage.AbstractStorageManager.mapEnabledStorageProvidersWithTimeout(AbstractStorageManager.java:135)
at org.keycloak.storage.UserStorageManager.getUsersCount(UserStorageManager.java:374)
at org.keycloak.models.cache.infinispan.UserCacheSession.getUsersCount(UserCacheSession.java:545)
at org.keycloak.storage.user.UserQueryProvider.getUsersCount(UserQueryProvider.java:52)
at org.keycloak.services.managers.ApplianceBootstrap.createMasterRealmUser(ApplianceBootstrap.java:99)

We had similar vague error messages when upgrading our embedded keycloak to 15 in our Spring Boot/Servlet-style application. But we eventually fixed them by crawling through the code/history of the project that this baeldung.com tutorial was based; thomasdarimont's Embedded Keycloak Server running in a Spring Boot App. This repo is a few upgrades past the initial tutorial; up to Keycloak 13. We compared the history to our code and applying/testing fixes incrementally for each version from keycloak 11 to 13. After that, the updates from 13 to 15 were a bit easier to understand, and the errors were not as cryptic.
Our application is not exactly like the example code given in the Baeldung tutorial, but below is a list of changes we needed to upgrade to Keycloak 15.0.1. These changes may not be 1:1 with your project, but hopefully these help someone save some time.
In KeycloakApplication moved the realm-setup functions from constructor method to bootstrap() method. (An example fix found here)
public class EmbeddedKeycloakApplication extends KeycloakApplication {
...
//public EmbeddedKeycloakApplication() {
// createMasterRealmAdminUser();
// createBaeldungRealm();
//}
#Override
protected ExportImportManager bootstrap() {
final ExportImportManager exportImportManager = super.bootstrap();
createMasterRealmAdminUser();
createBaeldungRealm();
return exportImportManager;
}
In the EmbeddedKeycloakConfig we added a couple new bean references/factories (Both found here)
public class EmbeddedKeycloakConfig {
...
#Bean("fixedThreadPool")
public ExecutorService fixedThreadPool() {
return Executors.newFixedThreadPool(5);
}
#Bean
#ConditionalOnMissingBean(name = "springBootPlatform")
protected SimplePlatformProvider springBootPlatform() {
return (SimplePlatformProvider) Platform.getPlatform();
}
In our keycloak-server.json, we removed the default 'concurrenthashmap' name from the mapStorage provider, but still provided the concurrenthashmap properties. This matches other Keycloak 15 examples. As Paul mentioned, the map_storage needs to be enabled, and the simplest solution is to use the system properties -Dkeycloak.profile.feature.map_storage=enabled -Dkeycloak.mapStorage.provider=concurrenthashmap. Similar examples can be found in the Keycloak test suites here and here . HOWEVER: We ran into errors when using these system properties, and decided it is not needed for our code. Luckilly, the 'mapStorage' is no specifically called out in the tutorial; it is only found in the Referenced Tutorial Code. So we simply matched other Keycloak 13+ examples and removed the reference in the keycloak-server.json (shown below) to get rid of the null pointer exception.
//Change this:
"mapStorage": {
"provider": "${keycloak.mapStorage.provider:concurrenthashmap}",
"concurrenthashmap": {
"dir": "${project.build.directory:target}"
}
},
//To This
"mapStorage": {
"provider": "${keycloak.mapStorage.provider:}",
"concurrenthashmap": {
"dir": "${project.build.directory:target}"
}
},
Upgraded the proper infinispan and rest versions with keycloak version. (Tutorial code reference)
< properties>
< keycloak.version>15.0.1</keycloak.version>
< !-- these should be updated together with Keycloak -->
< !-- check keycloak-dependencies-server-all effective pom -->
< infinispan.version>11.0.9.Final</infinispan.version>
< resteasy.version>3.15.1.Final/resteasy.version>
< /properties>
We also added the temp directory location in the SimplePlatformProvider, since this field was required. NOTE: The system temp folder, used in the example, is not recommended for production.
#Override
public File getTmpDirectory() {
return System.getProperty("java.io.tmpdir");
}
I hope someone finds this info useful, Good luck!

I’ve tried the same thing with keycloak 15 and I got the same result. It seems that keycloak’s ConcurrentHashMapStorageProvider isn’t taken in account.
EDIT: after looking into keycloak's code, I've found that MAP_STORAGE functionality is disabled by default (because the feature is tagged as EXPERIMENTAL). To resolve this issue you must launch your springboot app with the following Java option :
-Dkeycloak.profile.feature.map_storage=enabled
Unfortunately, I'm now facing another nullPointerException...

I have applied the changes suggested by #mrieman to the initial baeldung.com tutorial. It works fine with 17.0.1 version of Keycloak as of now. Hope it will help someone. My repository on Github is here

Related

Integrate Spring Boot Actuator with New Relic

I am trying to integrate New Relic with Spring Boot actuator. Most of the tutorials and response in StackOverflow itself suggest to use New Relic Java Agent but as per Spring Boot documentation installing Java Agent is not mandatory (unless I misunderstood something) also checked this. So, here is my application.properties currently.
management.metrics.export.newrelic.enabled = true
management.metrics.export.newrelic.api-key = <API_KEY>
management.metrics.export.newrelic.account-id = <ACCOUNT_ID>
logging.level.io.micrometer.newrelic=TRACE
management.metrics.export.newrelic.step=30s
and in the log I am seeing
2021-01-11 12:05:18.315 DEBUG 44635 --- [trics-publisher] i.m.n.NewRelicInsightsApiClientProvider : successfully sent 73 metrics to New Relic.
Based on this logs it looks like it is sending logs. But I have no idea where to see this logs. Ideally I would like to pass app name as well so that I can differentiate metric by app name and preferably by env as well later. Any suggestions?
To add "app name" and "env" to your metrics, you just need to configure the MeterFilter with the common tags:
#Configuration
public class MetricsConfig {
#Bean
public MeterFilter commonTagsMeterFilter(#Value("...") appName, #Value("...") env) {
return MeterFilter.commonTags(Tag.of("app name", appName), Tag.of("env", env);
}
}
Setting the following property you should be able to see what metrics are being sent to NewRelic:
logging.level.io.micrometer.newrelic=TRACE

Springboot - validate "native" properties at startup

Context: after a recent springboot/spring migration, I've been scratching my head on why my HAL URLs were now returned as HTTP and not HTTPS anymore.
After investigation, this was related to some deprecated (and removed) property replaced by another one (ok, Intellij was doing a proper job and highlighted this)
server.use-forward-headers=true
#replaced by
server.forward-headers-strategy=native
Whereas I know you can validate your own properties set via your #ConfigurationProperties, its parameters or even your own JSR annotations, is there a way to validate "native" properties (server., spring., ...) at application startup and/or build?
UC1: I want to be sure I'm not using any unknown property (e.g. the property has been totally removed or is misspelt)
UC2: I want to be sure I'm not using any deprecated property
Approaches tried:
Searching for an existing property that would do the job
Overriding ServerProperties (sounds like a bad idea)
Using an existing FailureAnalyzer or defining a custom one
Thanks for your help!
You can rely on your IDE but it can only show you the configuration files that you are managing. Those properties are potentially set as environment variables in an specific environment or using a remote config server you don't have access to.
That's one of the reasons we're providing spring-boot-properties-migrator. It is referenced in the release notes.
Here is a sample demo app that yield the following:
2020-07-18 19:19:54.168 INFO 69898 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication on taurus.lan with PID 69898 (/Users/snicoll/Downloads/demo-properties-migrator/target/classes started by snicoll in /Users/snicoll/Downloads/demo-properties-migrator)
2020-07-18 19:19:54.170 INFO 69898 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2020-07-18 19:19:54.545 INFO 69898 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 0.571 seconds (JVM running for 0.924)
2020-07-18 19:19:54.551 ERROR 69898 --- [ main] o.s.b.c.p.m.PropertiesMigrationListener :
The use of configuration keys that are no longer supported was found in the environment:
Property source 'systemProperties':
Key: server.connection-timeout
Reason: Each server behaves differently.
Property source 'applicationConfig: [classpath:/application.properties]':
Key: server.use-forward-headers
Line: 1
Reason: Replaced to support additional strategies.
Please refer to the release notes or reference guide for potential alternatives.
This will work for known properties (your use case) but it won't work for properties that are set and we don't know about (i.e. that don't have metadata). An obvious candidate in that set is typos of course but that's no longer an upgrade problem. Relying on your IDE to make sure that the property you introduce is legit sounds reasonable to me.
We'd like also to offer some validation for users that are adding properties without assistance, see this issue for more details.

Spring Cloud Kubernetes not reloading secret change

I am exploring the capabilities of Spring Cloud Kubernetes by using its feature to reload secrets on the fly. However, I still did not get this working.
I have a simple Spring Boot application that just prints out the contents of the secret that is mounted in the pod. Here is the configuration in bootstrap.properties
spring.cloud.kubernetes.reload.enabled=true
spring.cloud.kubernetes.reload.monitoring-secrets=true
spring.cloud.kubernetes.secrets.enabled=true
spring.cloud.kubernetes.secrets.paths=/etc/secret-volume
management.endpoint.info.enabled=true
management.endpoint.health.enabled=true
management.endpoint.restart.enabled=true
In application.properties, I have defined the property to get the value of the secret:
mysecret.password=${MY-PWD}
In the Spring Boot application, I defined a bean that will store the value of the secret:
#Configuration
#ConfigurationProperties(prefix = "mysecret")
public class MySecret {
private String password;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
When I run the application on minikube, I see in the logs that Spring detects the declared secret and activates the profile:
16:54:30.887 [main] INFO
o.s.c.b.c.PropertySourceBootstrapConfiguration - Located property
source: [BootstrapPropertySource#1132379993
{name='bootstrapProperties-my-pwd', properties={MY-PWD=qwerty}}]
16:54:30.899 [main] INFO c.c.r.ReloadSecretsApplication - The
following profiles are active: kubernetes
After a while, I get the following log that says that it was added a watcher over the secret:
16:54:35.460 [OkHttp https://10.96.0.1/...] DEBUG
i.f.k.c.d.i.WatchConnectionManager - WebSocket successfully opened
16:54:35.460 [main] INFO
o.s.c.k.c.r.EventBasedConfigurationChangeDetector - Added new
Kubernetes watch: secrets-watch 16:54:35.460 [main] INFO
o.s.c.k.c.r.EventBasedConfigurationChangeDetector - Kubernetes
event-based configuration change detector activated
Then, when I change the secret, I get this line saying that the reload won't be triggered:
11:20:15.963 [OkHttp https://10.96.0.1/...] WARN
o.s.c.k.c.r.EventBasedConfigurationChangeDetector - The current number
of Confimap PropertySources does not match the ones loaded from the
Kubernetes - No reload will take place
The documentation is very scarce about this topic.
Do I have any missing configuration here?
Link to the Spring Boot Application: https://github.com/Azlop/spring-cloud-kubernetes-reload-secrets
I know this is an old question without answer. I met the same issue and solved it after some research. Here I share my experience on this question if it can help someone in the future.
Spring Cloud Kubernetes has stored the external configmaps as several ConfigMapPropertySource instances in a CompositePropertySource or BootstrapPropertySource. When the application minitors the configmaps have been changed, it takes the ConfigMapPropertySource instances from the CompositePropertySource and compares the length of the ConfigMapPropertySource instances with the incoming instances. If the lengths are not same, the application returns an error message: "The current number of Confimap PropertySources does not match the ones loaded from the Kubernetes - No reload will take place".
But here is a potential problem: The other functions, in my case it is an encryptor, may change the type of the CompositePropertySource. Later, the application can't get the ConfigMapPropertySource instances and the comparison must be failed.
So my solution is to create a customized EventBasedConfigurationChangeDetector which has been changed the logic of the comparison. The original version is:
private void onEvent(ConfigMap configMap) {
boolean changed = changed(locateMapPropertySources(this.configMapPropertySourceLocator, this.environment), findPropertySources(ConfigMapPropertySource.class));
if (changed) {
XXXX
}
}
You should rewrite the findPropertySources function to fit in your stituation. In my instance, I unwrap the encrypted PropertySource to obtain the original ConfigMapPropertySource instances.
Finally, injecting your customized EventBasedConfigurationChangeDetector to replace the vanilla one will resolve the issue.

Has anyone migrated Spring Security from 3.1.2 to 5.1.4

Team
Just trying to migrate Spring Security from 3.1.2.RELEASE to 5.1.4.RELEASE. We are using xml based configuration and checking if there is any reference around that helps me in this activity.
I have done the changes and resolved most of run time issues except the below one. I believe it is also not making any call to the user details service.
SecurityContextPersistenceFilter: Eagerly created session:
a4931bcd-f260-4382-ad88-46237bed8fce 10:43:35,192 INFO [stdout]
(http-/127.0.0.1:8080-3) http-/127.0.0.1:8080-3 - [2019-03-15
10:43:35,192] - DEBUG - HttpSessionSecurityContextRepository:
HttpSession returned null object for SPRING_SECURITY_CONTEXT
10:43:35,192 INFO [stdout] (http-/127.0.0.1:8080-3)
http-/127.0.0.1:8080-3 - [2019-03-15 10:43:35,192] - DEBUG -
HttpSessionSecurityContextRepository: No SecurityContext was available
from the HttpSession:
org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper#32436149.
A new one will be created.
Below is the configuration I've in our code.
<bean id="securityContextRepository"
class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />
Appreciate any help on this. Thanks in advance.
Regards
Ravi
For a first step please refer to the Migration Guide for Update Spring Security 3 to 4.
Migrating to Spring Security 5 is then a rather small step and mainly about password migration.

Tenant not found during startup of cloud-s4-sdk-book / RequestContextServletFilter issue

During (local) startup of the example application cloud-s4-sdk-book in the branch 15_2_spring_boot the following exception is thrown:
16:01:38.786 [localhost-startStop-1] DEBUG com.sap.cloud.sdk.cloudplatform.servlet.RequestContextServletFilter - Filter initialization parameter factory not defined.
16:01:38.787 [localhost-startStop-1] INFO com.sap.cloud.sdk.cloudplatform.servlet.RequestContextServletFilter - No RequestContextFactory configured. Falling back to DefaultRequestContextFactory.
16:01:38.798 [localhost-startStop-1] INFO com.sap.cloud.sdk.cloudplatform.servlet.RequestContextListenerChain - Added default listener com.sap.cloud.sdk.cloudplatform.security.user.UserRequestContextListener with priority -4.
16:01:38.798 [localhost-startStop-1] INFO com.sap.cloud.sdk.cloudplatform.servlet.RequestContextListenerChain - Added default listener com.sap.cloud.sdk.cloudplatform.tenant.TenantRequestContextListener with priority -3.
16:01:38.799 [localhost-startStop-1] INFO com.sap.cloud.sdk.cloudplatform.servlet.RequestContextListenerChain - Added default listener com.sap.cloud.sdk.cloudplatform.connectivity.DestinationsRequestContextListener with priority -2.
16:01:38.799 [localhost-startStop-1] INFO com.sap.cloud.sdk.cloudplatform.servlet.RequestContextListenerChain - Added default listener com.sap.cloud.sdk.cloudplatform.security.AuthTokenRequestContextListener with priority -5.
16:01:43.862 [main] WARN com.sap.cloud.s4hana.examples.addressmgr.config.TenantIdentifierResolver - Tenant not found
com.sap.cloud.sdk.cloudplatform.tenant.exception.TenantAccessException: Failed to get current tenant: no RequestContext available. Have you correctly configured a RequestContextServletFilter or have you wrapped your logic in a RequestContextExecutor when executing background tasks that are not triggered by a request?
The exception is thrown inside method "loadFactory" of class RequestContextServletFilter.
I am starting the application using mvn spring-boot:run.
What can I do the get the application started correctly?
This exception is expected and can usually be ignored if you are running this application locally, where no tenant is available. The application should still work, because there is a fallback in place to a tenant identifier default-tenant .
Keep in mind that you need to subscribe the default tenant before interacting with the database - do an HTTP request PUT /api/callback/tenant/default-tenant.
Please note that this exception is only being logged as a warning. If you want to clean the log, adapt the TenantIdentifierResolver class.

Resources