KEYCLOAK-14231 - validate supported locales
This commit is contained in:
parent
edef93cd49
commit
7f916ad20c
4 changed files with 93 additions and 13 deletions
|
@ -409,6 +409,7 @@ public class RealmAdminResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
ReservedCharValidator.validate(rep.getRealm());
|
ReservedCharValidator.validate(rep.getRealm());
|
||||||
|
ReservedCharValidator.validateLocales(rep.getSupportedLocales());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!Constants.GENERATE.equals(rep.getPublicKey()) && (rep.getPrivateKey() != null && rep.getPublicKey() != null)) {
|
if (!Constants.GENERATE.equals(rep.getPublicKey()) && (rep.getPrivateKey() != null && rep.getPublicKey() != null)) {
|
||||||
|
|
|
@ -19,29 +19,45 @@ package org.keycloak.utils;
|
||||||
import javax.ws.rs.BadRequestException;
|
import javax.ws.rs.BadRequestException;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Stan Silvert
|
* @author Stan Silvert
|
||||||
|
* @author Lukas Hanusovsky lhanusov@redhat.com
|
||||||
*/
|
*/
|
||||||
public class ReservedCharValidator {
|
public class ReservedCharValidator {
|
||||||
protected static final Logger logger = Logger.getLogger(ReservedCharValidator.class);
|
protected static final Logger logger = Logger.getLogger(ReservedCharValidator.class);
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc3986#section-2.2
|
// https://tools.ietf.org/html/rfc3986#section-2.2
|
||||||
private static final String[] RESERVED_CHARS = { ":", "/", "?", "#", "[", "@", "!", "$",
|
private static final Pattern RESERVED_CHARS_PATTERN = Pattern.compile("[:/?#@!$&()*+,;=\\[\\]\\\\]");
|
||||||
"&", "(", ")", "*", "+", ",", ";", "=",
|
|
||||||
"]", "[", "\\" };
|
// KEYCLOAK-14231 - Supported Locales: Three new characters were added on top of this RFC: "{", "}", "%"
|
||||||
|
private static final Pattern RESERVED_CHARS_LOCALES_PATTERN = Pattern.compile("[:/?#@!$&()*+,;=\\[\\]\\\\{}%]");
|
||||||
|
|
||||||
private ReservedCharValidator() {}
|
private ReservedCharValidator() {}
|
||||||
|
|
||||||
public static void validate(String str) throws ReservedCharException {
|
public static void validate(String str, Pattern pattern) throws ReservedCharException {
|
||||||
if (str == null) return;
|
if (str == null) return;
|
||||||
|
|
||||||
for (String c : RESERVED_CHARS) {
|
Matcher matcher = pattern.matcher(str);
|
||||||
if (str.contains(c)) {
|
if (matcher.find()) {
|
||||||
String message = "Character '" + c + "' not allowed.";
|
String message = "Character '" + matcher.group() + "' not allowed.";
|
||||||
ReservedCharException e = new ReservedCharException(message);
|
logger.warn(message);
|
||||||
logger.warn(message, e);
|
throw new ReservedCharException(message);
|
||||||
throw e;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void validate(String str) {
|
||||||
|
validate(str, RESERVED_CHARS_PATTERN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void validateLocales(Iterable<String> strIterable) {
|
||||||
|
if (strIterable == null) return;
|
||||||
|
|
||||||
|
for (String str: strIterable) {
|
||||||
|
validate(str, RESERVED_CHARS_LOCALES_PATTERN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
package org.keycloak.testsuite.console.page.realm;
|
package org.keycloak.testsuite.console.page.realm;
|
||||||
|
|
||||||
import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
|
import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
|
||||||
|
import org.openqa.selenium.Keys;
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
import org.openqa.selenium.support.FindBy;
|
import org.openqa.selenium.support.FindBy;
|
||||||
import org.openqa.selenium.support.ui.Select;
|
import org.openqa.selenium.support.ui.Select;
|
||||||
|
|
||||||
|
@ -26,6 +28,7 @@ import static org.keycloak.testsuite.util.UIUtils.clickLink;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Filip Kiss
|
* @author Filip Kiss
|
||||||
|
* @author Lukas Hanusovsky lhanusov@redhat.com
|
||||||
*/
|
*/
|
||||||
public class ThemeSettings extends RealmSettings {
|
public class ThemeSettings extends RealmSettings {
|
||||||
|
|
||||||
|
@ -49,6 +52,12 @@ public class ThemeSettings extends RealmSettings {
|
||||||
@FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='internationalizationEnabled']]")
|
@FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='internationalizationEnabled']]")
|
||||||
private OnOffSwitch internatEnabledSwitch;
|
private OnOffSwitch internatEnabledSwitch;
|
||||||
|
|
||||||
|
@FindBy(className = "select2-input")
|
||||||
|
private WebElement supportedLocalesInput;
|
||||||
|
|
||||||
|
@FindBy(id = "defaultLocale")
|
||||||
|
private Select defaultLocaleSelect;
|
||||||
|
|
||||||
public void changeLoginTheme(String themeName) {
|
public void changeLoginTheme(String themeName) {
|
||||||
loginThemeSelect.selectByVisibleText(themeName);
|
loginThemeSelect.selectByVisibleText(themeName);
|
||||||
}
|
}
|
||||||
|
@ -73,6 +82,17 @@ public class ThemeSettings extends RealmSettings {
|
||||||
return internatEnabledSwitch.isOn();
|
return internatEnabledSwitch.isOn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addSupportedLocale(String supportedLocale) {
|
||||||
|
supportedLocalesInput.sendKeys(supportedLocale);
|
||||||
|
supportedLocalesInput.sendKeys(Keys.RETURN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteSupportedLocale(String supportedLocale) {
|
||||||
|
supportedLocalesInput.sendKeys(Keys.chord(Keys.CONTROL, supportedLocale, Keys.BACK_SPACE, Keys.BACK_SPACE));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultLocale () { defaultLocaleSelect.selectByVisibleText("en"); }
|
||||||
|
|
||||||
public void saveTheme() {
|
public void saveTheme() {
|
||||||
clickLink(primaryButton);
|
clickLink(primaryButton);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import static org.keycloak.testsuite.util.URLAssert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
* @author Vaclav Muzikar <vmuzikar@redhat.com>
|
||||||
|
* @author Lukas Hanusovsky lhanusov@redhat.com
|
||||||
*/
|
*/
|
||||||
@DisableFeature(value = Profile.Feature.ACCOUNT2, skipRestart = true) // TODO remove this (KEYCLOAK-16228)
|
@DisableFeature(value = Profile.Feature.ACCOUNT2, skipRestart = true) // TODO remove this (KEYCLOAK-16228)
|
||||||
public class InternationalizationTest extends AbstractRealmTest {
|
public class InternationalizationTest extends AbstractRealmTest {
|
||||||
|
@ -95,6 +96,34 @@ public class InternationalizationTest extends AbstractRealmTest {
|
||||||
assertConsoleLocale(LABEL_CS_REALM_SETTINGS);
|
assertConsoleLocale(LABEL_CS_REALM_SETTINGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSupportedLocalesOnReservedChars() {
|
||||||
|
realmSettingsPage.setAdminRealm(AuthRealm.MASTER);
|
||||||
|
realmSettingsPage.navigateTo();
|
||||||
|
loginPage.form().login(adminUser);
|
||||||
|
tabs().themes();
|
||||||
|
|
||||||
|
if (!themeSettingsPage.isInternatEnabled()) {
|
||||||
|
themeSettingsPage.setInternatEnabled(true);
|
||||||
|
themeSettingsPage.saveTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This Locales should pass, because they do not contain special chars.
|
||||||
|
assertSupportedLocale("test", "succeed");
|
||||||
|
assertSupportedLocale("sausage", "succeed");
|
||||||
|
|
||||||
|
// This Locales should raise exception, because the reserved chars are validated.
|
||||||
|
assertSupportedLocale("%00f%00", "fail");
|
||||||
|
assertSupportedLocale("test; Path=/", "fail");
|
||||||
|
assertSupportedLocale("{test}", "fail");
|
||||||
|
assertSupportedLocale("\\xc0", "fail");
|
||||||
|
assertSupportedLocale("\\xbc", "fail");
|
||||||
|
|
||||||
|
// Clean up session: back to realm Test
|
||||||
|
realmSettingsPage.setAdminRealm(AuthRealm.TEST);
|
||||||
|
deleteAllCookiesForMasterRealm();
|
||||||
|
}
|
||||||
|
|
||||||
private void assertConsoleLocale(String expected) {
|
private void assertConsoleLocale(String expected) {
|
||||||
assertCurrentUrlEquals(realmSettingsPage);
|
assertCurrentUrlEquals(realmSettingsPage);
|
||||||
assertLocale(".//div[@class='nav-category'][1]/ul/li[1]//a", expected); // Realm Settings
|
assertLocale(".//div[@class='nav-category'][1]/ul/li[1]//a", expected); // Realm Settings
|
||||||
|
@ -113,4 +142,18 @@ public class InternationalizationTest extends AbstractRealmTest {
|
||||||
private void assertLocale(WebElement element, String expected) {
|
private void assertLocale(WebElement element, String expected) {
|
||||||
assertEquals(expected, getTextFromElement(element));
|
assertEquals(expected, getTextFromElement(element));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertSupportedLocale(String supportedLocale, String updateStatus) {
|
||||||
|
themeSettingsPage.addSupportedLocale(supportedLocale);
|
||||||
|
themeSettingsPage.setDefaultLocale();
|
||||||
|
themeSettingsPage.saveTheme();
|
||||||
|
if (updateStatus.equals("succeed")) {
|
||||||
|
assertAlertSuccess();
|
||||||
|
} else if (updateStatus.equals("fail")) {
|
||||||
|
assertAlertDanger();
|
||||||
|
themeSettingsPage.deleteSupportedLocale(supportedLocale);
|
||||||
|
} else {
|
||||||
|
assertTrue(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue