Fix deprecations in common module

- Use charset in `Encode` class
- Replace reflective call to protected `Liquibase#resetServices()` with call to exposed public method on a custom subclass `KeycloakLiquibase`
- Remove usage of deprecated AccessController class in Reflections
- Deprecated SetAccessibleProvilegedAction and UnsetAccessibleProvilegedAction

Fixes #22209

Signed-off-by: Thomas Darimont <thomas.darimont@googlemail.com>
Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
Co-authored-by: Alexander Schwartz <aschwart@redhat.com>
This commit is contained in:
Thomas Darimont 2024-07-02 18:02:35 +02:00 committed by GitHub
parent a8aa410ad3
commit f34bb21af6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 109 additions and 46 deletions

View file

@ -86,7 +86,11 @@ public class Encode
case '@':
continue;
}
pathEncoding[i] = URLEncoder.encode(String.valueOf((char) i));
try {
pathEncoding[i] = URLEncoder.encode(String.valueOf((char) i), UTF_8);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
pathEncoding[' '] = "%20";
System.arraycopy(pathEncoding, 0, matrixParameterEncoding, 0, pathEncoding.length);
@ -119,7 +123,11 @@ public class Encode
queryNameValueEncoding[i] = "+";
continue;
}
queryNameValueEncoding[i] = URLEncoder.encode(String.valueOf((char) i));
try {
queryNameValueEncoding[i] = URLEncoder.encode(String.valueOf((char) i), UTF_8);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/*
@ -157,7 +165,11 @@ public class Encode
queryStringEncoding[i] = "%20";
continue;
}
queryStringEncoding[i] = URLEncoder.encode(String.valueOf((char) i));
try {
queryStringEncoding[i] = URLEncoder.encode(String.valueOf((char) i), UTF_8);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/*

View file

@ -31,7 +31,6 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.security.AccessController;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@ -398,7 +397,7 @@ public class Reflections {
/**
* Set the accessibility flag on the {@link AccessibleObject} as described in {@link
* AccessibleObject#setAccessible(boolean)} within the context of a {link PrivilegedAction}.
* AccessibleObject#setAccessible(boolean)}.
*
* @param <A> member the accessible object type
* @param member the accessible object
@ -406,13 +405,13 @@ public class Reflections {
* @return the accessible object after the accessible flag has been altered
*/
public static <A extends AccessibleObject> A setAccessible(A member) {
AccessController.doPrivileged(new SetAccessiblePrivilegedAction(member));
member.setAccessible(true);
return member;
}
/**
* Set the accessibility flag on the {@link AccessibleObject} to false as described in {@link
* AccessibleObject#setAccessible(boolean)} within the context of a {link PrivilegedAction}.
* AccessibleObject#setAccessible(boolean)}.
*
* @param <A> member the accessible object type
* @param member the accessible object
@ -420,7 +419,7 @@ public class Reflections {
* @return the accessible object after the accessible flag has been altered
*/
public static <A extends AccessibleObject> A unsetAccessible(A member) {
AccessController.doPrivileged(new UnSetAccessiblePrivilegedAction(member));
member.setAccessible(false);
return member;
}
@ -987,7 +986,9 @@ public class Reflections {
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
* @deprecated for removal in Keycloak 27
*/
@Deprecated(forRemoval = true)
public static <T> T newInstance(final Class<T> fromClass) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
return newInstance(fromClass, fromClass.getName());
}
@ -1005,7 +1006,9 @@ public class Reflections {
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
* @deprecated for removal in Keycloak 27
*/
@Deprecated(forRemoval = true)
public static <T> T newInstance(final Class<?> type, final String fullQualifiedName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
return (T) classForName(fullQualifiedName, type.getClassLoader()).newInstance();
}

View file

@ -22,7 +22,9 @@ import java.security.PrivilegedAction;
/**
* A {@link java.security.PrivilegedAction} that calls {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}
* @deprecated for removal in Keycloak 27
*/
@Deprecated(forRemoval = true)
public class SetAccessiblePrivilegedAction implements PrivilegedAction<Void> {
private final AccessibleObject member;

View file

@ -22,7 +22,9 @@ import java.security.PrivilegedAction;
/**
* A {@link PrivilegedAction} that calls {@link AccessibleObject#setAccessible(boolean)}
* @deprecated for removal in Keycloak 27
*/
@Deprecated(forRemoval = true)
public class UnSetAccessiblePrivilegedAction implements PrivilegedAction<Void> {
private final AccessibleObject member;

View file

@ -24,3 +24,16 @@ The default implementation performs a single network call per an event, and it w
= Operator's default CPU and memory limits/requests
In order to follow the best practices, the default CPU and memory limits/requests for the Operator were introduced. It affects both non-OLM and OLM installs. To override the default values for the OLM install, edit the `resources` section in the operator's https://github.com/operator-framework/operator-lifecycle-manager/blob/master/doc/design/subscription-config.md#resources[subscription].
= Deprecations in `keycloak-common` module
The following items have been deprecated for removal in upcoming {project_name} versions with no replacement:
- `org.keycloak.common.util.reflections.Reflections.newInstance(java.lang.Class<T>)`
- `org.keycloak.common.util.reflections.Reflections.newInstance(java.lang.Class<?>, java.lang.String)`
- `org.keycloak.common.util.reflections.SetAccessiblePrivilegedAction`
- `org.keycloak.common.util.reflections.UnSetAccessiblePrivilegedAction`
= Consistent usage of UTF-8 charset for URL encoding
`org.keycloak.common.util.Encode` now always uses the `UTF-8` charset for URL encoding instead relying implicitly on the `file.encoding` system property.

View file

@ -42,10 +42,10 @@ import liquibase.structure.core.Column;
import liquibase.structure.core.Table;
import liquibase.util.StreamUtil;
import org.jboss.logging.Logger;
import org.keycloak.common.util.reflections.Reflections;
import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
import org.keycloak.connections.jpa.updater.liquibase.conn.CustomChangeLogHistoryService;
import org.keycloak.connections.jpa.updater.liquibase.conn.KeycloakLiquibase;
import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProvider;
import org.keycloak.connections.jpa.util.JpaUtils;
import org.keycloak.models.KeycloakSession;
@ -101,7 +101,7 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
Writer exportWriter = null;
try {
// Run update with keycloak master changelog first
Liquibase liquibase = getLiquibaseForKeycloakUpdate(connection, defaultSchema);
KeycloakLiquibase liquibase = getLiquibaseForKeycloakUpdate(connection, defaultSchema);
if (file != null) {
exportWriter = new FileWriter(file);
}
@ -133,7 +133,7 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
}
}
protected void updateChangeSet(Liquibase liquibase, Writer exportWriter) throws LiquibaseException, SQLException {
protected void updateChangeSet(KeycloakLiquibase liquibase, Writer exportWriter) throws LiquibaseException, SQLException {
String changelog = liquibase.getChangeLogFile();
Database database = liquibase.getDatabase();
Table changelogTable = SnapshotGeneratorFactory.getInstance().getDatabaseChangeLogTable(new SnapshotControl(database, false, Table.class, Column.class), database);
@ -235,7 +235,7 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
try {
// Validate with keycloak master changelog first
Liquibase liquibase = getLiquibaseForKeycloakUpdate(connection, defaultSchema);
KeycloakLiquibase liquibase = getLiquibaseForKeycloakUpdate(connection, defaultSchema);
Status status = validateChangeSet(liquibase, liquibase.getChangeLogFile());
if (status != Status.VALID) {
@ -262,7 +262,7 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
return Status.VALID;
}
protected Status validateChangeSet(Liquibase liquibase, String changelog) throws LiquibaseException {
protected Status validateChangeSet(KeycloakLiquibase liquibase, String changelog) throws LiquibaseException {
final Status result;
List<ChangeSet> changeSets = getLiquibaseUnrunChangeSets(liquibase);
@ -285,10 +285,8 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
return result;
}
private void resetLiquibaseServices(Liquibase liquibase) {
Method resetServices = Reflections.findDeclaredMethod(Liquibase.class, "resetServices");
Reflections.invokeMethod(true, resetServices, liquibase);
private void resetLiquibaseServices(KeycloakLiquibase liquibase) {
liquibase.resetServices();
ChangeLogHistoryServiceFactory.getInstance().register(new CustomChangeLogHistoryService());
}
@ -296,12 +294,12 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
return liquibase.listUnrunChangeSets(null, new LabelExpression(), false);
}
private Liquibase getLiquibaseForKeycloakUpdate(Connection connection, String defaultSchema) throws LiquibaseException {
private KeycloakLiquibase getLiquibaseForKeycloakUpdate(Connection connection, String defaultSchema) throws LiquibaseException {
LiquibaseConnectionProvider liquibaseProvider = session.getProvider(LiquibaseConnectionProvider.class);
return liquibaseProvider.getLiquibase(connection, defaultSchema);
}
private Liquibase getLiquibaseForCustomProviderUpdate(Connection connection, String defaultSchema, String changelogLocation, ClassLoader classloader, String changelogTableName) throws LiquibaseException {
private KeycloakLiquibase getLiquibaseForCustomProviderUpdate(Connection connection, String defaultSchema, String changelogLocation, ClassLoader classloader, String changelogTableName) throws LiquibaseException {
LiquibaseConnectionProvider liquibaseProvider = session.getProvider(LiquibaseConnectionProvider.class);
return liquibaseProvider.getLiquibaseForCustomUpdate(connection, defaultSchema, changelogLocation, classloader, changelogTableName);
}

View file

@ -17,7 +17,6 @@
package org.keycloak.connections.jpa.updater.liquibase.conn;
import liquibase.Liquibase;
import liquibase.Scope;
import liquibase.ScopeManager;
import liquibase.ThreadLocalScopeManager;
@ -114,7 +113,7 @@ public class DefaultLiquibaseConnectionProvider implements LiquibaseConnectionPr
}
@Override
public Liquibase getLiquibase(Connection connection, String defaultSchema) throws LiquibaseException {
public KeycloakLiquibase getLiquibase(Connection connection, String defaultSchema) throws LiquibaseException {
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
if (defaultSchema != null) {
database.setDefaultSchemaName(defaultSchema);
@ -126,11 +125,11 @@ public class DefaultLiquibaseConnectionProvider implements LiquibaseConnectionPr
logger.debugf("Using changelog file %s and changelogTableName %s", changelog, database.getDatabaseChangeLogTableName());
((AbstractJdbcDatabase) database).set(INDEX_CREATION_THRESHOLD_PARAM, indexCreationThreshold);
return new Liquibase(changelog, resourceAccessor, database);
return new KeycloakLiquibase(changelog, resourceAccessor, database);
}
@Override
public Liquibase getLiquibaseForCustomUpdate(Connection connection, String defaultSchema, String changelogLocation, ClassLoader classloader, String changelogTableName) throws LiquibaseException {
public KeycloakLiquibase getLiquibaseForCustomUpdate(Connection connection, String defaultSchema, String changelogLocation, ClassLoader classloader, String changelogTableName) throws LiquibaseException {
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
if (defaultSchema != null) {
database.setDefaultSchemaName(defaultSchema);
@ -141,7 +140,7 @@ public class DefaultLiquibaseConnectionProvider implements LiquibaseConnectionPr
logger.debugf("Using changelog file %s and changelogTableName %s", changelogLocation, database.getDatabaseChangeLogTableName());
return new Liquibase(changelogLocation, resourceAccessor, database);
return new KeycloakLiquibase(changelogLocation, resourceAccessor, database);
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright 2023 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.connections.jpa.updater.liquibase.conn;
import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.resource.ResourceAccessor;
/**
* Custom subclass to expose protected liquibase API.
*/
public class KeycloakLiquibase extends Liquibase {
public KeycloakLiquibase(String changeLogFile, ResourceAccessor resourceAccessor, Database database) {
super(changeLogFile, resourceAccessor, database);
}
@Override
public void resetServices() {
// expose protected method for use without reflection
super.resetServices();
}
}

View file

@ -17,7 +17,6 @@
package org.keycloak.connections.jpa.updater.liquibase.conn;
import liquibase.Liquibase;
import liquibase.exception.LiquibaseException;
import org.keycloak.provider.Provider;
@ -28,8 +27,8 @@ import java.sql.Connection;
*/
public interface LiquibaseConnectionProvider extends Provider {
Liquibase getLiquibase(Connection connection, String defaultSchema) throws LiquibaseException;
KeycloakLiquibase getLiquibase(Connection connection, String defaultSchema) throws LiquibaseException;
Liquibase getLiquibaseForCustomUpdate(Connection connection, String defaultSchema, String changelogLocation, ClassLoader classloader, String changelogTableName) throws LiquibaseException;
KeycloakLiquibase getLiquibaseForCustomUpdate(Connection connection, String defaultSchema, String changelogLocation, ClassLoader classloader, String changelogTableName) throws LiquibaseException;
}

View file

@ -21,7 +21,6 @@ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
@ -31,12 +30,12 @@ import java.util.Set;
import liquibase.Scope;
import org.jboss.logging.Logger;
import org.keycloak.common.util.reflections.Reflections;
import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
import org.keycloak.connections.jpa.updater.liquibase.LiquibaseConstants;
import org.keycloak.connections.jpa.updater.liquibase.ThreadLocalSessionContext;
import org.keycloak.connections.jpa.updater.liquibase.conn.CustomChangeLogHistoryService;
import org.keycloak.connections.jpa.updater.liquibase.conn.KeycloakLiquibase;
import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProvider;
import org.keycloak.connections.jpa.util.JpaUtils;
import org.keycloak.models.KeycloakSession;
@ -100,7 +99,7 @@ public class QuarkusJpaUpdaterProvider implements JpaUpdaterProvider {
try {
if (needVerifyMasterChangelog()) {
// Run update with keycloak master changelog first
Liquibase liquibase = getLiquibaseForKeycloakUpdate(connection, defaultSchema);
KeycloakLiquibase liquibase = getLiquibaseForKeycloakUpdate(connection, defaultSchema);
if (file != null) {
exportWriter = new FileWriter(file);
}
@ -114,7 +113,7 @@ public class QuarkusJpaUpdaterProvider implements JpaUpdaterProvider {
if (customChangelog != null) {
String factoryId = jpaProvider.getFactoryId();
String changelogTableName = JpaUtils.getCustomChangelogTableName(factoryId);
Liquibase liquibase = getLiquibaseForCustomProviderUpdate(connection, defaultSchema, customChangelog, jpaProvider.getClass().getClassLoader(), changelogTableName);
KeycloakLiquibase liquibase = getLiquibaseForCustomProviderUpdate(connection, defaultSchema, customChangelog, jpaProvider.getClass().getClassLoader(), changelogTableName);
updateChangeSet(liquibase, exportWriter);
}
}
@ -136,7 +135,7 @@ public class QuarkusJpaUpdaterProvider implements JpaUpdaterProvider {
return session.getAttributeOrDefault(VERIFY_AND_RUN_MASTER_CHANGELOG, Boolean.TRUE);
}
protected void updateChangeSet(Liquibase liquibase, Writer exportWriter) throws LiquibaseException {
protected void updateChangeSet(KeycloakLiquibase liquibase, Writer exportWriter) throws LiquibaseException {
String changelog = liquibase.getChangeLogFile();
Database database = liquibase.getDatabase();
Table changelogTable = SnapshotGeneratorFactory.getInstance().getDatabaseChangeLogTable(new SnapshotControl(database, false, Table.class, Column.class), database);
@ -232,7 +231,7 @@ public class QuarkusJpaUpdaterProvider implements JpaUpdaterProvider {
try {
if (needVerifyMasterChangelog()) {
// Validate with keycloak master changelog first
Liquibase liquibase = getLiquibaseForKeycloakUpdate(connection, defaultSchema);
KeycloakLiquibase liquibase = getLiquibaseForKeycloakUpdate(connection, defaultSchema);
Status status = validateChangeSet(liquibase, liquibase.getChangeLogFile());
if (status != Status.VALID) {
@ -247,7 +246,7 @@ public class QuarkusJpaUpdaterProvider implements JpaUpdaterProvider {
if (customChangelog != null) {
String factoryId = jpaProvider.getFactoryId();
String changelogTableName = JpaUtils.getCustomChangelogTableName(factoryId);
Liquibase liquibase = getLiquibaseForCustomProviderUpdate(connection, defaultSchema, customChangelog, jpaProvider.getClass().getClassLoader(), changelogTableName);
KeycloakLiquibase liquibase = getLiquibaseForCustomProviderUpdate(connection, defaultSchema, customChangelog, jpaProvider.getClass().getClassLoader(), changelogTableName);
if (validateChangeSet(liquibase, liquibase.getChangeLogFile()) != Status.VALID) {
return Status.OUTDATED;
}
@ -260,7 +259,7 @@ public class QuarkusJpaUpdaterProvider implements JpaUpdaterProvider {
return Status.VALID;
}
protected Status validateChangeSet(Liquibase liquibase, String changelog) throws LiquibaseException {
protected Status validateChangeSet(KeycloakLiquibase liquibase, String changelog) throws LiquibaseException {
final Status result;
List<ChangeSet> changeSets = getLiquibaseUnrunChangeSets(liquibase);
@ -283,10 +282,8 @@ public class QuarkusJpaUpdaterProvider implements JpaUpdaterProvider {
return result;
}
private void resetLiquibaseServices(Liquibase liquibase) {
Method resetServices = Reflections.findDeclaredMethod(Liquibase.class, "resetServices");
Reflections.invokeMethod(true, resetServices, liquibase);
private void resetLiquibaseServices(KeycloakLiquibase liquibase) {
liquibase.resetServices();
ChangeLogHistoryServiceFactory.getInstance().register(new CustomChangeLogHistoryService());
}
@ -301,12 +298,12 @@ public class QuarkusJpaUpdaterProvider implements JpaUpdaterProvider {
});
}
private Liquibase getLiquibaseForKeycloakUpdate(Connection connection, String defaultSchema) throws LiquibaseException {
private KeycloakLiquibase getLiquibaseForKeycloakUpdate(Connection connection, String defaultSchema) throws LiquibaseException {
LiquibaseConnectionProvider liquibaseProvider = session.getProvider(LiquibaseConnectionProvider.class);
return liquibaseProvider.getLiquibase(connection, defaultSchema);
}
private Liquibase getLiquibaseForCustomProviderUpdate(Connection connection, String defaultSchema, String changelogLocation, ClassLoader classloader, String changelogTableName) throws LiquibaseException {
private KeycloakLiquibase getLiquibaseForCustomProviderUpdate(Connection connection, String defaultSchema, String changelogLocation, ClassLoader classloader, String changelogTableName) throws LiquibaseException {
LiquibaseConnectionProvider liquibaseProvider = session.getProvider(LiquibaseConnectionProvider.class);
return liquibaseProvider.getLiquibaseForCustomUpdate(connection, defaultSchema, changelogLocation, classloader, changelogTableName);
}

View file

@ -28,12 +28,12 @@ import liquibase.Scope;
import liquibase.ui.LoggerUIService;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.connections.jpa.updater.liquibase.conn.KeycloakLiquibase;
import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProvider;
import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
@ -117,7 +117,7 @@ public class QuarkusLiquibaseConnectionProvider implements LiquibaseConnectionPr
}
@Override
public Liquibase getLiquibase(Connection connection, String defaultSchema) throws LiquibaseException {
public KeycloakLiquibase getLiquibase(Connection connection, String defaultSchema) throws LiquibaseException {
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
if (defaultSchema != null) {
database.setDefaultSchemaName(defaultSchema);
@ -127,11 +127,11 @@ public class QuarkusLiquibaseConnectionProvider implements LiquibaseConnectionPr
logger.debugf("Using changelog file %s and changelogTableName %s", changelog, database.getDatabaseChangeLogTableName());
return new Liquibase(changelog, resourceAccessor, database);
return new KeycloakLiquibase(changelog, resourceAccessor, database);
}
@Override
public Liquibase getLiquibaseForCustomUpdate(Connection connection, String defaultSchema, String changelogLocation, ClassLoader classloader, String changelogTableName) throws LiquibaseException {
public KeycloakLiquibase getLiquibaseForCustomUpdate(Connection connection, String defaultSchema, String changelogLocation, ClassLoader classloader, String changelogTableName) throws LiquibaseException {
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
if (defaultSchema != null) {
database.setDefaultSchemaName(defaultSchema);
@ -142,7 +142,7 @@ public class QuarkusLiquibaseConnectionProvider implements LiquibaseConnectionPr
logger.debugf("Using changelog file %s and changelogTableName %s", changelogLocation, database.getDatabaseChangeLogTableName());
return new Liquibase(changelogLocation, resourceAccessor, database);
return new KeycloakLiquibase(changelogLocation, resourceAccessor, database);
}
@Override