KEYCLOAK-5082 : Add new redirect-rewrite-rule parameters for the adapters (#4255)
* add rewrite rule config property * add subsystem support for redirect rewrite * update deployment unit test * add license headers * Optimize rewrite method
This commit is contained in:
parent
620bea3553
commit
500a21685f
17 changed files with 395 additions and 8 deletions
|
@ -92,6 +92,8 @@ public class KeycloakDeployment {
|
|||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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());
|
||||
|
@ -376,5 +380,22 @@ public class OAuthRequestAuthenticator {
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -38,6 +38,8 @@ 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();
|
||||
|
||||
public static KeycloakAdapterConfigService getInstance() {
|
||||
|
@ -130,6 +132,56 @@ public final class KeycloakAdapterConfigService {
|
|||
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);
|
||||
}
|
||||
|
@ -142,6 +194,10 @@ public final class KeycloakAdapterConfigService {
|
|||
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);
|
||||
if (deploymentName == null) throw new RuntimeException("Can't find '" + addrElement + "' in address " + operation.toString());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -150,6 +156,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();
|
||||
addCredential.get(ModelDescriptionConstants.OP).set(ModelDescriptionConstants.ADD);
|
||||
|
@ -159,6 +202,15 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
|
|||
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 {
|
||||
String name = null;
|
||||
|
@ -220,6 +272,11 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
|
|||
writeCredentials(writer, credentials);
|
||||
}
|
||||
|
||||
ModelNode redirectRewriteRule = deploymentElements.get(RedirecRewritetRuleDefinition.TAG_NAME);
|
||||
if (redirectRewriteRule.isDefined()) {
|
||||
writeRedirectRules(writer, redirectRewriteRule);
|
||||
}
|
||||
|
||||
writer.writeEndElement();
|
||||
}
|
||||
}
|
||||
|
@ -266,6 +323,34 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
if (content.indexOf('\n') > -1) {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
@ -95,3 +96,8 @@ keycloak.credential=Credential
|
|||
keycloak.credential.value=Credential value
|
||||
keycloak.credential.add=Credential add
|
||||
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
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -258,6 +258,7 @@ public class TokenEndpoint {
|
|||
}
|
||||
|
||||
event.user(userSession.getUser());
|
||||
|
||||
event.session(userSession.getId());
|
||||
|
||||
String redirectUri = clientSession.getNote(OIDCLoginProtocol.REDIRECT_URI_PARAM);
|
||||
|
|
Loading…
Reference in a new issue