diff --git a/pom.xml b/pom.xml index 83c59d5..ae40ed2 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ THE SOFTWARE. cas-plugin - 1.3.1-SNAPSHOT + 1.4.0-SNAPSHOT hpi CAS Plugin http://wiki.jenkins-ci.org/display/JENKINS/CAS+Plugin @@ -140,6 +140,11 @@ THE SOFTWARE. mailer 1.16 + + org.jenkins-ci.plugins + script-security + 1.27 + org.jasig.cas.client cas-client-core diff --git a/src/main/java/org/jenkinsci/plugins/cas/protocols/Cas10Protocol.java b/src/main/java/org/jenkinsci/plugins/cas/protocols/Cas10Protocol.java index ca0beb6..be151c2 100644 --- a/src/main/java/org/jenkinsci/plugins/cas/protocols/Cas10Protocol.java +++ b/src/main/java/org/jenkinsci/plugins/cas/protocols/Cas10Protocol.java @@ -1,12 +1,5 @@ package org.jenkinsci.plugins.cas.protocols; -import groovy.lang.GroovyShell; -import groovy.lang.Script; -import hudson.Extension; -import hudson.Util; -import hudson.model.Descriptor; -import hudson.util.FormValidation; - import java.util.Collection; import org.codehaus.groovy.control.CompilationFailedException; @@ -14,9 +7,18 @@ import org.jenkinsci.plugins.cas.CasProtocol; import org.jenkinsci.plugins.cas.Messages; import org.jenkinsci.plugins.cas.validation.Cas10RoleParsingTicketValidator; +import org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException; +import org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript; +import org.jenkinsci.plugins.scriptsecurity.scripts.UnapprovedClasspathException; +import org.jenkinsci.plugins.scriptsecurity.scripts.UnapprovedUsageException; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; +import hudson.Extension; +import hudson.Util; +import hudson.model.Descriptor; +import hudson.util.FormValidation; + /** * CAS 1.0 protocol support. * @@ -27,11 +29,21 @@ public class Cas10Protocol extends CasProtocol { public final String rolesValidationScript; public final String testValidationResponse; + public final boolean sandbox; - @DataBoundConstructor + private final SecureGroovyScript secureRolesValidationScript; + + @Deprecated public Cas10Protocol(String rolesValidationScript, String testValidationResponse) { + this(rolesValidationScript, testValidationResponse, false); + } + + @DataBoundConstructor + public Cas10Protocol(String rolesValidationScript, String testValidationResponse, boolean sandbox) { this.rolesValidationScript = Util.fixEmptyAndTrim(rolesValidationScript); this.testValidationResponse = Util.fixEmpty(testValidationResponse); + this.sandbox = sandbox; + this.secureRolesValidationScript = getSecureGroovyScript(this.rolesValidationScript, this.sandbox); } @Override @@ -42,10 +54,14 @@ public String getAuthoritiesAttribute() { @Override public TicketValidator createTicketValidator(String casServerUrl) { Cas10RoleParsingTicketValidator ticketValidator = new Cas10RoleParsingTicketValidator(casServerUrl); - ticketValidator.setRolesValidationScript(rolesValidationScript); + ticketValidator.setRolesValidationScript(secureRolesValidationScript); return ticketValidator; } + private static SecureGroovyScript getSecureGroovyScript(String script, boolean sandbox) { + return new SecureGroovyScript(script, sandbox, null).configuringWithKeyItem(); + } + @Extension public static final class DescriptorImpl extends Descriptor { @Override @@ -56,10 +72,10 @@ public String getDisplayName() { @SuppressWarnings("rawtypes") public FormValidation doTestScript( @QueryParameter("rolesValidationScript") final String rolesValidationScript, - @QueryParameter("testValidationResponse") final String testValidationResponse) { + @QueryParameter("testValidationResponse") final String testValidationResponse, + @QueryParameter("sandbox") final boolean sandbox) { try { - Script script = new GroovyShell().parse(rolesValidationScript); - Collection roles = Cas10RoleParsingTicketValidator.parseRolesFromValidationResponse(script, testValidationResponse); + Collection roles = Cas10RoleParsingTicketValidator.parseRolesFromValidationResponse(getSecureGroovyScript(rolesValidationScript, sandbox), testValidationResponse); if (roles == null) { return FormValidation.error(Messages.Cas10Protocol_rolesValidationScript_noResult()); } @@ -68,6 +84,14 @@ public FormValidation doTestScript( return FormValidation.error(Messages.Cas10Protocol_rolesValidationScript_compilationError() + ": " + e); } catch (ClassCastException e) { return FormValidation.error(Messages.Cas10Protocol_rolesValidationScript_returnTypeError() + ": " + e); + } catch (RejectedAccessException e) { + return FormValidation.error(Messages.Cas10Protocol_rolesValidationScript_rejectedAccessError() + ": " + e); + } catch (UnapprovedUsageException e) { + return FormValidation.error(Messages.Cas10Protocol_rolesValidationScript_unapprovedUsageError() + ": " + e); + } catch (UnapprovedClasspathException e) { + return FormValidation.error(Messages.Cas10Protocol_rolesValidationScript_unapprovedClasspathError() + ": " + e); + } catch (Exception e) { + return FormValidation.error(Messages.Cas10Protocol_rolesValidationScript_unknownError() + ": " + e); } } } diff --git a/src/main/java/org/jenkinsci/plugins/cas/validation/Cas10RoleParsingTicketValidator.java b/src/main/java/org/jenkinsci/plugins/cas/validation/Cas10RoleParsingTicketValidator.java index 34035ab..cc74dab 100644 --- a/src/main/java/org/jenkinsci/plugins/cas/validation/Cas10RoleParsingTicketValidator.java +++ b/src/main/java/org/jenkinsci/plugins/cas/validation/Cas10RoleParsingTicketValidator.java @@ -1,10 +1,6 @@ package org.jenkinsci.plugins.cas.validation; -import groovy.lang.GroovyShell; -import groovy.lang.Script; - import java.io.BufferedReader; -import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Collection; @@ -12,13 +8,16 @@ import java.util.List; import java.util.Map; -import org.apache.commons.lang.StringUtils; import org.jasig.cas.client.authentication.AttributePrincipal; import org.jasig.cas.client.authentication.AttributePrincipalImpl; import org.jasig.cas.client.validation.AbstractCasProtocolUrlBasedTicketValidator; import org.jasig.cas.client.validation.Assertion; import org.jasig.cas.client.validation.AssertionImpl; import org.jasig.cas.client.validation.TicketValidationException; +import org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript; + +import groovy.lang.Binding; +import jenkins.model.Jenkins; /** * Implementation of a Ticket Validator that can validate tickets conforming to the CAS 1.0 specification. @@ -31,9 +30,8 @@ public class Cas10RoleParsingTicketValidator extends AbstractCasProtocolUrlBased public static final String DEFAULT_ROLE_ATTRIBUTE = "roles"; - private String rolesValidationScript; + private SecureGroovyScript rolesValidationScript; private String rolesAttribute = DEFAULT_ROLE_ATTRIBUTE; - private Script parsedScript; public Cas10RoleParsingTicketValidator(final String casServerUrlPrefix) { super(casServerUrlPrefix); @@ -61,7 +59,7 @@ protected Assertion parseResponseFromServer(final String response) throws Ticket reader.readLine(); final String name = reader.readLine(); - List roles = parseRolesFromValidationResponse(getParsedScript(), response); + List roles = parseRolesFromValidationResponse(rolesValidationScript, response); if (roles != null) { Map attributes = new HashMap(1); attributes.put(rolesAttribute, roles); @@ -71,7 +69,7 @@ protected Assertion parseResponseFromServer(final String response) throws Ticket return new AssertionImpl(name); } - } catch (final IOException e) { + } catch (final Exception e) { throw new TicketValidationException("Unable to parse response.", e); } } @@ -83,13 +81,14 @@ protected Assertion parseResponseFromServer(final String response) throws Ticket * @return list of roles */ @SuppressWarnings("rawtypes") - public static List parseRolesFromValidationResponse(Script script, String response) { + public static List parseRolesFromValidationResponse(SecureGroovyScript script, String response) throws Exception { if (script == null) return null; // Run the script to parse the response - script.getBinding().setVariable("response", response); - Collection coll = (Collection) script.run(); + Binding binding = new Binding(); + binding.setVariable("response", response); + Collection coll = (Collection) script.evaluate(Jenkins.getInstance().getPluginManager().uberClassLoader, binding); if (coll == null || coll.isEmpty()) return null; @@ -104,30 +103,18 @@ public static List parseRolesFromValidationResponse(Script script, Strin return roles; } - /** - * Get the parsed Groovy roles validation script. - * @return parsed Groovy script - */ - protected synchronized Script getParsedScript() { - if (parsedScript == null && StringUtils.isNotEmpty(rolesValidationScript)) { - parsedScript = new GroovyShell().parse(rolesValidationScript); - } - return parsedScript; - } - /** * @return the rolesValidationScript */ - public String getRolesValidationScript() { + public SecureGroovyScript getRolesValidationScript() { return rolesValidationScript; } /** * @param rolesValidationScript the rolesValidationScript to set */ - public void setRolesValidationScript(String rolesValidationScript) { + public void setRolesValidationScript(SecureGroovyScript rolesValidationScript) { this.rolesValidationScript = rolesValidationScript; - this.parsedScript = null; } /** diff --git a/src/main/resources/org/jenkinsci/plugins/cas/Messages.properties b/src/main/resources/org/jenkinsci/plugins/cas/Messages.properties index d3b3492..3ea59ce 100644 --- a/src/main/resources/org/jenkinsci/plugins/cas/Messages.properties +++ b/src/main/resources/org/jenkinsci/plugins/cas/Messages.properties @@ -7,3 +7,7 @@ Cas10Protocol.rolesValidationScript.result=Roles parsed from the test validation Cas10Protocol.rolesValidationScript.noResult=Roles Validation Script returned no result Cas10Protocol.rolesValidationScript.compilationError=Roles Validation Script failed to compile Cas10Protocol.rolesValidationScript.returnTypeError=Roles Validation Script did not return a Collection +Cas10Protocol.rolesValidationScript.rejectedAccessError=Roles Validation Script uses forbidden language elements +Cas10Protocol.rolesValidationScript.unapprovedUsageError=Roles Validation Script is not approved for execution +Cas10Protocol.rolesValidationScript.unapprovedClasspathError=Roles Validation Script classpath is not approved +Cas10Protocol.rolesValidationScript.unknownError=Roles Validation Script could not be tested due to an unknown error diff --git a/src/main/resources/org/jenkinsci/plugins/cas/Messages_fr.properties b/src/main/resources/org/jenkinsci/plugins/cas/Messages_fr.properties index f851e8e..2eb93f3 100644 --- a/src/main/resources/org/jenkinsci/plugins/cas/Messages_fr.properties +++ b/src/main/resources/org/jenkinsci/plugins/cas/Messages_fr.properties @@ -7,3 +7,7 @@ Cas10Protocol.rolesValidationScript.result=R Cas10Protocol.rolesValidationScript.noResult=Le script de validation des rôles n''a retourné aucun résultat Cas10Protocol.rolesValidationScript.compilationError=Le script de validation des rôles n''a pas pu être compilé Cas10Protocol.rolesValidationScript.returnTypeError=Le script de validation des rôles n''a pas retourné de Collection +Cas10Protocol.rolesValidationScript.rejectedAccessError=Le script de validation des rôles utilise des éléments interdits du langage +Cas10Protocol.rolesValidationScript.unapprovedUsageError=Le script de validation des rôles n''est pas approuvé +Cas10Protocol.rolesValidationScript.unapprovedClasspathError=Le script de validation des rôles utilise un classpath non approuvé +Cas10Protocol.rolesValidationScript.unknownError=Le script de validation des rôles n''a pas pu être testé en raison d''une erreur inconnue diff --git a/src/main/resources/org/jenkinsci/plugins/cas/protocols/Cas10Protocol/config.jelly b/src/main/resources/org/jenkinsci/plugins/cas/protocols/Cas10Protocol/config.jelly index c565a8d..46955f6 100644 --- a/src/main/resources/org/jenkinsci/plugins/cas/protocols/Cas10Protocol/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/cas/protocols/Cas10Protocol/config.jelly @@ -9,9 +9,12 @@ + + + - + diff --git a/src/main/resources/org/jenkinsci/plugins/cas/protocols/Cas10Protocol/config.properties b/src/main/resources/org/jenkinsci/plugins/cas/protocols/Cas10Protocol/config.properties index 41de01f..882967f 100644 --- a/src/main/resources/org/jenkinsci/plugins/cas/protocols/Cas10Protocol/config.properties +++ b/src/main/resources/org/jenkinsci/plugins/cas/protocols/Cas10Protocol/config.properties @@ -1,4 +1,5 @@ description=CAS 1.0 is a text-based protocol. Custom extensions may provide support for roles, which can be parsed with a Groovy script. rolesValidationScript=Roles Validation Script testValidationResponse=Test Validation Response +sandbox=Use Groovy sandbox testScript=Test Script diff --git a/src/main/resources/org/jenkinsci/plugins/cas/protocols/Cas10Protocol/config_fr.properties b/src/main/resources/org/jenkinsci/plugins/cas/protocols/Cas10Protocol/config_fr.properties index db7e9fd..7a1d4ae 100644 --- a/src/main/resources/org/jenkinsci/plugins/cas/protocols/Cas10Protocol/config_fr.properties +++ b/src/main/resources/org/jenkinsci/plugins/cas/protocols/Cas10Protocol/config_fr.properties @@ -1,4 +1,5 @@ description=CAS 1.0 est un protocole basé sur du texte. Des extensions spécifiques peuvent apporter le support de rôles, qui peuvent être extraits à l''aide d''un script Groovy. rolesValidationScript=Script de validation des rôles testValidationResponse=Réponse de validation de test +sandbox=Utiliser la sandbox Groovy testScript=Tester le script