keycloak-uncached
Changes
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java 1(+1 -0)
adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java 31(+26 -5)
adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java 1(+1 -0)
adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java 56(+56 -0)
adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakExtension.java 2(+2 -0)
adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java 85(+85 -0)
adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirecRewritetRuleDefinition.java 61(+61 -0)
adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirectRewriteRuleAddHandler.java 38(+38 -0)
adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirectRewriteRuleReadWriteAttributeHandler.java 44(+44 -0)
adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirectRewriteRuleRemoveHandler.java 36(+36 -0)
adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties 8(+7 -1)
Details
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
index 45c4557..d5761bc 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeployment.java
@@ -91,6 +91,8 @@ public class KeycloakDeployment {
// https://tools.ietf.org/html/rfc7636
protected boolean pkce = false;
protected boolean ignoreOAuthQueryParameter;
+
+ protected Map<String, String> redirectRewriteRules;
public KeycloakDeployment() {
}
@@ -446,4 +448,14 @@ public class KeycloakDeployment {
public boolean isOAuthQueryParameterEnabled() {
return !this.ignoreOAuthQueryParameter;
}
+
+ public Map<String, String> getRedirectRewriteRules() {
+ return redirectRewriteRules;
+ }
+
+ public void setRewriteRedirectRules(Map<String, String> redirectRewriteRules) {
+ this.redirectRewriteRules = redirectRewriteRules;
+ }
+
+
}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
index eca6849..7fca1f1 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/KeycloakDeploymentBuilder.java
@@ -116,6 +116,7 @@ public class KeycloakDeploymentBuilder {
deployment.setMinTimeBetweenJwksRequests(adapterConfig.getMinTimeBetweenJwksRequests());
deployment.setPublicKeyCacheTtl(adapterConfig.getPublicKeyCacheTtl());
deployment.setIgnoreOAuthQueryParameter(adapterConfig.isIgnoreOAuthQueryParameter());
+ deployment.setRewriteRedirectRules(adapterConfig.getRedirectRewriteRules());
if (realmKeyPem == null && adapterConfig.isBearerOnly() && adapterConfig.getAuthServerUrl() == null) {
throw new IllegalArgumentException("For bearer auth, you must set the realm-public-key or auth-server-url");
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
index 2757b36..ee3f214 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
@@ -25,7 +25,6 @@ import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.common.VerificationException;
-import org.keycloak.common.util.Encode;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.common.util.UriUtils;
import org.keycloak.constants.AdapterConstants;
@@ -38,7 +37,10 @@ import org.keycloak.representations.IDToken;
import org.keycloak.util.TokenUtil;
import java.io.IOException;
-import java.util.concurrent.atomic.AtomicLong;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+import java.util.logging.Level;
/**
@@ -141,6 +143,7 @@ public class OAuthRequestAuthenticator {
protected String getRedirectUri(String state) {
String url = getRequestUrl();
log.debugf("callback uri: %s", url);
+
if (!facade.getRequest().isSecure() && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
int port = sslRedirectPort();
if (port < 0) {
@@ -170,7 +173,7 @@ public class OAuthRequestAuthenticator {
KeycloakUriBuilder redirectUriBuilder = deployment.getAuthUrl().clone()
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
- .queryParam(OAuth2Constants.REDIRECT_URI, url)
+ .queryParam(OAuth2Constants.REDIRECT_URI, rewrittenRedirectUri(url))
.queryParam(OAuth2Constants.STATE, state)
.queryParam("login", "true");
if(loginHint != null && loginHint.length() > 0){
@@ -320,10 +323,11 @@ public class OAuthRequestAuthenticator {
AccessTokenResponse tokenResponse = null;
strippedOauthParametersRequestUri = stripOauthParametersFromRedirect();
+
try {
// For COOKIE store we don't have httpSessionId and single sign-out won't be available
String httpSessionId = deployment.getTokenStore() == TokenStore.SESSION ? reqAuthenticator.changeHttpSessionId(true) : null;
- tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, strippedOauthParametersRequestUri, httpSessionId);
+ tokenResponse = ServerRequest.invokeAccessCodeToToken(deployment, code, rewrittenRedirectUri(strippedOauthParametersRequestUri), httpSessionId);
} catch (ServerRequest.HttpFailure failure) {
log.error("failed to turn code into token");
log.error("status from server: " + failure.getStatus());
@@ -375,6 +379,23 @@ public class OAuthRequestAuthenticator {
.replaceQueryParam(OAuth2Constants.STATE, null);
return builder.build().toString();
}
-
+
+ private String rewrittenRedirectUri(String originalUri) {
+ Map<String, String> rewriteRules = deployment.getRedirectRewriteRules();
+ if(rewriteRules != null && !rewriteRules.isEmpty()) {
+ try {
+ URL url = new URL(originalUri);
+ Map.Entry<String, String> rule = rewriteRules.entrySet().iterator().next();
+ StringBuilder redirectUriBuilder = new StringBuilder(url.getProtocol());
+ redirectUriBuilder.append("://"+ url.getAuthority());
+ redirectUriBuilder.append(url.getPath().replaceFirst(rule.getKey(), rule.getValue()));
+ return redirectUriBuilder.toString();
+ } catch (MalformedURLException ex) {
+ log.error("Not a valid request url");
+ throw new RuntimeException(ex);
+ }
+ }
+ return originalUri;
+ }
}
diff --git a/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java b/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java
index cd191e2..af58b33 100644
--- a/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java
+++ b/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java
@@ -75,6 +75,7 @@ public class KeycloakDeploymentBuilderTest {
assertEquals(10, deployment.getTokenMinimumTimeToLive());
assertEquals(20, deployment.getMinTimeBetweenJwksRequests());
assertEquals(120, deployment.getPublicKeyCacheTtl());
+ assertEquals("/api/$1", deployment.getRedirectRewriteRules().get("^/wsmaster/api/(.*)$"));
}
@Test
diff --git a/adapters/oidc/adapter-core/src/test/resources/keycloak.json b/adapters/oidc/adapter-core/src/test/resources/keycloak.json
index 521b8a9..e1b8881 100644
--- a/adapters/oidc/adapter-core/src/test/resources/keycloak.json
+++ b/adapters/oidc/adapter-core/src/test/resources/keycloak.json
@@ -33,5 +33,8 @@
"token-minimum-time-to-live": 10,
"min-time-between-jwks-requests": 20,
"public-key-cache-ttl": 120,
- "ignore-oauth-query-parameter": true
+ "ignore-oauth-query-parameter": true,
+ "redirect-rewrite-rules" : {
+ "^/wsmaster/api/(.*)$" : "/api/$1"
+ }
}
\ No newline at end of file
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java
index e96a5e5..5a71e61 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakAdapterConfigService.java
@@ -37,6 +37,8 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD
public final class KeycloakAdapterConfigService {
private static final String CREDENTIALS_JSON_NAME = "credentials";
+
+ private static final String REDIRECT_REWRITE_RULE_JSON_NAME = "redirect-rewrite-rule";
private static final KeycloakAdapterConfigService INSTANCE = new KeycloakAdapterConfigService();
@@ -129,6 +131,56 @@ public final class KeycloakAdapterConfigService {
ModelNode deployment = this.secureDeployments.get(deploymentNameFromOp(operation));
return deployment.get(CREDENTIALS_JSON_NAME);
}
+
+ public void addRedirectRewriteRule(ModelNode operation, ModelNode model) {
+ ModelNode redirectRewritesRules = redirectRewriteRuleFromOp(operation);
+ if (!redirectRewritesRules.isDefined()) {
+ redirectRewritesRules = new ModelNode();
+ }
+
+ String redirectRewriteRuleName = redirectRewriteRule(operation);
+ if (!redirectRewriteRuleName.contains(".")) {
+ redirectRewritesRules.get(redirectRewriteRuleName).set(model.get("value").asString());
+ } else {
+ String[] parts = redirectRewriteRuleName.split("\\.");
+ String provider = parts[0];
+ String property = parts[1];
+ ModelNode redirectRewriteRule = redirectRewritesRules.get(provider);
+ if (!redirectRewriteRule.isDefined()) {
+ redirectRewriteRule = new ModelNode();
+ }
+ redirectRewriteRule.get(property).set(model.get("value").asString());
+ redirectRewritesRules.set(provider, redirectRewriteRule);
+ }
+
+ ModelNode deployment = this.secureDeployments.get(deploymentNameFromOp(operation));
+ deployment.get(REDIRECT_REWRITE_RULE_JSON_NAME).set(redirectRewritesRules);
+ }
+
+ public void removeRedirectRewriteRule(ModelNode operation) {
+ ModelNode redirectRewritesRules = redirectRewriteRuleFromOp(operation);
+ if (!redirectRewritesRules.isDefined()) {
+ throw new RuntimeException("Can not remove redirect rewrite rule. No rules defined for deployment in op " + operation.toString());
+ }
+
+ String ruleName = credentialNameFromOp(operation);
+ redirectRewritesRules.remove(ruleName);
+ }
+
+ public void updateRedirectRewriteRule(ModelNode operation, String attrName, ModelNode resolvedValue) {
+ ModelNode redirectRewritesRules = redirectRewriteRuleFromOp(operation);
+ if (!redirectRewritesRules.isDefined()) {
+ throw new RuntimeException("Can not update redirect rewrite rule. No rules defined for deployment in op " + operation.toString());
+ }
+
+ String ruleName = credentialNameFromOp(operation);
+ redirectRewritesRules.get(ruleName).set(resolvedValue);
+ }
+
+ private ModelNode redirectRewriteRuleFromOp(ModelNode operation) {
+ ModelNode deployment = this.secureDeployments.get(deploymentNameFromOp(operation));
+ return deployment.get(REDIRECT_REWRITE_RULE_JSON_NAME);
+ }
private String realmNameFromOp(ModelNode operation) {
return valueFromOpAddress(RealmDefinition.TAG_NAME, operation);
@@ -141,6 +193,10 @@ public final class KeycloakAdapterConfigService {
private String credentialNameFromOp(ModelNode operation) {
return valueFromOpAddress(CredentialDefinition.TAG_NAME, operation);
}
+
+ private String redirectRewriteRule(ModelNode operation) {
+ return valueFromOpAddress(RedirecRewritetRuleDefinition.TAG_NAME, operation);
+ }
private String valueFromOpAddress(String addrElement, ModelNode operation) {
String deploymentName = getValueOfAddrElement(operation.get(ADDRESS), addrElement);
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakExtension.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakExtension.java
index 541454a..d04e72d 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakExtension.java
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakExtension.java
@@ -48,6 +48,7 @@ public class KeycloakExtension implements Extension {
static final RealmDefinition REALM_DEFINITION = new RealmDefinition();
static final SecureDeploymentDefinition SECURE_DEPLOYMENT_DEFINITION = new SecureDeploymentDefinition();
static final CredentialDefinition CREDENTIAL_DEFINITION = new CredentialDefinition();
+ static final RedirecRewritetRuleDefinition REDIRECT_RULE_DEFINITON = new RedirecRewritetRuleDefinition();
public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
@@ -77,6 +78,7 @@ public class KeycloakExtension implements Extension {
registration.registerSubModel(REALM_DEFINITION);
ManagementResourceRegistration secureDeploymentRegistration = registration.registerSubModel(SECURE_DEPLOYMENT_DEFINITION);
secureDeploymentRegistration.registerSubModel(CREDENTIAL_DEFINITION);
+ secureDeploymentRegistration.registerSubModel(REDIRECT_RULE_DEFINITON);
subsystem.registerXMLElementWriter(PARSER);
}
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java
index d4ddc02..79555e3 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/KeycloakSubsystemParser.java
@@ -96,12 +96,17 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
PathElement.pathElement(SecureDeploymentDefinition.TAG_NAME, name));
addSecureDeployment.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
List<ModelNode> credentialsToAdd = new ArrayList<ModelNode>();
+ List<ModelNode> redirectRulesToAdd = new ArrayList<ModelNode>();
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (tagName.equals(CredentialDefinition.TAG_NAME)) {
readCredential(reader, addr, credentialsToAdd);
continue;
}
+ if (tagName.equals(RedirecRewritetRuleDefinition.TAG_NAME)) {
+ readRewriteRule(reader, addr, redirectRulesToAdd);
+ continue;
+ }
SimpleAttributeDefinition def = SecureDeploymentDefinition.lookup(tagName);
if (def == null) throw new XMLStreamException("Unknown secure-deployment tag " + tagName);
@@ -111,6 +116,7 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
// Must add credentials after the deployment is added.
resourcesToAdd.add(addSecureDeployment);
resourcesToAdd.addAll(credentialsToAdd);
+ resourcesToAdd.addAll(redirectRulesToAdd);
}
public void readCredential(XMLExtendedStreamReader reader, PathAddress parent, List<ModelNode> credentialsToAdd) throws XMLStreamException {
@@ -149,6 +155,43 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
}
}
}
+
+ public void readRewriteRule(XMLExtendedStreamReader reader, PathAddress parent, List<ModelNode> rewriteRuleToToAdd) throws XMLStreamException {
+ String name = readNameAttribute(reader);
+
+ Map<String, String> values = new HashMap<>();
+ String textValue = null;
+ while (reader.hasNext()) {
+ int next = reader.next();
+ if (next == CHARACTERS) {
+ // text value of redirect rule element
+ String text = reader.getText();
+ if (text == null || text.trim().isEmpty()) {
+ continue;
+ }
+ textValue = text;
+ } else if (next == START_ELEMENT) {
+ String key = reader.getLocalName();
+ reader.next();
+ String value = reader.getText();
+ reader.next();
+
+ values.put(key, value);
+ } else if (next == END_ELEMENT) {
+ break;
+ }
+ }
+
+ if (textValue != null) {
+ ModelNode addRedirectRule = getRedirectRuleToAdd(parent, name, textValue);
+ rewriteRuleToToAdd.add(addRedirectRule);
+ } else {
+ for (Map.Entry<String, String> entry : values.entrySet()) {
+ ModelNode addRedirectRule = getRedirectRuleToAdd(parent, name + "." + entry.getKey(), entry.getValue());
+ rewriteRuleToToAdd.add(addRedirectRule);
+ }
+ }
+ }
private ModelNode getCredentialToAdd(PathAddress parent, String name, String value) {
ModelNode addCredential = new ModelNode();
@@ -158,6 +201,15 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
addCredential.get(CredentialDefinition.VALUE.getName()).set(value);
return addCredential;
}
+
+ private ModelNode getRedirectRuleToAdd(PathAddress parent, String name, String value) {
+ ModelNode addRedirectRule = new ModelNode();
+ addRedirectRule.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
+ PathAddress addr = PathAddress.pathAddress(parent, PathElement.pathElement(RedirecRewritetRuleDefinition.TAG_NAME, name));
+ addRedirectRule.get(ModelDescriptionConstants.OP_ADDR).set(addr.toModelNode());
+ addRedirectRule.get(RedirecRewritetRuleDefinition.VALUE.getName()).set(value);
+ return addRedirectRule;
+ }
// expects that the current tag will have one single attribute called "name"
private String readNameAttribute(XMLExtendedStreamReader reader) throws XMLStreamException {
@@ -219,6 +271,11 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
if (credentials.isDefined()) {
writeCredentials(writer, credentials);
}
+
+ ModelNode redirectRewriteRule = deploymentElements.get(RedirecRewritetRuleDefinition.TAG_NAME);
+ if (redirectRewriteRule.isDefined()) {
+ writeRedirectRules(writer, redirectRewriteRule);
+ }
writer.writeEndElement();
}
@@ -265,6 +322,34 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
writer.writeEndElement();
}
}
+
+ private void writeRedirectRules(XMLExtendedStreamWriter writer, ModelNode redirectRules) throws XMLStreamException {
+ Map<String, Object> parsed = new LinkedHashMap<>();
+ for (Property redirectRule : redirectRules.asPropertyList()) {
+ String ruleName = redirectRule.getName();
+ String ruleValue = redirectRule.getValue().get(RedirecRewritetRuleDefinition.VALUE.getName()).asString();
+ parsed.put(ruleName, ruleValue);
+ }
+
+ for (Map.Entry<String, Object> entry : parsed.entrySet()) {
+ writer.writeStartElement(RedirecRewritetRuleDefinition.TAG_NAME);
+ writer.writeAttribute("name", entry.getKey());
+
+ Object value = entry.getValue();
+ if (value instanceof String) {
+ writeCharacters(writer, (String) value);
+ } else {
+ Map<String, String> redirectRulesProps = (Map<String, String>) value;
+ for (Map.Entry<String, String> prop : redirectRulesProps.entrySet()) {
+ writer.writeStartElement(prop.getKey());
+ writeCharacters(writer, prop.getValue());
+ writer.writeEndElement();
+ }
+ }
+
+ writer.writeEndElement();
+ }
+ }
// code taken from org.jboss.as.controller.AttributeMarshaller
private void writeCharacters(XMLExtendedStreamWriter writer, String content) throws XMLStreamException {
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirecRewritetRuleDefinition.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirecRewritetRuleDefinition.java
new file mode 100644
index 0000000..a9095c7
--- /dev/null
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirecRewritetRuleDefinition.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016 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.subsystem.adapter.extension;
+
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
+import org.jboss.as.controller.operations.validation.StringLengthValidator;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.dmr.ModelType;
+
+/**
+ *
+ * @author sblanc
+ */
+public class RedirecRewritetRuleDefinition extends SimpleResourceDefinition {
+
+ public static final String TAG_NAME = "redirect-rewrite-rule";
+
+ protected static final AttributeDefinition VALUE =
+ new SimpleAttributeDefinitionBuilder("value", ModelType.STRING, false)
+ .setAllowExpression(true)
+ .setValidator(new StringLengthValidator(1, Integer.MAX_VALUE, false, true))
+ .build();
+
+ public RedirecRewritetRuleDefinition() {
+ super(PathElement.pathElement(TAG_NAME),
+ KeycloakExtension.getResourceDescriptionResolver(TAG_NAME),
+ new RedirectRewriteRuleAddHandler(VALUE),
+ RedirectRewriteRuleRemoveHandler.INSTANCE);
+ }
+
+ @Override
+ public void registerOperations(ManagementResourceRegistration resourceRegistration) {
+ super.registerOperations(resourceRegistration);
+ resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
+ }
+
+ @Override
+ public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
+ super.registerAttributes(resourceRegistration);
+ resourceRegistration.registerReadWriteAttribute(VALUE, null, new RedirectRewriteRuleReadWriteAttributeHandler());
+ }
+}
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirectRewriteRuleAddHandler.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirectRewriteRuleAddHandler.java
new file mode 100644
index 0000000..2fc25f7
--- /dev/null
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirectRewriteRuleAddHandler.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 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.subsystem.adapter.extension;
+
+import org.jboss.as.controller.AbstractAddStepHandler;
+import org.jboss.as.controller.AttributeDefinition;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+public class RedirectRewriteRuleAddHandler extends AbstractAddStepHandler {
+
+ public RedirectRewriteRuleAddHandler(AttributeDefinition... attributes) {
+ super(attributes);
+ }
+
+ @Override
+ protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+ KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
+ ckService.addRedirectRewriteRule(operation, context.resolveExpressions(model));
+ }
+
+}
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirectRewriteRuleReadWriteAttributeHandler.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirectRewriteRuleReadWriteAttributeHandler.java
new file mode 100644
index 0000000..171e755
--- /dev/null
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirectRewriteRuleReadWriteAttributeHandler.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 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.subsystem.adapter.extension;
+
+import org.jboss.as.controller.AbstractWriteAttributeHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+public class RedirectRewriteRuleReadWriteAttributeHandler extends AbstractWriteAttributeHandler<KeycloakAdapterConfigService> {
+
+ @Override
+ protected boolean applyUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
+ ModelNode resolvedValue, ModelNode currentValue, AbstractWriteAttributeHandler.HandbackHolder<KeycloakAdapterConfigService> hh) throws OperationFailedException {
+
+ KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
+ ckService.updateRedirectRewriteRule(operation, attributeName, resolvedValue);
+
+ hh.setHandback(ckService);
+
+ return false;
+ }
+
+ @Override
+ protected void revertUpdateToRuntime(OperationContext context, ModelNode operation, String attributeName,
+ ModelNode valueToRestore, ModelNode valueToRevert, KeycloakAdapterConfigService ckService) throws OperationFailedException {
+ ckService.updateRedirectRewriteRule(operation, attributeName, valueToRestore);
+ }
+
+}
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirectRewriteRuleRemoveHandler.java b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirectRewriteRuleRemoveHandler.java
new file mode 100644
index 0000000..de17c96
--- /dev/null
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/extension/RedirectRewriteRuleRemoveHandler.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016 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.subsystem.adapter.extension;
+
+import org.jboss.as.controller.AbstractRemoveStepHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.dmr.ModelNode;
+
+public class RedirectRewriteRuleRemoveHandler extends AbstractRemoveStepHandler {
+
+ public static RedirectRewriteRuleRemoveHandler INSTANCE = new RedirectRewriteRuleRemoveHandler();
+
+ private RedirectRewriteRuleRemoveHandler() {}
+
+ @Override
+ protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
+ KeycloakAdapterConfigService ckService = KeycloakAdapterConfigService.getInstance();
+ ckService.removeRedirectRewriteRule(operation);
+ }
+
+}
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
index 1df5979..c9cea77 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/extension/LocalDescriptions.properties
@@ -65,6 +65,7 @@ keycloak.secure-deployment.connection-pool-size=Connection pool size for the cli
keycloak.secure-deployment.resource=Application name
keycloak.secure-deployment.use-resource-role-mappings=Use resource level permissions from token
keycloak.secure-deployment.credentials=Adapter credentials
+keycloak.secure-deployment.redirect-rewrite-rule=Apply a rewrite rule for the redirect URI
keycloak.secure-deployment.bearer-only=Bearer Token Auth only
keycloak.secure-deployment.enable-basic-auth=Enable Basic Authentication
keycloak.secure-deployment.public-client=Public client
@@ -94,4 +95,9 @@ keycloak.secure-deployment.credential=Credential value
keycloak.credential=Credential
keycloak.credential.value=Credential value
keycloak.credential.add=Credential add
-keycloak.credential.remove=Credential remove
\ No newline at end of file
+keycloak.credential.remove=Credential remove
+
+keycloak.redirect-rewrite-rule=redirect-rewrite-rule
+keycloak.redirect-rewrite-rule.value=redirect-rewrite-rule value
+keycloak.redirect-rewrite-rule.add=redirect-rewrite-rule add
+keycloak.redirect-rewrite-rule.remove=redirect-rewrite-rule remove
\ No newline at end of file
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
index 604e6ac..d8f5bc3 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak_1_1.xsd
@@ -101,6 +101,7 @@
<xs:element name="ssl-required" type="xs:string" minOccurs="0" maxOccurs="1" />
<xs:element name="realm-public-key" type="xs:string" minOccurs="1" maxOccurs="1"/>
<xs:element name="credential" type="credential-type" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="redirect-rewrite-rule" type="redirect-rewrite-rule-type" minOccurs="1" maxOccurs="1"/>
<xs:element name="auth-server-url-for-backend-requests" type="xs:string" minOccurs="0" maxOccurs="1"/>
<xs:element name="always-refresh-token" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
<xs:element name="register-node-at-startup" type="xs:boolean" minOccurs="0" maxOccurs="1"/>
@@ -127,4 +128,10 @@
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required" />
</xs:complexType>
+ <xs:complexType name="redirect-rewrite-rule-type" mixed="true">
+ <xs:sequence maxOccurs="unbounded" minOccurs="0">
+ <xs:any processContents="lax"></xs:any>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required" />
+ </xs:complexType>
</xs:schema>
diff --git a/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml b/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml
index 3dcb61d..246d768 100755
--- a/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml
+++ b/adapters/oidc/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/extension/keycloak-1.1.xml
@@ -53,6 +53,7 @@
<auth-server-url>http://localhost:8080/auth</auth-server-url>
<ssl-required>EXTERNAL</ssl-required>
<credential name="secret">0aa31d98-e0aa-404c-b6e0-e771dba1e798</credential>
+ <redirect-rewrite-rule name="^/wsmaster/api/(.*)$">api/$1/</redirect-rewrite-rule>
</secure-deployment>
<secure-deployment name="http-endpoint">
<realm>master</realm>
@@ -66,5 +67,6 @@
<credential name="jwt">
<client-keystore-file>/tmp/keystore.jks</client-keystore-file>
</credential>
+ <redirect-rewrite-rule name="^/wsmaster/api/(.*)$">/api/$1/</redirect-rewrite-rule>
</secure-deployment>
</subsystem>
\ No newline at end of file
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java
index 4a2b7e2..ebd49ab 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java
@@ -62,7 +62,8 @@ public class BaseAdapterConfig extends BaseRealmConfig {
protected boolean publicClient;
@JsonProperty("credentials")
protected Map<String, Object> credentials = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
+ @JsonProperty("redirect-rewrite-rules")
+ protected Map<String, String> redirectRewriteRules;
public boolean isUseResourceRoleMappings() {
return useResourceRoleMappings;
@@ -167,4 +168,14 @@ public class BaseAdapterConfig extends BaseRealmConfig {
public void setPublicClient(boolean publicClient) {
this.publicClient = publicClient;
}
+
+ public Map<String, String> getRedirectRewriteRules() {
+ return redirectRewriteRules;
+ }
+
+ public void setRedirectRewriteRules(Map<String, String> redirectRewriteRules) {
+ this.redirectRewriteRules = redirectRewriteRules;
+ }
+
+
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index 4870415..14d5570 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -258,6 +258,7 @@ public class TokenEndpoint {
}
event.user(userSession.getUser());
+
event.session(userSession.getId());
String redirectUri = clientSession.getNote(OIDCLoginProtocol.REDIRECT_URI_PARAM);