Skip to content

Commit

Permalink
[keycloak#10608] Password blacklists folder
Browse files Browse the repository at this point in the history
  • Loading branch information
rmartinc authored and pedroigor committed Mar 8, 2022
1 parent 8454dc5 commit 4856583
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
import org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory;
import org.keycloak.connections.jpa.updater.liquibase.LiquibaseJpaUpdaterProviderFactory;
import org.keycloak.connections.jpa.updater.liquibase.conn.DefaultLiquibaseConnectionProvider;
import org.keycloak.policy.BlacklistPasswordPolicyProviderFactory;
import org.keycloak.protocol.ProtocolMapperSpi;
import org.keycloak.protocol.oidc.mappers.DeployedScriptOIDCProtocolMapper;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
Expand Down Expand Up @@ -153,7 +154,8 @@ class KeycloakProcessor {
DefaultHostnameProviderFactory.class,
FixedHostnameProviderFactory.class,
RequestHostnameProviderFactory.class,
FilesPlainTextVaultProviderFactory.class);
FilesPlainTextVaultProviderFactory.class,
BlacklistPasswordPolicyProviderFactory.class);

static {
DEPLOYEABLE_SCRIPT_PROVIDERS.put(AUTHENTICATORS, KeycloakProcessor::registerScriptAuthenticator);
Expand Down
Original file line number Diff line number Diff line change
@@ -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 org.keycloak.quarkus.runtime.policy;

import org.keycloak.policy.BlacklistPasswordPolicyProviderFactory;
import org.keycloak.quarkus.runtime.Environment;

/**
* <p>Quarkus implementation of the BlacklistPasswordPolicyProviderFactory. The
* default path for the list files is calculated using the quarkus environment
* class, in order to obtain the correct <em>data</em> directory.
*
* @author rmartinc
*/
public class QuarkusBlacklistPasswordPolicyProviderFactory extends BlacklistPasswordPolicyProviderFactory {

@Override
public String getDefaultBlacklistsBasePath() {
return Environment.getDataDir() + "/" + PASSWORD_BLACKLISTS_FOLDER;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#
# 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.
#

org.keycloak.quarkus.runtime.policy.QuarkusBlacklistPasswordPolicyProviderFactory
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;

/**
* Creates {@link BlacklistPasswordPolicyProvider} instances.
Expand Down Expand Up @@ -87,7 +88,7 @@ public PasswordPolicyProvider create(KeycloakSession session) {
if (this.blacklistsBasePath == null) {
synchronized (this) {
if (this.blacklistsBasePath == null) {
this.blacklistsBasePath = FileBasedPasswordBlacklist.detectBlacklistsBasePath(config);
this.blacklistsBasePath = FileBasedPasswordBlacklist.detectBlacklistsBasePath(config, this::getDefaultBlacklistsBasePath);
}
}
}
Expand Down Expand Up @@ -132,6 +133,17 @@ public String getId() {
return ID;
}

/**
* Method to obtain the default location for the list folder. The method
* will return the <em>data</em> directory of the installation concatenated
* with <em>/password-blacklists/</em>.
*
* @return The default path used by the provider to lookup the lists
* when no other configuration is in place.
*/
public String getDefaultBlacklistsBasePath() {
return System.getProperty(JBOSS_SERVER_DATA_DIR) + "/" + PASSWORD_BLACKLISTS_FOLDER;
}

/**
* Resolves and potentially registers a {@link PasswordBlacklist} for the given {@code blacklistName}.
Expand Down Expand Up @@ -302,10 +314,11 @@ private static BufferedReader newReader(Path path) throws IOException {
* running wildfly instance.
*
* @param config
* @param defaultPathSupplier default path to use if not specified in a system prop or configuration
* @return the detected blacklist path
* @throws IllegalStateException if no blacklist folder could be detected
*/
private static Path detectBlacklistsBasePath(Config.Scope config) {
private static Path detectBlacklistsBasePath(Config.Scope config, Supplier<String> defaultPathSupplier) {

String pathFromSysProperty = System.getProperty(SYSTEM_PROPERTY);
if (pathFromSysProperty != null) {
Expand All @@ -317,7 +330,11 @@ private static Path detectBlacklistsBasePath(Config.Scope config) {
return ensureExists(Paths.get(pathFromSpiConfig));
}

String pathFromJbossDataPath = System.getProperty(JBOSS_SERVER_DATA_DIR) + "/" + PASSWORD_BLACKLISTS_FOLDER;
String pathFromJbossDataPath = defaultPathSupplier.get();
if (pathFromJbossDataPath == null) {
throw new IllegalStateException("Default path for the blacklist folder was null");
}

if (!Files.exists(Paths.get(pathFromJbossDataPath))) {
if (!Paths.get(pathFromJbossDataPath).toFile().mkdirs()) {
LOG.errorf("Could not create folder for password blacklists: %s", pathFromJbossDataPath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.policy.BlacklistPasswordPolicyProvider;
import org.keycloak.policy.BlacklistPasswordPolicyProviderFactory;
import org.keycloak.policy.MaximumLengthPasswordPolicyProviderFactory;
import org.keycloak.policy.PasswordPolicyManagerProvider;
import org.keycloak.policy.PasswordPolicyProvider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
Expand All @@ -34,6 +37,9 @@

import java.util.List;

import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.endsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
Expand Down Expand Up @@ -184,6 +190,17 @@ public void testBlacklistPasswordPolicyWithTestBlacklist() throws Exception {
});
}

@Test
public void testBlacklistPasswordPolicyDefaultPath() throws Exception {
testingClient.server("passwordPolicy").run(session -> {
ProviderFactory<PasswordPolicyProvider> passPolicyFact = session.getKeycloakSessionFactory().getProviderFactory(
PasswordPolicyProvider.class, BlacklistPasswordPolicyProviderFactory.ID);
assertThat(passPolicyFact, instanceOf(BlacklistPasswordPolicyProviderFactory.class));
assertThat(((BlacklistPasswordPolicyProviderFactory)passPolicyFact).getDefaultBlacklistsBasePath(),
endsWith("/data/password-blacklists/"));
});
}

@Test
public void testNotUsername() {
testingClient.server("passwordPolicy").run(session -> {
Expand Down

0 comments on commit 4856583

Please sign in to comment.