[KEYCLOAK-10868] - Deploy JavaScript code directly to Keycloak server
Conflicts: testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java (cherry picked from commit 338fe2ae47a1494e786030eb39f908c964ea76c4)
This commit is contained in:
parent
bad9e29c15
commit
bb4ff55229
48 changed files with 1387 additions and 74 deletions
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright 2019 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.authorization.policy.provider.js;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.ScriptModel;
|
||||
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
|
||||
import org.keycloak.representations.provider.ScriptProviderMetadata;
|
||||
import org.keycloak.scripting.ScriptingProvider;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public final class DeployedScriptPolicyFactory extends JSPolicyProviderFactory {
|
||||
|
||||
private final ScriptProviderMetadata metadata;
|
||||
|
||||
public DeployedScriptPolicyFactory(ScriptProviderMetadata metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return metadata.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return metadata.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDeployed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSPolicyRepresentation toRepresentation(Policy policy, AuthorizationProvider authorization) {
|
||||
JSPolicyRepresentation representation = new JSPolicyRepresentation();
|
||||
|
||||
representation.setId(policy.getId());
|
||||
representation.setName(policy.getName());
|
||||
representation.setDescription(metadata.getDescription());
|
||||
representation.setType(getId());
|
||||
representation.setCode(metadata.getCode());
|
||||
|
||||
return representation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ScriptModel getScriptModel(Policy policy, RealmModel realm, ScriptingProvider scripting) {
|
||||
return scripting.createScript(realm.getId(), ScriptModel.TEXT_JAVASCRIPT, metadata.getName(), metadata.getCode(),
|
||||
metadata.getDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Policy policy, JSPolicyRepresentation representation, AuthorizationProvider authorization) {
|
||||
representation.setDescription(metadata.getDescription());
|
||||
policy.setDescription(metadata.getDescription());
|
||||
super.onCreate(policy, representation, authorization);
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import org.keycloak.authorization.AuthorizationProvider;
|
|||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -56,17 +57,17 @@ public class JSPolicyProviderFactory implements PolicyProviderFactory<JSPolicyRe
|
|||
|
||||
@Override
|
||||
public void onCreate(Policy policy, JSPolicyRepresentation representation, AuthorizationProvider authorization) {
|
||||
updatePolicy(policy, representation.getCode());
|
||||
updatePolicy(policy, representation.getCode(), authorization);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(Policy policy, JSPolicyRepresentation representation, AuthorizationProvider authorization) {
|
||||
updatePolicy(policy, representation.getCode());
|
||||
updatePolicy(policy, representation.getCode(), authorization);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
|
||||
updatePolicy(policy, representation.getConfig().get("code"));
|
||||
updatePolicy(policy, representation.getConfig().get("code"), authorization);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,6 +97,11 @@ public class JSPolicyProviderFactory implements PolicyProviderFactory<JSPolicyRe
|
|||
return "js";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return !Profile.isFeatureEnabled(Profile.Feature.UPLOAD_SCRIPTS);
|
||||
}
|
||||
|
||||
private EvaluatableScriptAdapter getEvaluatableScript(final AuthorizationProvider authz, final Policy policy) {
|
||||
return scriptCache.computeIfAbsent(policy.getId(), id -> {
|
||||
final ScriptingProvider scripting = authz.getKeycloakSession().getProvider(ScriptingProvider.class);
|
||||
|
@ -104,7 +110,7 @@ public class JSPolicyProviderFactory implements PolicyProviderFactory<JSPolicyRe
|
|||
});
|
||||
}
|
||||
|
||||
private ScriptModel getScriptModel(final Policy policy, final RealmModel realm, final ScriptingProvider scripting) {
|
||||
protected ScriptModel getScriptModel(final Policy policy, final RealmModel realm, final ScriptingProvider scripting) {
|
||||
String scriptName = policy.getName();
|
||||
String scriptCode = policy.getConfig().get("code");
|
||||
String scriptDescription = policy.getDescription();
|
||||
|
@ -113,8 +119,15 @@ public class JSPolicyProviderFactory implements PolicyProviderFactory<JSPolicyRe
|
|||
return scripting.createScript(realm.getId(), ScriptModel.TEXT_JAVASCRIPT, scriptName, scriptCode, scriptDescription);
|
||||
}
|
||||
|
||||
private void updatePolicy(Policy policy, String code) {
|
||||
private void updatePolicy(Policy policy, String code, AuthorizationProvider authorization) {
|
||||
scriptCache.remove(policy.getId());
|
||||
if (!Profile.isFeatureEnabled(Profile.Feature.UPLOAD_SCRIPTS) && !authorization.getKeycloakSession().getAttributeOrDefault("ALLOW_CREATE_POLICY", false) && !isDeployed()) {
|
||||
throw new RuntimeException("Script upload is disabled");
|
||||
}
|
||||
policy.putConfig("code", code);
|
||||
}
|
||||
|
||||
protected boolean isDeployed() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,11 @@ public abstract class AbstractPermissionProvider implements PolicyProvider {
|
|||
|
||||
if (effect == null) {
|
||||
PolicyProvider policyProvider = authorization.getProvider(associatedPolicy.getType());
|
||||
|
||||
if (policyProvider == null) {
|
||||
throw new RuntimeException("No policy provider found for policy [" + associatedPolicy.getType() + "]");
|
||||
}
|
||||
|
||||
policyProvider.evaluate(defaultEvaluation);
|
||||
evaluation.denyIfNoEffect();
|
||||
decisions.put(permission, defaultEvaluation.getEffect());
|
||||
|
|
|
@ -110,6 +110,11 @@ public class Profile {
|
|||
disabledFeatures.add(f);
|
||||
} else if (DEPRECATED.equals(f.getType())) {
|
||||
logger.warnf("Deprecated feature enabled: " + f.name().toLowerCase());
|
||||
if (Feature.UPLOAD_SCRIPTS.equals(f)) {
|
||||
previewFeatures.add(Feature.SCRIPTS);
|
||||
disabledFeatures.remove(Feature.SCRIPTS);
|
||||
logger.warnf("Preview feature enabled: " + Feature.SCRIPTS.name().toLowerCase());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PREVIEW:
|
||||
|
|
|
@ -25,8 +25,11 @@ public class JSPolicyRepresentation extends AbstractPolicyRepresentation {
|
|||
|
||||
@Override
|
||||
public String getType() {
|
||||
if (super.getType() == null) {
|
||||
return "js";
|
||||
}
|
||||
return super.getType();
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2019 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.representations.provider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonSetter;
|
||||
import com.fasterxml.jackson.annotation.JsonUnwrapped;
|
||||
|
||||
public class ScriptProviderDescriptor {
|
||||
|
||||
public static final String AUTHENTICATORS = "authenticators";
|
||||
public static final String POLICIES = "policies";
|
||||
public static final String MAPPERS = "mappers";
|
||||
|
||||
private Map<String, List<ScriptProviderMetadata>> providers = new HashMap<>();
|
||||
|
||||
@JsonUnwrapped
|
||||
@JsonGetter
|
||||
public Map<String, List<ScriptProviderMetadata>> getProviders() {
|
||||
return providers;
|
||||
}
|
||||
|
||||
@JsonSetter
|
||||
public void setAuthenticators(List<ScriptProviderMetadata> metadata) {
|
||||
providers.put(AUTHENTICATORS, metadata);
|
||||
}
|
||||
|
||||
@JsonSetter
|
||||
public void setPolicies(List<ScriptProviderMetadata> metadata) {
|
||||
providers.put(POLICIES, metadata);
|
||||
}
|
||||
|
||||
@JsonSetter
|
||||
public void setMappers(List<ScriptProviderMetadata> metadata) {
|
||||
providers.put(MAPPERS, metadata);
|
||||
}
|
||||
|
||||
public void addAuthenticator(String name, String fileName) {
|
||||
addProvider(AUTHENTICATORS, name, fileName, null);
|
||||
}
|
||||
|
||||
private void addProvider(String type, String name, String fileName, String description) {
|
||||
List<ScriptProviderMetadata> authenticators = providers.get(type);
|
||||
|
||||
if (authenticators == null) {
|
||||
authenticators = new ArrayList<>();
|
||||
providers.put(type, authenticators);
|
||||
}
|
||||
|
||||
authenticators.add(new ScriptProviderMetadata(name, fileName, description));
|
||||
}
|
||||
|
||||
public void addPolicy(String name, String fileName) {
|
||||
addProvider(POLICIES, name, fileName, null);
|
||||
}
|
||||
|
||||
public void addMapper(String name, String fileName) {
|
||||
addProvider(MAPPERS, name, fileName, null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2019 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.representations.provider;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
public class ScriptProviderMetadata {
|
||||
|
||||
@JsonIgnore
|
||||
private String id;
|
||||
private String name;
|
||||
private String fileName;
|
||||
private String description;
|
||||
|
||||
@JsonIgnore
|
||||
private String code;
|
||||
|
||||
public ScriptProviderMetadata() {
|
||||
|
||||
}
|
||||
|
||||
public ScriptProviderMetadata(String name, String fileName, String description) {
|
||||
this.name = name;
|
||||
this.fileName = fileName;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
}
|
|
@ -45,6 +45,10 @@
|
|||
<module name="org.keycloak.keycloak-services"/>
|
||||
<module name="org.keycloak.keycloak-server-spi-private"/>
|
||||
<module name="org.keycloak.keycloak-wildfly-adapter" optional="true"/>
|
||||
<module name="org.keycloak.keycloak-core"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.keycloak.keycloak-server-spi"/>
|
||||
<module name="org.keycloak.keycloak-authz-policy-common"/>
|
||||
<module name="org.jboss.metadata"/>
|
||||
</dependencies>
|
||||
</module>
|
||||
|
|
|
@ -78,14 +78,12 @@ public final class AuthorizationProvider implements Provider {
|
|||
private final PolicyEvaluator policyEvaluator;
|
||||
private StoreFactory storeFactory;
|
||||
private StoreFactory storeFactoryDelegate;
|
||||
private final Map<String, PolicyProviderFactory> policyProviderFactories;
|
||||
private final KeycloakSession keycloakSession;
|
||||
private final RealmModel realm;
|
||||
|
||||
public AuthorizationProvider(KeycloakSession session, RealmModel realm, Map<String, PolicyProviderFactory> policyProviderFactories, PolicyEvaluator policyEvaluator) {
|
||||
public AuthorizationProvider(KeycloakSession session, RealmModel realm, PolicyEvaluator policyEvaluator) {
|
||||
this.keycloakSession = session;
|
||||
this.realm = realm;
|
||||
this.policyProviderFactories = policyProviderFactories;
|
||||
this.policyEvaluator = policyEvaluator;
|
||||
}
|
||||
|
||||
|
@ -131,7 +129,8 @@ public final class AuthorizationProvider implements Provider {
|
|||
* @return a {@link List} containing all registered {@link PolicyProviderFactory}
|
||||
*/
|
||||
public Collection<PolicyProviderFactory> getProviderFactories() {
|
||||
return this.policyProviderFactories.values();
|
||||
return keycloakSession.getKeycloakSessionFactory().getProviderFactories(PolicyProvider.class).stream().map(
|
||||
PolicyProviderFactory.class::cast).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -141,8 +140,8 @@ public final class AuthorizationProvider implements Provider {
|
|||
* @param <F> the expected type of the provider
|
||||
* @return a {@link PolicyProviderFactory} with the given <code>type</code>
|
||||
*/
|
||||
public <F extends PolicyProviderFactory> F getProviderFactory(String type) {
|
||||
return (F) policyProviderFactories.get(type);
|
||||
public PolicyProviderFactory getProviderFactory(String type) {
|
||||
return (PolicyProviderFactory) keycloakSession.getKeycloakSessionFactory().getProviderFactory(PolicyProvider.class, type);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -153,7 +152,7 @@ public final class AuthorizationProvider implements Provider {
|
|||
* @return a {@link PolicyProvider} with the given <code>type</code>
|
||||
*/
|
||||
public <P extends PolicyProvider> P getProvider(String type) {
|
||||
PolicyProviderFactory policyProviderFactory = policyProviderFactories.get(type);
|
||||
PolicyProviderFactory policyProviderFactory = getProviderFactory(type);
|
||||
|
||||
if (policyProviderFactory == null) {
|
||||
return null;
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
package org.keycloak.provider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class KeycloakDeploymentInfo {
|
||||
|
||||
private String name;
|
||||
private boolean services;
|
||||
private boolean themes;
|
||||
private boolean themeResources;
|
||||
private Map<Class<? extends Spi>, List<ProviderFactory>> providers = new HashMap<>();
|
||||
|
||||
public boolean isProvider() {
|
||||
return services || themes || themeResources;
|
||||
return services || themes || themeResources || !providers.isEmpty();
|
||||
}
|
||||
|
||||
public boolean hasServices() {
|
||||
|
@ -53,4 +59,12 @@ public class KeycloakDeploymentInfo {
|
|||
themeResources = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void addProvider(Class<? extends Spi> spi, ProviderFactory factory) {
|
||||
providers.computeIfAbsent(spi, key -> new ArrayList<>()).add(factory);
|
||||
}
|
||||
|
||||
public Map<Class<? extends Spi>, List<ProviderFactory>> getProviders() {
|
||||
return providers;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright 2019 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.authentication.authenticators.browser;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||
import org.keycloak.authentication.Authenticator;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.representations.provider.ScriptProviderMetadata;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public final class DeployedScriptAuthenticatorFactory extends ScriptBasedAuthenticatorFactory {
|
||||
|
||||
private final AuthenticatorConfigModel model;
|
||||
|
||||
public DeployedScriptAuthenticatorFactory(ScriptProviderMetadata metadata) {
|
||||
model = new AuthenticatorConfigModel();
|
||||
|
||||
model.setId(metadata.getId());
|
||||
model.setAlias(metadata.getName());
|
||||
model.setConfig(new HashMap<>());
|
||||
model.getConfig().put("scriptName", metadata.getName());
|
||||
model.getConfig().put("scriptCode", metadata.getCode());
|
||||
model.getConfig().put("scriptDescription", metadata.getDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authenticator create(KeycloakSession session) {
|
||||
return new ScriptBasedAuthenticator() {
|
||||
@Override
|
||||
protected AuthenticatorConfigModel getAuthenticatorConfig(AuthenticationFlowContext context) {
|
||||
return model;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return model.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfigurable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserSetupAllowed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return model.getAlias();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return model.getAlias();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return Profile.isFeatureEnabled(Profile.Feature.SCRIPTS);
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||
import org.keycloak.authentication.AuthenticationFlowError;
|
||||
import org.keycloak.authentication.Authenticator;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.ScriptModel;
|
||||
|
@ -132,15 +133,21 @@ public class ScriptBasedAuthenticator implements Authenticator {
|
|||
}
|
||||
|
||||
private boolean hasAuthenticatorConfig(AuthenticationFlowContext context) {
|
||||
return context != null
|
||||
&& context.getAuthenticatorConfig() != null
|
||||
&& context.getAuthenticatorConfig().getConfig() != null
|
||||
&& !context.getAuthenticatorConfig().getConfig().isEmpty();
|
||||
if (context == null)
|
||||
return false;
|
||||
AuthenticatorConfigModel config = getAuthenticatorConfig(context);
|
||||
return config != null
|
||||
&& config.getConfig() != null
|
||||
&& !config.getConfig().isEmpty();
|
||||
}
|
||||
|
||||
protected AuthenticatorConfigModel getAuthenticatorConfig(AuthenticationFlowContext context) {
|
||||
return context.getAuthenticatorConfig();
|
||||
}
|
||||
|
||||
private InvocableScriptAdapter getInvocableScriptAdapter(AuthenticationFlowContext context) {
|
||||
|
||||
Map<String, String> config = context.getAuthenticatorConfig().getConfig();
|
||||
Map<String, String> config = getAuthenticatorConfig(context).getConfig();
|
||||
|
||||
String scriptName = config.get(SCRIPT_NAME);
|
||||
String scriptCode = config.get(SCRIPT_CODE);
|
||||
|
|
|
@ -153,6 +153,6 @@ public class ScriptBasedAuthenticatorFactory implements AuthenticatorFactory, En
|
|||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return Profile.isFeatureEnabled(Profile.Feature.SCRIPTS);
|
||||
return Profile.isFeatureEnabled(Profile.Feature.SCRIPTS) && Profile.isFeatureEnabled(Profile.Feature.UPLOAD_SCRIPTS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,26 +18,18 @@
|
|||
|
||||
package org.keycloak.authorization;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.policy.evaluation.DefaultPolicyEvaluator;
|
||||
import org.keycloak.authorization.policy.evaluation.PolicyEvaluator;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class DefaultAuthorizationProviderFactory implements AuthorizationProviderFactory {
|
||||
|
||||
private Map<String, PolicyProviderFactory> policyProviderFactories;
|
||||
private PolicyEvaluator policyEvaluator = new DefaultPolicyEvaluator();
|
||||
|
||||
@Override
|
||||
|
@ -51,7 +43,6 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide
|
|||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
policyProviderFactories = configurePolicyProviderFactories(factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -66,21 +57,6 @@ public class DefaultAuthorizationProviderFactory implements AuthorizationProvide
|
|||
|
||||
@Override
|
||||
public AuthorizationProvider create(KeycloakSession session, RealmModel realm) {
|
||||
return new AuthorizationProvider(session, realm, policyProviderFactories, policyEvaluator);
|
||||
return new AuthorizationProvider(session, realm, policyEvaluator);
|
||||
}
|
||||
|
||||
private Map<String, PolicyProviderFactory> configurePolicyProviderFactories(KeycloakSessionFactory keycloakSessionFactory) {
|
||||
List<ProviderFactory> providerFactories = keycloakSessionFactory.getProviderFactories(PolicyProvider.class);
|
||||
|
||||
if (providerFactories.isEmpty()) {
|
||||
throw new RuntimeException("Could not find any policy provider.");
|
||||
}
|
||||
|
||||
HashMap<String, PolicyProviderFactory> providers = new HashMap<>();
|
||||
|
||||
providerFactories.forEach(providerFactory -> providers.put(providerFactory.getId(), (PolicyProviderFactory) providerFactory));
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
}
|
|
@ -109,7 +109,9 @@ public class PolicyResourceService {
|
|||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
PolicyProviderFactory resource = getProviderFactory(policy.getType());
|
||||
|
||||
if (resource != null) {
|
||||
resource.onRemove(policy, authorization);
|
||||
}
|
||||
|
||||
policyStore.delete(policy.getId());
|
||||
|
||||
|
|
|
@ -206,6 +206,8 @@ public class ResourceServerService {
|
|||
|
||||
defaultPolicy.setConfig(defaultPolicyConfig);
|
||||
|
||||
session.setAttribute("ALLOW_CREATE_POLICY", true);
|
||||
|
||||
getPolicyResource().create(defaultPolicy);
|
||||
|
||||
return defaultPolicy;
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright 2019 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.protocol.oidc.mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
import org.keycloak.representations.provider.ScriptProviderMetadata;
|
||||
|
||||
public final class DeployedScriptOIDCProtocolMapper extends ScriptBasedOIDCProtocolMapper {
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties;
|
||||
|
||||
static {
|
||||
configProperties = ProviderConfigurationBuilder.create()
|
||||
.property()
|
||||
.name(ProtocolMapperUtils.MULTIVALUED)
|
||||
.label(ProtocolMapperUtils.MULTIVALUED_LABEL)
|
||||
.helpText(ProtocolMapperUtils.MULTIVALUED_HELP_TEXT)
|
||||
.type(ProviderConfigProperty.BOOLEAN_TYPE)
|
||||
.defaultValue(false)
|
||||
.add()
|
||||
.build();
|
||||
|
||||
OIDCAttributeMapperHelper.addAttributeConfig(configProperties, UserPropertyMapper.class);
|
||||
}
|
||||
|
||||
private final ScriptProviderMetadata metadata;
|
||||
|
||||
public DeployedScriptOIDCProtocolMapper(ScriptProviderMetadata metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return metadata.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return metadata.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return metadata.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getScriptCode(ProtocolMapperModel mapperModel) {
|
||||
return metadata.getCode();
|
||||
}
|
||||
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return Profile.isFeatureEnabled(Profile.Feature.SCRIPTS);
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ import org.keycloak.models.UserModel;
|
|||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.ProtocolMapperConfigException;
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
import org.keycloak.representations.IDToken;
|
||||
|
@ -43,7 +44,8 @@ import java.util.List;
|
|||
*
|
||||
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
|
||||
*/
|
||||
public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper {
|
||||
public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper,
|
||||
EnvironmentDependentProviderFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "oidc-script-based-protocol-mapper";
|
||||
|
||||
|
@ -115,8 +117,9 @@ public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper im
|
|||
return "Evaluates a JavaScript function to produce a token claim based on context information.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return Profile.isFeatureEnabled(Profile.Feature.SCRIPTS);
|
||||
return Profile.isFeatureEnabled(Profile.Feature.SCRIPTS) && Profile.isFeatureEnabled(Profile.Feature.UPLOAD_SCRIPTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -128,7 +131,7 @@ public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper im
|
|||
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) {
|
||||
|
||||
UserModel user = userSession.getUser();
|
||||
String scriptSource = mappingModel.getConfig().get(SCRIPT);
|
||||
String scriptSource = getScriptCode(mappingModel);
|
||||
RealmModel realm = userSession.getRealm();
|
||||
|
||||
ScriptingProvider scripting = keycloakSession.getProvider(ScriptingProvider.class);
|
||||
|
@ -156,7 +159,7 @@ public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper im
|
|||
@Override
|
||||
public void validateConfig(KeycloakSession session, RealmModel realm, ProtocolMapperContainerModel client, ProtocolMapperModel mapperModel) throws ProtocolMapperConfigException {
|
||||
|
||||
String scriptCode = mapperModel.getConfig().get(SCRIPT);
|
||||
String scriptCode = getScriptCode(mapperModel);
|
||||
if (scriptCode == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -171,6 +174,10 @@ public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper im
|
|||
}
|
||||
}
|
||||
|
||||
protected String getScriptCode(ProtocolMapperModel mapperModel) {
|
||||
return mapperModel.getConfig().get(SCRIPT);
|
||||
}
|
||||
|
||||
public static ProtocolMapperModel create(String name,
|
||||
String userAttribute,
|
||||
String tokenClaimName, String claimType,
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2019 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.provider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
final class DeploymentProviderLoader implements ProviderLoader {
|
||||
|
||||
private final KeycloakDeploymentInfo info;
|
||||
|
||||
DeploymentProviderLoader(KeycloakDeploymentInfo info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Spi> loadSpis() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderFactory> load(Spi spi) {
|
||||
return info.getProviders().getOrDefault(spi.getClass(), Collections.emptyList());
|
||||
}
|
||||
}
|
|
@ -49,6 +49,7 @@ public class ProviderManager {
|
|||
logger.debugv("Provider loaders {0}", factories);
|
||||
|
||||
loaders.add(new DefaultProviderLoader(info, baseClassLoader));
|
||||
loaders.add(new DeploymentProviderLoader(info));
|
||||
|
||||
if (resources != null) {
|
||||
for (String r : resources) {
|
||||
|
|
|
@ -77,6 +77,7 @@ public class ProfileAssume {
|
|||
}
|
||||
|
||||
private static boolean isFeatureEnabled(Profile.Feature feature) {
|
||||
updateProfile();
|
||||
return !disabledFeatures.contains(feature.name());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.keycloak.admin.client.resource.AuthenticationManagementResource;
|
|||
import org.keycloak.admin.client.resource.RealmsResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
|
@ -61,6 +62,7 @@ import org.keycloak.testsuite.util.TestEventsLogger;
|
|||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -81,6 +83,7 @@ import java.util.stream.Collectors;
|
|||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.keycloak.testsuite.admin.Users.setPasswordFor;
|
||||
import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN;
|
||||
|
@ -149,6 +152,7 @@ public abstract class AbstractKeycloakTest {
|
|||
private PropertiesConfiguration constantsProperties;
|
||||
|
||||
private boolean resetTimeOffset;
|
||||
private List<Profile.Feature> enabledFeatures = new ArrayList<>();
|
||||
|
||||
@Before
|
||||
public void beforeAbstractKeycloakTest() throws Exception {
|
||||
|
@ -226,6 +230,10 @@ public abstract class AbstractKeycloakTest {
|
|||
testContext.getCleanups().clear();
|
||||
}
|
||||
|
||||
for (Profile.Feature feature : enabledFeatures) {
|
||||
disableFeature(feature);
|
||||
}
|
||||
|
||||
postAfterAbstractKeycloak();
|
||||
|
||||
// Remove all browsers from queue
|
||||
|
@ -631,4 +639,17 @@ public abstract class AbstractKeycloakTest {
|
|||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
protected void enableFeature(Profile.Feature feature) {
|
||||
enabledFeatures.add(feature);
|
||||
try (Response response = getTestingClient().testing().enableFeature(feature.toString())) {
|
||||
assertEquals(200, response.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
protected void disableFeature(Profile.Feature feature) {
|
||||
try (Response response = getTestingClient().testing().disableFeature(feature.toString())) {
|
||||
assertEquals(200, response.getStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import static org.hamcrest.Matchers.equalTo;
|
|||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.keycloak.common.Profile.Feature.UPLOAD_SCRIPTS;
|
||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
|
||||
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
|
||||
import static org.keycloak.testsuite.utils.io.IOUtil.loadJson;
|
||||
|
@ -164,6 +165,7 @@ public abstract class AbstractBasePhotozExampleAdapterTest extends AbstractPhoto
|
|||
|
||||
@Override
|
||||
public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
|
||||
enableFeature(UPLOAD_SCRIPTS);
|
||||
RealmRepresentation realm = loadRealm(new File(TEST_APPS_HOME_DIR + "/photoz/photoz-realm.json"));
|
||||
|
||||
realm.setAccessTokenLifespan(30 + TOKEN_LIFESPAN_LEEWAY); // seconds
|
||||
|
|
|
@ -18,7 +18,7 @@ package org.keycloak.testsuite.adapter.example.authorization;
|
|||
|
||||
import org.jboss.arquillian.container.test.api.Deployer;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Before;
|
||||
import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
|
@ -27,7 +27,6 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
|||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
||||
import org.keycloak.testsuite.util.UIUtils;
|
||||
import org.openqa.selenium.By;
|
||||
|
@ -42,6 +41,7 @@ import java.net.URL;
|
|||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.keycloak.common.Profile.Feature.UPLOAD_SCRIPTS;
|
||||
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
|
||||
import static org.keycloak.testsuite.utils.io.IOUtil.loadJson;
|
||||
import static org.keycloak.testsuite.utils.io.IOUtil.loadRealm;
|
||||
|
@ -58,6 +58,11 @@ public abstract class AbstractBaseServletAuthzAdapterTest extends AbstractExampl
|
|||
@ArquillianResource
|
||||
private Deployer deployer;
|
||||
|
||||
@Before
|
||||
public void onBefore() {
|
||||
enableFeature(UPLOAD_SCRIPTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
|
||||
testRealms.add(
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.keycloak.testsuite.adapter.example.authorization;
|
|||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.keycloak.common.Profile.Feature.UPLOAD_SCRIPTS;
|
||||
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
|
||||
import static org.keycloak.testsuite.utils.io.IOUtil.loadRealm;
|
||||
|
||||
|
@ -32,6 +33,7 @@ import org.jboss.arquillian.container.test.api.Deployer;
|
|||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
|
@ -41,6 +43,7 @@ import org.keycloak.representations.idm.ClientRepresentation;
|
|||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
|
||||
|
@ -69,6 +72,7 @@ public class ServletPolicyEnforcerTest extends AbstractExampleAdapterTest {
|
|||
|
||||
@Override
|
||||
public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
|
||||
enableFeature(UPLOAD_SCRIPTS);
|
||||
testRealms.add(
|
||||
loadRealm(new File(TEST_APPS_HOME_DIR + "/servlet-policy-enforcer/servlet-policy-enforcer-authz-realm.json")));
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.junit.After;
|
|||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
||||
import org.keycloak.testsuite.adapter.page.AngularCorsProductTestApp;
|
||||
|
@ -108,6 +109,7 @@ public class CorsExampleAdapterTest extends AbstractExampleAdapterTest {
|
|||
|
||||
@Override
|
||||
public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
|
||||
enableFeature(Profile.Feature.UPLOAD_SCRIPTS);
|
||||
testRealms.add(
|
||||
loadRealm(new File(TEST_APPS_HOME_DIR + "/cors/cors-realm.json")));
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ import org.keycloak.representations.idm.RequiredActionProviderSimpleRepresentati
|
|||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
||||
|
@ -957,13 +958,10 @@ public class PermissionsTest extends AbstractKeycloakTest {
|
|||
invoke(new InvocationWithResponse() {
|
||||
public void invoke(RealmResource realm, AtomicReference<Response> response) {
|
||||
AuthorizationResource authorization = realm.clients().get(foo.getId()).authorization();
|
||||
PolicyRepresentation representation = new PolicyRepresentation();
|
||||
ResourcePermissionRepresentation representation = new ResourcePermissionRepresentation();
|
||||
representation.setName("Test PermissionsTest");
|
||||
representation.setType("js");
|
||||
HashMap<String, String> config = new HashMap<>();
|
||||
config.put("code", "");
|
||||
representation.setConfig(config);
|
||||
response.set(authorization.policies().create(representation));
|
||||
representation.addResource("Default Resource");
|
||||
response.set(authorization.permissions().resource().create(representation));
|
||||
}
|
||||
}, AUTHORIZATION, true);
|
||||
invoke(new Invocation() {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package org.keycloak.testsuite.admin.client.authorization;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
|
@ -36,6 +37,7 @@ import org.keycloak.testsuite.util.UserBuilder;
|
|||
import javax.ws.rs.core.Response;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.keycloak.common.Profile.Feature.UPLOAD_SCRIPTS;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -46,6 +48,11 @@ public abstract class AbstractAuthorizationTest extends AbstractClientTest {
|
|||
|
||||
protected static final String RESOURCE_SERVER_CLIENT_ID = "resource-server-test";
|
||||
|
||||
@Before
|
||||
public void onBefore() {
|
||||
enableFeature(UPLOAD_SCRIPTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultPageUriParameters() {
|
||||
super.setDefaultPageUriParameters();
|
||||
|
|
|
@ -24,10 +24,12 @@ import java.util.Collections;
|
|||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||
import org.keycloak.admin.client.resource.JSPoliciesResource;
|
||||
import org.keycloak.admin.client.resource.JSPolicyResource;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
|
@ -37,6 +39,11 @@ import org.keycloak.representations.idm.authorization.Logic;
|
|||
*/
|
||||
public class JSPolicyManagementTest extends AbstractPolicyManagementTest {
|
||||
|
||||
@Before
|
||||
public void onBefore() {
|
||||
enableFeature(Profile.Feature.UPLOAD_SCRIPTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() {
|
||||
AuthorizationResource authorization = getClient().authorization();
|
||||
|
|
|
@ -35,6 +35,7 @@ import java.util.stream.Collectors;
|
|||
|
||||
import javax.security.cert.X509Certificate;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.AuthorizationContext;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
|
@ -51,6 +52,7 @@ import org.keycloak.adapters.spi.LogoutError;
|
|||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
import org.keycloak.authorization.client.AuthzClient;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.JWSInputException;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
@ -108,6 +110,11 @@ public class PolicyEnforcerClaimsTest extends AbstractKeycloakTest {
|
|||
.build());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void onBefore() {
|
||||
enableFeature(Profile.Feature.UPLOAD_SCRIPTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnforceUMAAccessWithClaimsUsingBearerToken() {
|
||||
initAuthorizationSettings(getClientResource("resource-server-uma-test"));
|
||||
|
|
|
@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.keycloak.common.Profile.Feature.UPLOAD_SCRIPTS;
|
||||
|
||||
import javax.security.cert.X509Certificate;
|
||||
import javax.ws.rs.HttpMethod;
|
||||
|
@ -126,6 +127,7 @@ public class PolicyEnforcerTest extends AbstractKeycloakTest {
|
|||
|
||||
@Before
|
||||
public void onBefore() {
|
||||
enableFeature(UPLOAD_SCRIPTS);
|
||||
initAuthorizationSettings(getClientResource(RESOURCE_SERVER_CLIENT_ID));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
package org.keycloak.testsuite.authz;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import static org.keycloak.common.Profile.Feature.UPLOAD_SCRIPTS;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.JWSInputException;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public abstract class AbstractAuthzTest extends AbstractKeycloakTest {
|
||||
|
||||
@Before
|
||||
public void onBefore() {
|
||||
enableFeature(UPLOAD_SCRIPTS);
|
||||
}
|
||||
|
||||
protected AccessToken toAccessToken(String rpt) {
|
||||
AccessToken accessToken;
|
||||
|
||||
|
|
|
@ -96,8 +96,7 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
|
|||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() {
|
||||
private void testCreate() {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setName("Resource A");
|
||||
|
@ -148,7 +147,12 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() {
|
||||
public void testCreateDeprecatedFeaturesDisabled() {
|
||||
ProfileAssume.assumeFeatureDisabled(Profile.Feature.UPLOAD_SCRIPTS);
|
||||
testCreate();
|
||||
}
|
||||
|
||||
private void testUpdate() {
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setName("Resource A");
|
||||
|
@ -338,8 +342,14 @@ public class UserManagedPermissionServiceTest extends AbstractResourceServerTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testUploadScriptDisabled() {
|
||||
public void testUpdateDeprecatedFeaturesDisabled() {
|
||||
ProfileAssume.assumeFeatureDisabled(Profile.Feature.UPLOAD_SCRIPTS);
|
||||
testUpdate();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadScriptDisabled() {
|
||||
disableFeature(Profile.Feature.UPLOAD_SCRIPTS);
|
||||
ResourceRepresentation resource = new ResourceRepresentation();
|
||||
|
||||
resource.setName("Resource A");
|
||||
|
|
|
@ -67,7 +67,9 @@ public class ScriptAuthenticatorTest extends AbstractFlowTest {
|
|||
|
||||
@BeforeClass
|
||||
public static void verifyEnvironment() {
|
||||
// TODO: we should probably enable SCRIPTS automatically when UPLOAD_SCRIPTS is enabled
|
||||
ProfileAssume.assumeFeatureEnabled(Profile.Feature.SCRIPTS);
|
||||
ProfileAssume.assumeFeatureEnabled(Profile.Feature.UPLOAD_SCRIPTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.keycloak.admin.client.resource.ClientScopeResource;
|
|||
import org.keycloak.admin.client.resource.ProtocolMappersResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.util.UriUtils;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.models.AccountRoles;
|
||||
|
@ -45,6 +46,7 @@ import org.keycloak.representations.idm.UserRepresentation;
|
|||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.util.ClientManager;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
|
@ -126,6 +128,27 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
|||
testRealms.add(realm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenScriptMapping() {
|
||||
ProfileAssume.assumeFeatureEnabled(Profile.Feature.UPLOAD_SCRIPTS);
|
||||
{
|
||||
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
|
||||
|
||||
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper1","computed-via-script", "computed-via-script", "String", true, true, "'hello_' + user.username", false)).close();
|
||||
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper2","multiValued-via-script", "multiValued-via-script", "String", true, true, "new java.util.ArrayList(['A','B'])", true)).close();
|
||||
|
||||
Response response = app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3", "syntax-error-script", "syntax-error-script", "String", true, true, "func_tion foo(){ return 'fail';} foo()", false));
|
||||
assertThat(response.getStatusInfo().getFamily(), is(Response.Status.Family.CLIENT_ERROR));
|
||||
response.close();
|
||||
}
|
||||
{
|
||||
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password");
|
||||
AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
|
||||
|
||||
assertEquals("hello_test-user@localhost", accessToken.getOtherClaims().get("computed-via-script"));
|
||||
assertEquals(Arrays.asList("A","B"), accessToken.getOtherClaims().get("multiValued-via-script"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenMapping() throws Exception {
|
||||
|
@ -242,9 +265,6 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
|
|||
Assert.assertNull(accessToken.getResourceAccess("test-app"));
|
||||
assertTrue(accessToken.getResourceAccess("app").getRoles().contains("hardcoded"));
|
||||
|
||||
assertEquals("hello_test-user@localhost", accessToken.getOtherClaims().get("computed-via-script"));
|
||||
assertEquals(Arrays.asList("A","B"), accessToken.getOtherClaims().get("multiValued-via-script"));
|
||||
|
||||
// Assert audiences added through AudienceResolve mapper
|
||||
Assert.assertThat(accessToken.getAudience(), arrayContainingInAnyOrder( "app", "account"));
|
||||
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* 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.testsuite.script;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.keycloak.common.Profile.Feature.SCRIPTS;
|
||||
import static org.keycloak.common.Profile.Feature.UPLOAD_SCRIPTS;
|
||||
import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.Deployer;
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.container.test.api.TargetsContainer;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.jboss.shrinkwrap.api.ShrinkWrap;
|
||||
import org.jboss.shrinkwrap.api.asset.StringAsset;
|
||||
import org.jboss.shrinkwrap.api.spec.JavaArchive;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticatorFactory;
|
||||
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.representations.idm.AuthenticationExecutionRepresentation;
|
||||
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.provider.ScriptProviderDescriptor;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.forms.AbstractFlowTest;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.util.ContainerAssume;
|
||||
import org.keycloak.testsuite.util.ExecutionBuilder;
|
||||
import org.keycloak.testsuite.util.FlowBuilder;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class DeployedScriptAuthenticatorTest extends AbstractFlowTest {
|
||||
|
||||
public static final String EXECUTION_ID = "scriptAuth";
|
||||
private static final String SCRIPT_DEPLOYMENT_NAME = "scripts.jar";
|
||||
|
||||
@Deployment(name = SCRIPT_DEPLOYMENT_NAME, managed = false, testable = false)
|
||||
@TargetsContainer(AUTH_SERVER_CURRENT)
|
||||
public static JavaArchive deploy() throws IOException {
|
||||
ScriptProviderDescriptor representation = new ScriptProviderDescriptor();
|
||||
|
||||
representation.addAuthenticator("My Authenticator", "authenticator-a.js");
|
||||
|
||||
return ShrinkWrap.create(JavaArchive.class, SCRIPT_DEPLOYMENT_NAME)
|
||||
.addAsManifestResource(new StringAsset(JsonSerialization.writeValueAsPrettyString(representation)),
|
||||
"keycloak-scripts.json")
|
||||
.addAsResource("scripts/authenticator-example.js", "authenticator-a.js");
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void verifyEnvironment() {
|
||||
ContainerAssume.assumeNotAuthServerUndertow();
|
||||
}
|
||||
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(this);
|
||||
|
||||
@Page
|
||||
protected LoginPage loginPage;
|
||||
|
||||
@ArquillianResource
|
||||
private Deployer deployer;
|
||||
|
||||
private AuthenticationFlowRepresentation flow;
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
UserRepresentation failUser = UserBuilder.create()
|
||||
.id("fail")
|
||||
.username("fail")
|
||||
.email("fail@test.com")
|
||||
.enabled(true)
|
||||
.password("password")
|
||||
.build();
|
||||
|
||||
UserRepresentation okayUser = UserBuilder.create()
|
||||
.id("user")
|
||||
.username("user")
|
||||
.email("user@test.com")
|
||||
.enabled(true)
|
||||
.password("password")
|
||||
.build();
|
||||
|
||||
RealmBuilder.edit(testRealm)
|
||||
.user(failUser)
|
||||
.user(okayUser);
|
||||
}
|
||||
|
||||
public void configureFlows() {
|
||||
deployer.deploy(SCRIPT_DEPLOYMENT_NAME);
|
||||
|
||||
if (testContext.isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String scriptFlow = "scriptBrowser";
|
||||
|
||||
AuthenticationFlowRepresentation scriptBrowserFlow = FlowBuilder.create()
|
||||
.alias(scriptFlow)
|
||||
.description("dummy pass through registration")
|
||||
.providerId("basic-flow")
|
||||
.topLevel(true)
|
||||
.builtIn(false)
|
||||
.build();
|
||||
|
||||
Response createFlowResponse = testRealm().flows().createFlow(scriptBrowserFlow);
|
||||
Assert.assertEquals(201, createFlowResponse.getStatus());
|
||||
|
||||
RealmRepresentation realm = testRealm().toRepresentation();
|
||||
realm.setBrowserFlow(scriptFlow);
|
||||
realm.setDirectGrantFlow(scriptFlow);
|
||||
testRealm().update(realm);
|
||||
|
||||
this.flow = findFlowByAlias(scriptFlow);
|
||||
|
||||
AuthenticationExecutionRepresentation usernamePasswordFormExecution = ExecutionBuilder.create()
|
||||
.id("username password form")
|
||||
.parentFlow(this.flow.getId())
|
||||
.requirement(AuthenticationExecutionModel.Requirement.REQUIRED.name())
|
||||
.authenticator(UsernamePasswordFormFactory.PROVIDER_ID)
|
||||
.build();
|
||||
|
||||
AuthenticationExecutionRepresentation authScriptExecution = ExecutionBuilder.create()
|
||||
.id(EXECUTION_ID)
|
||||
.parentFlow(this.flow.getId())
|
||||
.requirement(AuthenticationExecutionModel.Requirement.REQUIRED.name())
|
||||
.authenticator("script-authenticator-a.js")
|
||||
.build();
|
||||
|
||||
Response addExecutionResponse = testRealm().flows().addExecution(usernamePasswordFormExecution);
|
||||
Assert.assertEquals(201, addExecutionResponse.getStatus());
|
||||
addExecutionResponse.close();
|
||||
|
||||
addExecutionResponse = testRealm().flows().addExecution(authScriptExecution);
|
||||
Assert.assertEquals(201, addExecutionResponse.getStatus());
|
||||
addExecutionResponse.close();
|
||||
|
||||
testContext.setInitialized(true);
|
||||
}
|
||||
|
||||
@After
|
||||
public void onAfter() {
|
||||
deployer.undeploy(SCRIPT_DEPLOYMENT_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* KEYCLOAK-3491
|
||||
*/
|
||||
@Test
|
||||
public void loginShouldWorkWithScriptAuthenticator() {
|
||||
ProfileAssume.assumeFeatureEnabled(SCRIPTS);
|
||||
configureFlows();
|
||||
|
||||
loginPage.open();
|
||||
|
||||
loginPage.login("user", "password");
|
||||
|
||||
events.expectLogin().user("user").detail(Details.USERNAME, "user").assertEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* KEYCLOAK-3491
|
||||
*/
|
||||
@Test
|
||||
public void loginShouldFailWithScriptAuthenticator() {
|
||||
ProfileAssume.assumeFeatureEnabled(SCRIPTS);
|
||||
configureFlows();
|
||||
|
||||
loginPage.open();
|
||||
|
||||
loginPage.login("fail", "password");
|
||||
|
||||
events.expect(EventType.LOGIN_ERROR).user((String) null).error(Errors.USER_NOT_FOUND).assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScriptAuthenticatorNotAvailable() {
|
||||
ProfileAssume.assumeFeatureDisabled(UPLOAD_SCRIPTS);
|
||||
assertFalse(testRealm().flows().getAuthenticatorProviders().stream().anyMatch(
|
||||
provider -> ScriptBasedAuthenticatorFactory.PROVIDER_ID.equals(provider.get("id"))));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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.testsuite.script;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.keycloak.common.Profile.Feature.UPLOAD_SCRIPTS;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.findClientResourceByClientId;
|
||||
import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
|
||||
import static org.keycloak.testsuite.util.ProtocolMapperUtil.createScriptMapper;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.Deployer;
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.container.test.api.TargetsContainer;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.jboss.shrinkwrap.api.ShrinkWrap;
|
||||
import org.jboss.shrinkwrap.api.asset.StringAsset;
|
||||
import org.jboss.shrinkwrap.api.spec.JavaArchive;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.mappers.ScriptBasedOIDCProtocolMapper;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.provider.ScriptProviderDescriptor;
|
||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.util.ContainerAssume;
|
||||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class DeployedScriptMapperTest extends AbstractTestRealmKeycloakTest {
|
||||
|
||||
private static final String SCRIPT_DEPLOYMENT_NAME = "scripts.jar";
|
||||
|
||||
@Deployment(name = SCRIPT_DEPLOYMENT_NAME, managed = false, testable = false)
|
||||
@TargetsContainer(AUTH_SERVER_CURRENT)
|
||||
public static JavaArchive deploy() throws IOException {
|
||||
ScriptProviderDescriptor representation = new ScriptProviderDescriptor();
|
||||
|
||||
representation.addMapper("My Mapper", "mapper-a.js");
|
||||
|
||||
return ShrinkWrap.create(JavaArchive.class, SCRIPT_DEPLOYMENT_NAME)
|
||||
.addAsManifestResource(new StringAsset(JsonSerialization.writeValueAsPrettyString(representation)),
|
||||
"keycloak-scripts.json")
|
||||
.addAsResource("scripts/mapper-example.js", "mapper-a.js");
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void verifyEnvironment() {
|
||||
ContainerAssume.assumeNotAuthServerUndertow();
|
||||
}
|
||||
|
||||
@ArquillianResource
|
||||
private Deployer deployer;
|
||||
|
||||
@Before
|
||||
public void configureFlows() {
|
||||
deployer.deploy(SCRIPT_DEPLOYMENT_NAME);
|
||||
}
|
||||
|
||||
@After
|
||||
public void onAfter() {
|
||||
deployer.undeploy(SCRIPT_DEPLOYMENT_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScriptMapperNotAvailable() {
|
||||
ProfileAssume.assumeFeatureDisabled(UPLOAD_SCRIPTS);
|
||||
assertFalse(adminClient.serverInfo().getInfo().getProtocolMapperTypes().get(OIDCLoginProtocol.LOGIN_PROTOCOL).stream()
|
||||
.anyMatch(
|
||||
mapper -> ScriptBasedOIDCProtocolMapper.PROVIDER_ID.equals(mapper.getId())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenScriptMapping() {
|
||||
ProfileAssume.assumeFeatureEnabled(Profile.Feature.SCRIPTS);
|
||||
{
|
||||
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
|
||||
|
||||
ProtocolMapperRepresentation mapper = createScriptMapper("test-script-mapper1", "computed-via-script",
|
||||
"computed-via-script", "String", true, true, "'hello_' + user.username", false);
|
||||
|
||||
mapper.setProtocolMapper("script-mapper-a.js");
|
||||
|
||||
app.getProtocolMappers().createMapper(mapper).close();
|
||||
}
|
||||
{
|
||||
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password");
|
||||
AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
|
||||
|
||||
assertEquals("hello_test-user@localhost", accessToken.getOtherClaims().get("computed-via-script"));
|
||||
}
|
||||
}
|
||||
|
||||
private OAuthClient.AccessTokenResponse browserLogin(String clientSecret, String username, String password) {
|
||||
OAuthClient.AuthorizationEndpointResponse authzEndpointResponse = oauth.doLogin(username, password);
|
||||
return oauth.doAccessTokenRequest(authzEndpointResponse.getCode(), clientSecret);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* 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.testsuite.script;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.keycloak.common.Profile.Feature.UPLOAD_SCRIPTS;
|
||||
import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.Deployer;
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.container.test.api.TargetsContainer;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.jboss.shrinkwrap.api.ShrinkWrap;
|
||||
import org.jboss.shrinkwrap.api.asset.StringAsset;
|
||||
import org.jboss.shrinkwrap.api.spec.JavaArchive;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
import org.keycloak.admin.client.resource.PermissionsResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.DecisionEffect;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEvaluationRequest;
|
||||
import org.keycloak.representations.idm.authorization.PolicyEvaluationResponse;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.provider.ScriptProviderDescriptor;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.arquillian.annotation.UncaughtServerErrorExpected;
|
||||
import org.keycloak.testsuite.authz.AbstractAuthzTest;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
import org.keycloak.testsuite.util.ContainerAssume;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
import org.keycloak.testsuite.util.RoleBuilder;
|
||||
import org.keycloak.testsuite.util.RolesBuilder;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class DeployedScriptPolicyTest extends AbstractAuthzTest {
|
||||
|
||||
private static final String SCRIPT_DEPLOYMENT_NAME = "scripts.jar";
|
||||
|
||||
@Deployment(name = SCRIPT_DEPLOYMENT_NAME, managed = false, testable = false)
|
||||
@TargetsContainer(AUTH_SERVER_CURRENT)
|
||||
public static JavaArchive deploy() throws IOException {
|
||||
ScriptProviderDescriptor representation = new ScriptProviderDescriptor();
|
||||
|
||||
representation.addPolicy("Grant Policy", "policy-grant.js");
|
||||
representation.addPolicy("Deny Policy", "policy-deny.js");
|
||||
|
||||
return ShrinkWrap.create(JavaArchive.class, SCRIPT_DEPLOYMENT_NAME)
|
||||
.addAsManifestResource(new StringAsset(JsonSerialization.writeValueAsPrettyString(representation)),
|
||||
"keycloak-scripts.json")
|
||||
.addAsResource(new StringAsset("$evaluation.grant();"), "policy-grant.js")
|
||||
.addAsResource(new StringAsset("$evaluation.deny();"), "policy-deny.js");
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void verifyEnvironment() {
|
||||
ContainerAssume.assumeNotAuthServerUndertow();
|
||||
}
|
||||
@ArquillianResource
|
||||
private Deployer deployer;
|
||||
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
testRealms.add(RealmBuilder.create().name("authz-test")
|
||||
.roles(RolesBuilder.create().realmRole(RoleBuilder.create().name("uma_authorization").build()))
|
||||
.user(UserBuilder.create().username("marta").password("password").addRoles("uma_authorization"))
|
||||
.user(UserBuilder.create().username("kolo").password("password"))
|
||||
.client(ClientBuilder.create().clientId("resource-server")
|
||||
.secret("secret")
|
||||
.authorizationServicesEnabled(true)
|
||||
.redirectUris("http://localhost/resource-server-test")
|
||||
.defaultRoles("uma_protection")
|
||||
.directAccessGrants())
|
||||
.build());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void onBefore() {
|
||||
deployer.deploy(SCRIPT_DEPLOYMENT_NAME);
|
||||
AuthorizationResource authorization = getAuthorizationResource();
|
||||
authorization.resources().create(new ResourceRepresentation("Default Resource"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void onAfter() {
|
||||
deployer.undeploy(SCRIPT_DEPLOYMENT_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJSPolicyProviderNotAvailable() {
|
||||
ProfileAssume.assumeFeatureDisabled(UPLOAD_SCRIPTS);
|
||||
assertFalse(getAuthorizationResource().policies().policyProviders().stream().anyMatch(rep -> "js".equals(rep.getType())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UncaughtServerErrorExpected
|
||||
public void failCreateJSPolicy() {
|
||||
ProfileAssume.assumeFeatureDisabled(UPLOAD_SCRIPTS);
|
||||
JSPolicyRepresentation grantPolicy = new JSPolicyRepresentation();
|
||||
|
||||
grantPolicy.setName("JS Policy");
|
||||
grantPolicy.setType("js");
|
||||
grantPolicy.setCode("$evaluation.grant();");
|
||||
|
||||
try (Response response = getAuthorizationResource().policies().js().create(grantPolicy)) {
|
||||
assertEquals(500, response.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePermission() {
|
||||
AuthorizationResource authorization = getAuthorizationResource();
|
||||
PolicyRepresentation grantPolicy = new PolicyRepresentation();
|
||||
|
||||
grantPolicy.setName("Grant Policy");
|
||||
grantPolicy.setType("script-policy-grant.js");
|
||||
|
||||
authorization.policies().create(grantPolicy).close();
|
||||
|
||||
PolicyRepresentation denyPolicy = new PolicyRepresentation();
|
||||
|
||||
denyPolicy.setName("Deny Policy");
|
||||
denyPolicy.setType("script-policy-deny.js");
|
||||
|
||||
authorization.policies().create(denyPolicy).close();
|
||||
|
||||
PermissionsResource permissions = authorization.permissions();
|
||||
|
||||
ResourcePermissionRepresentation permission = new ResourcePermissionRepresentation();
|
||||
|
||||
permission.setName("Test Deployed JS Permission");
|
||||
permission.addResource("Default Resource");
|
||||
permission.addPolicy(grantPolicy.getName());
|
||||
|
||||
permissions.resource().create(permission).close();
|
||||
|
||||
PolicyEvaluationRequest request = new PolicyEvaluationRequest();
|
||||
|
||||
request.setUserId("marta");
|
||||
request.addResource("Default Resource");
|
||||
|
||||
PolicyEvaluationResponse response = authorization.policies().evaluate(request);
|
||||
|
||||
assertEquals(DecisionEffect.PERMIT, response.getStatus());
|
||||
|
||||
permission = permissions.resource().findByName(permission.getName());
|
||||
|
||||
permission.addPolicy(denyPolicy.getName());
|
||||
|
||||
permissions.resource().findById(permission.getId()).update(permission);
|
||||
|
||||
response = authorization.policies().evaluate(request);
|
||||
|
||||
assertEquals(DecisionEffect.DENY, response.getStatus());
|
||||
|
||||
permission.addPolicy(grantPolicy.getName());
|
||||
|
||||
permissions.resource().findById(permission.getId()).update(permission);
|
||||
|
||||
response = authorization.policies().evaluate(request);
|
||||
|
||||
assertEquals(DecisionEffect.DENY, response.getStatus());
|
||||
|
||||
permission.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
|
||||
|
||||
permissions.resource().findById(permission.getId()).update(permission);
|
||||
|
||||
response = authorization.policies().evaluate(request);
|
||||
|
||||
assertEquals(DecisionEffect.PERMIT, response.getStatus());
|
||||
}
|
||||
|
||||
private AuthorizationResource getAuthorizationResource() {
|
||||
return getClient(realmsResouce().realm("authz-test"), "resource-server").authorization();
|
||||
}
|
||||
|
||||
private ClientResource getClient(RealmResource realm, String clientId) {
|
||||
ClientsResource clients = realm.clients();
|
||||
return clients.findByClientId(clientId).stream().map(representation -> clients.get(representation.getId())).findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("Expected client [resource-server-test]"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
'hello_' + user.username
|
|
@ -19,6 +19,7 @@ package org.keycloak.testsuite.console.authorization;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.keycloak.common.Profile.Feature.UPLOAD_SCRIPTS;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -52,6 +53,11 @@ import org.keycloak.testsuite.util.UserBuilder;
|
|||
*/
|
||||
public class AggregatePolicyManagementTest extends AbstractAuthorizationSettingsTest {
|
||||
|
||||
@Before
|
||||
public void onBefore() {
|
||||
enableFeature(UPLOAD_SCRIPTS);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void configureTest() {
|
||||
super.configureTest();
|
||||
|
|
|
@ -18,7 +18,11 @@ package org.keycloak.testsuite.console.authorization;
|
|||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.keycloak.common.Profile.Feature.UPLOAD_SCRIPTS;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
|
@ -29,6 +33,11 @@ import org.keycloak.testsuite.console.page.clients.authorization.policy.JSPolicy
|
|||
*/
|
||||
public class JSPolicyManagementTest extends AbstractAuthorizationSettingsTest {
|
||||
|
||||
@Before
|
||||
public void onBefore() {
|
||||
enableFeature(UPLOAD_SCRIPTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() throws InterruptedException {
|
||||
authorizationPage.navigateTo();
|
||||
|
|
|
@ -354,6 +354,9 @@ module.config(['$routeProvider', function ($routeProvider) {
|
|||
},
|
||||
client : function(ClientLoader) {
|
||||
return ClientLoader();
|
||||
},
|
||||
serverInfo : function(ServerInfoLoader) {
|
||||
return ServerInfoLoader();
|
||||
}
|
||||
},
|
||||
controller: 'ResourceServerPolicyJSDetailCtrl'
|
||||
|
@ -365,6 +368,9 @@ module.config(['$routeProvider', function ($routeProvider) {
|
|||
},
|
||||
client : function(ClientLoader) {
|
||||
return ClientLoader();
|
||||
},
|
||||
serverInfo : function(ServerInfoLoader) {
|
||||
return ServerInfoLoader();
|
||||
}
|
||||
},
|
||||
controller: 'ResourceServerPolicyJSDetailCtrl'
|
||||
|
|
|
@ -717,8 +717,15 @@ module.controller('ResourceServerPolicyCtrl', function($scope, $http, $route, $l
|
|||
});
|
||||
|
||||
$scope.addPolicy = function(policyType) {
|
||||
if (policyType.type.endsWith('.js')) {
|
||||
ResourceServerPolicy.save({realm : realm.realm, client : client.id, type: policyType.type}, {name: policyType.name, type: policyType.type}, function(data) {
|
||||
$location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/policy/");
|
||||
Notifications.success("The policy has been created.");
|
||||
});
|
||||
} else {
|
||||
$location.url("/realms/" + realm.realm + "/clients/" + client.id + "/authz/resource-server/policy/" + policyType.type + "/create");
|
||||
}
|
||||
}
|
||||
|
||||
$scope.firstPage = function() {
|
||||
$scope.query.first = 0;
|
||||
|
@ -1953,15 +1960,17 @@ module.controller('ResourceServerPolicyGroupDetailCtrl', function($scope, $route
|
|||
}, realm, client, $scope);
|
||||
});
|
||||
|
||||
module.controller('ResourceServerPolicyJSDetailCtrl', function($scope, $route, $location, realm, PolicyController, client) {
|
||||
module.controller('ResourceServerPolicyJSDetailCtrl', function($scope, $route, $location, realm, PolicyController, client, serverInfo) {
|
||||
PolicyController.onInit({
|
||||
getPolicyType : function() {
|
||||
return "js";
|
||||
},
|
||||
|
||||
onInit : function() {
|
||||
$scope.readOnly = !serverInfo.featureEnabled('UPLOAD_SCRIPTS');
|
||||
$scope.initEditor = function(editor){
|
||||
editor.$blockScrolling = Infinity;
|
||||
editor.setReadOnly($scope.readOnly);
|
||||
var session = editor.getSession();
|
||||
session.setMode('ace/mode/javascript');
|
||||
};
|
||||
|
|
|
@ -23,14 +23,14 @@
|
|||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
|
||||
<div class="col-sm-6">
|
||||
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required data-ng-blur="checkNewNameAvailability()">
|
||||
<input class="form-control" type="text" id="name" name="name" data-ng-model="policy.name" autofocus required data-ng-blur="checkNewNameAvailability()" data-ng-disabled="readOnly">
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'authz-policy-name.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="description">{{:: 'description' | translate}} </label>
|
||||
<div class="col-sm-6">
|
||||
<input class="form-control" type="text" id="description" name="description" data-ng-model="policy.description">
|
||||
<input class="form-control" type="text" id="description" name="description" data-ng-model="policy.description" data-ng-disabled="readOnly">
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'authz-policy-description.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
|
@ -46,7 +46,7 @@
|
|||
|
||||
<div class="col-sm-1">
|
||||
<select class="form-control" id="logic"
|
||||
data-ng-model="policy.logic">
|
||||
data-ng-model="policy.logic" data-ng-disabled="readOnly">
|
||||
<option value="POSITIVE">{{:: 'authz-policy-logic-positive' | translate}}</option>
|
||||
<option value="NEGATIVE">{{:: 'authz-policy-logic-negative' | translate}}</option>
|
||||
</select>
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
<span ng-if="!policy.details || !policy.details.loaded" class="fa fa-angle-right"></span>
|
||||
<span ng-if="policy.details && policy.details.loaded" class="fa fa-angle-right fa-angle-down"></span>
|
||||
</td>
|
||||
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy/{{policy.type}}/{{policy.id}}">{{policy.name}}</a></td>
|
||||
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/authz/resource-server/policy/{{policy.type.endsWith('.js') ? 'js': policy.type}}/{{policy.id}}">{{policy.name}}</a></td>
|
||||
<td>{{policy.description}}</td>
|
||||
<td>{{policy.type}}</td>
|
||||
<td align="center">
|
||||
|
|
|
@ -111,6 +111,16 @@
|
|||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-authz-policy-common</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.wildfly.core</groupId>
|
||||
|
|
|
@ -48,6 +48,9 @@ public class KeycloakProviderDeploymentProcessor implements DeploymentUnitProces
|
|||
}
|
||||
|
||||
KeycloakDeploymentInfo info = KeycloakProviderDependencyProcessor.getKeycloakProviderDeploymentInfo(deploymentUnit);
|
||||
|
||||
ScriptProviderDeploymentProcessor.deploy(deploymentUnit, info);
|
||||
|
||||
if (info.isProvider()) {
|
||||
logger.infov("Deploying Keycloak provider: {0}", deploymentUnit.getName());
|
||||
final Module module = deploymentUnit.getAttachment(Attachments.MODULE);
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* Copyright 2019 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.server.extension;
|
||||
|
||||
import static org.keycloak.representations.provider.ScriptProviderDescriptor.AUTHENTICATORS;
|
||||
import static org.keycloak.representations.provider.ScriptProviderDescriptor.MAPPERS;
|
||||
import static org.keycloak.representations.provider.ScriptProviderDescriptor.POLICIES;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.jboss.as.server.deployment.Attachments;
|
||||
import org.jboss.as.server.deployment.DeploymentUnit;
|
||||
import org.jboss.as.server.deployment.module.ResourceRoot;
|
||||
import org.jboss.vfs.VirtualFile;
|
||||
import org.keycloak.authentication.AuthenticatorSpi;
|
||||
import org.keycloak.authentication.authenticators.browser.DeployedScriptAuthenticatorFactory;
|
||||
import org.keycloak.authorization.policy.provider.PolicySpi;
|
||||
import org.keycloak.authorization.policy.provider.js.DeployedScriptPolicyFactory;
|
||||
import org.keycloak.common.util.StreamUtil;
|
||||
import org.keycloak.protocol.ProtocolMapperSpi;
|
||||
import org.keycloak.protocol.oidc.mappers.DeployedScriptOIDCProtocolMapper;
|
||||
import org.keycloak.provider.KeycloakDeploymentInfo;
|
||||
import org.keycloak.representations.provider.ScriptProviderDescriptor;
|
||||
import org.keycloak.representations.provider.ScriptProviderMetadata;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
final class ScriptProviderDeploymentProcessor {
|
||||
|
||||
private static final Map<String, BiConsumer<KeycloakDeploymentInfo, ScriptProviderMetadata>> PROVIDERS = new HashMap<>();
|
||||
|
||||
private static void registerScriptAuthenticator(KeycloakDeploymentInfo info, ScriptProviderMetadata metadata) {
|
||||
info.addProvider(AuthenticatorSpi.class, new DeployedScriptAuthenticatorFactory(metadata));
|
||||
}
|
||||
|
||||
private static void registerScriptPolicy(KeycloakDeploymentInfo info, ScriptProviderMetadata metadata) {
|
||||
info.addProvider(PolicySpi.class, new DeployedScriptPolicyFactory(metadata));
|
||||
}
|
||||
|
||||
private static void registerScriptMapper(KeycloakDeploymentInfo info, ScriptProviderMetadata metadata) {
|
||||
info.addProvider(ProtocolMapperSpi.class, new DeployedScriptOIDCProtocolMapper(metadata));
|
||||
}
|
||||
|
||||
static void deploy(DeploymentUnit deploymentUnit, KeycloakDeploymentInfo info) {
|
||||
ResourceRoot resourceRoot = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_ROOT);
|
||||
|
||||
if (resourceRoot == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
VirtualFile jarFile = resourceRoot.getRoot();
|
||||
|
||||
if (jarFile == null || !jarFile.exists() || !jarFile.getName().endsWith(".jar")) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScriptProviderDescriptor descriptor = readScriptProviderDescriptor(jarFile);
|
||||
|
||||
if (descriptor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, List<ScriptProviderMetadata>> entry : descriptor.getProviders().entrySet()) {
|
||||
for (ScriptProviderMetadata metadata : entry.getValue()) {
|
||||
String fileName = metadata.getFileName();
|
||||
|
||||
if (fileName == null) {
|
||||
throw new RuntimeException("You must provide the script file name");
|
||||
}
|
||||
|
||||
try (InputStream in = jarFile.getChild(fileName).openStream()) {
|
||||
metadata.setCode(StreamUtil.readString(in, StandardCharsets.UTF_8));
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to read script file [" + fileName + "]", cause);
|
||||
}
|
||||
|
||||
metadata.setId(new StringBuilder("script").append("-").append(fileName).toString());
|
||||
|
||||
String name = metadata.getName();
|
||||
|
||||
if (name == null) {
|
||||
name = fileName;
|
||||
}
|
||||
|
||||
metadata.setName(name);
|
||||
|
||||
PROVIDERS.get(entry.getKey()).accept(info, metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ScriptProviderDescriptor readScriptProviderDescriptor(VirtualFile deploymentRoot) {
|
||||
VirtualFile metadataFile = deploymentRoot.getChild("META-INF/keycloak-scripts.json");
|
||||
|
||||
if (!metadataFile.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try (InputStream inputStream = metadataFile.openStream()) {
|
||||
return JsonSerialization.readValue(inputStream, ScriptProviderDescriptor.class);
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to read providers metadata", cause);
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
PROVIDERS.put(AUTHENTICATORS, ScriptProviderDeploymentProcessor::registerScriptAuthenticator);
|
||||
PROVIDERS.put(POLICIES, ScriptProviderDeploymentProcessor::registerScriptPolicy);
|
||||
PROVIDERS.put(MAPPERS, ScriptProviderDeploymentProcessor::registerScriptMapper);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue