Skip to content

Commit

Permalink
Merge pull request #155 from jieryn/JENKINS-9915
Browse files Browse the repository at this point in the history
[FIXED JENKINS-9915] Make captcha support optional; remove LGPL jcaptcha
  • Loading branch information
olamy committed Jun 13, 2011
2 parents ef05e4c + 17a835d commit 8007d83
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 75 deletions.
3 changes: 3 additions & 0 deletions changelog.html
Expand Up @@ -55,6 +55,9 @@
<!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image>
<li class=rfe>
Make captcha support optional; remove LGPL jcaptcha
(<a href="http://issues.jenkins-ci.org/browse/JENKINS-9915">issue 9915</a>)
<li class=bug>
Validate new view name relative to current context
<li class=bug>
Expand Down
43 changes: 0 additions & 43 deletions core/pom.xml
Expand Up @@ -703,49 +703,6 @@ THE SOFTWARE.
<artifactId>memory-monitor</artifactId>
<version>1.5</version>
</dependency>
<dependency>
<groupId>com.octo.captcha</groupId>
<artifactId>jcaptcha-all</artifactId>
<version>1.0-RC6</version>
<exclusions>
<exclusion>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</exclusion>
<exclusion>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</exclusion>
<exclusion>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</exclusion>
<exclusion>
<groupId>quartz</groupId>
<artifactId>quartz</artifactId>
</exclusion>
<exclusion>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
</exclusion>
<exclusion>
<groupId>xerces</groupId>
<artifactId>xmlParserAPIs</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
<exclusion>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
</exclusion>
<exclusion>
<groupId>concurrent</groupId>
<artifactId>concurrent</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency><!-- StAX implementation. See HUDSON-2547. -->
<groupId>org.codehaus.woodstox</groupId>
<artifactId>wstx-asl</artifactId>
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/java/hudson/Functions.java
Expand Up @@ -34,6 +34,7 @@
import hudson.security.AuthorizationStrategy;
import hudson.security.Permission;
import hudson.security.SecurityRealm;
import hudson.security.captcha.CaptchaSupport;
import hudson.security.csrf.CrumbIssuer;
import hudson.slaves.Cloud;
import hudson.slaves.ComputerLauncher;
Expand Down Expand Up @@ -687,6 +688,10 @@ public static List<ParameterDescriptor> getParameterDescriptors() {
return ParameterDefinition.all();
}

public static List<Descriptor<CaptchaSupport>> getCaptchaSupportDescriptors() {
return CaptchaSupport.all();
}

public static List<Descriptor<ViewsTabBar>> getViewsTabBarDescriptors() {
return ViewsTabBar.all();
}
Expand Down
26 changes: 24 additions & 2 deletions core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java
Expand Up @@ -35,6 +35,7 @@
import hudson.model.UserProperty;
import hudson.model.UserPropertyDescriptor;
import hudson.security.FederatedLoginService.FederatedIdentity;
import hudson.security.captcha.CaptchaSupport;
import hudson.tasks.Mailer;
import hudson.util.PluginServletFilter;
import hudson.util.Protector;
Expand Down Expand Up @@ -92,9 +93,21 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
*/
private final boolean disableSignup;

@DataBoundConstructor
/**
* If true, captcha will be enabled.
*/
private final boolean enableCaptcha;

@Deprecated
public HudsonPrivateSecurityRealm(boolean allowsSignup) {
this(allowsSignup, false, (CaptchaSupport) null);
}

@DataBoundConstructor
public HudsonPrivateSecurityRealm(boolean allowsSignup, boolean enableCaptcha, CaptchaSupport captchaSupport) {
this.disableSignup = !allowsSignup;
this.enableCaptcha = enableCaptcha;
setCaptchaSupport(captchaSupport);
if(!allowsSignup && !hasSomeUser()) {
// if Hudson is newly set up with the security realm and there's no user account created yet,
// insert a filter that asks the user to create one
Expand All @@ -111,6 +124,15 @@ public boolean allowsSignup() {
return !disableSignup;
}

/**
* Checks if captcha is enabled on user signup.
*
* @return true if captcha is enabled on signup.
*/
public boolean isEnableCaptcha() {
return enableCaptcha;
}

/**
* Computes if this Hudson has some user accounts configured.
*
Expand Down Expand Up @@ -194,7 +216,7 @@ private User _doCreateAccount(StaplerRequest req, StaplerResponse rsp, String fo
throw HttpResponses.error(SC_UNAUTHORIZED,new Exception("User sign up is prohibited"));

boolean firstUser = !hasSomeUser();
User u = createAccount(req, rsp, true, formView);
User u = createAccount(req, rsp, enableCaptcha, formView);
if(u!=null) {
if(firstUser)
tryToMakeAdmin(u); // the first user should be admin, or else there's a risk of lock out
Expand Down
51 changes: 30 additions & 21 deletions core/src/main/java/hudson/security/SecurityRealm.java
Expand Up @@ -23,9 +23,6 @@
*/
package hudson.security;

import com.octo.captcha.service.CaptchaServiceException;
import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;
import com.octo.captcha.service.image.ImageCaptchaService;
import groovy.lang.Binding;
import hudson.ExtensionPoint;
import hudson.DescriptorExtensionList;
Expand All @@ -35,6 +32,7 @@
import hudson.model.Descriptor;
import jenkins.model.Jenkins;
import hudson.security.FederatedLoginService.FederatedIdentity;
import hudson.security.captcha.CaptchaSupport;
import hudson.util.DescriptorList;
import hudson.util.PluginServletFilter;
import hudson.util.spring.BeanBuilder;
Expand All @@ -57,13 +55,13 @@
import org.springframework.web.context.WebApplicationContext;
import org.springframework.dao.DataAccessException;

import javax.imageio.ImageIO;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import javax.servlet.http.Cookie;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand Down Expand Up @@ -127,6 +125,11 @@
* @see PluginServletFilter
*/
public abstract class SecurityRealm extends AbstractDescribableImpl<SecurityRealm> implements ExtensionPoint {
/**
* Captcha Support to be used with this SecurityRealm for User Signup
*/
private CaptchaSupport captchaSupport;

/**
* Creates fully-configured {@link AuthenticationManager} that performs authentication
* against the user realm. The implementation hides how such authentication manager
Expand Down Expand Up @@ -236,6 +239,18 @@ protected String getPostLogOutUrl(StaplerRequest req, Authentication auth) {
return req.getContextPath()+"/";
}

public CaptchaSupport getCaptchaSupport() {
return captchaSupport;
}

public void setCaptchaSupport(CaptchaSupport captchaSupport) {
this.captchaSupport = captchaSupport;
}

public List<Descriptor<CaptchaSupport>> getCaptchaSupportDescriptors() {
return CaptchaSupport.all();
}

/**
* Handles the logout processing.
*
Expand Down Expand Up @@ -324,35 +339,29 @@ public HttpResponse commenceSignup(FederatedIdentity identity) {
throw new UnsupportedOperationException();
}

/**
* {@link DefaultManageableImageCaptchaService} holder to defer initialization.
*/
public static final class CaptchaService {
public static ImageCaptchaService INSTANCE = new DefaultManageableImageCaptchaService();
}

/**
* Generates a captcha image.
*/
public final void doCaptcha(StaplerRequest req, StaplerResponse rsp) throws IOException {
String id = req.getSession().getId();
rsp.setContentType("image/png");
rsp.addHeader("Cache-Control","no-cache");
ImageIO.write( CaptchaService.INSTANCE.getImageChallengeForID(id), "PNG", rsp.getOutputStream() );
if (captchaSupport != null) {
String id = req.getSession().getId();
rsp.setContentType("image/png");
rsp.addHeader("Cache-Control", "no-cache");
captchaSupport.generateImage(id, rsp.getOutputStream());
}
}

/**
* Validates the captcha.
*/
protected final boolean validateCaptcha(String text) {
try {
if (captchaSupport != null) {
String id = Stapler.getCurrentRequest().getSession().getId();
Boolean b = CaptchaService.INSTANCE.validateResponseForID(id, text);
return b!=null && b;
} catch (CaptchaServiceException e) {
LOGGER.log(Level.INFO, "Captcha validation had a problem",e);
return false;
return captchaSupport.validateCaptcha(id, text);
}

// If no Captcha Support then bogus validation always returns true
return true;
}

/**
Expand Down
65 changes: 65 additions & 0 deletions core/src/main/java/hudson/security/captcha/CaptchaSupport.java
@@ -0,0 +1,65 @@
/*
* The MIT License
*
* Copyright (c) 2011, Winston.Prakash@oracle.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.security.captcha;


import hudson.DescriptorExtensionList;
import hudson.ExtensionPoint;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import hudson.model.Hudson;

import java.io.IOException;
import java.io.OutputStream;


/**
* Extension point for adding Captcha Support to User Registration Page {@link CaptchaSupport}.
*
* <p>
* This object can have an optional <tt>config.jelly</tt> to configure the Captcha Support
* <p>
* A default constructor is needed to create CaptchaSupport in
* the default configuration.
*
* @author Winston Prakash
* @since 1.416
* @see CaptchaSupportDescriptor
*/
public abstract class CaptchaSupport extends AbstractDescribableImpl<CaptchaSupport> implements ExtensionPoint {
/**
* Returns all the registered {@link CaptchaSupport} descriptors.
*/
public static DescriptorExtensionList<CaptchaSupport, Descriptor<CaptchaSupport>> all() {
return Hudson.getInstance().<CaptchaSupport, Descriptor<CaptchaSupport>>getDescriptorList(CaptchaSupport.class);
}

abstract public boolean validateCaptcha(String id, String text);

abstract public void generateImage(String id, OutputStream ios) throws IOException;

public CaptchaSupportDescriptor getDescriptor() {
return (CaptchaSupportDescriptor)super.getDescriptor();
}
}
@@ -0,0 +1,37 @@
/*
* The MIT License
*
* Copyright (c) 2011, Winston.Prakash@oracle.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.security.captcha;

import hudson.model.Descriptor;

/**
* {@link Descriptor} for {@link CaptchaSupport}.
*
* @author Winston Prakash
* @since 1.416
*/
public abstract class CaptchaSupportDescriptor extends Descriptor<CaptchaSupport> {
// so far nothing different from plain Descriptor
// but it may prove useful for future expansion
}
Expand Up @@ -23,9 +23,25 @@ THE SOFTWARE.
-->

<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:entry title="" help="/help/security/private-realm/allow-signup.html">
<f:checkbox name="privateRealm.allowsSignup" checked="${h.defaultToTrue(instance.allowsSignup())}"
title="${%Allow users to sign up}"/>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson"
xmlns:f="/lib/form">
<f:entry title="" help="/help/security/private-realm/allow-signup.html">
<f:checkbox name="privateRealm.allowsSignup" checked="${h.defaultToTrue(instance.allowsSignup())}" title="${%Allow users to sign up}" />
</f:entry>
</j:jelly>
<j:if test="${size(h.captchaSupportDescriptors) gt 0}">
<f:entry>
<f:checkbox name="privateRealm.enableCaptcha" checked="${h.defaultToTrue(instance.isEnableCaptcha())}"
title="${%Enable captcha on sign up}" />
</f:entry>
<f:dropdownList name="privateRealm.captchaSupport" title="${%Captcha Support}">
<!-- Loop through available Captcha Support Descriptors -->
<j:forEach var="descriptor" items="${h.captchaSupportDescriptors}" varStatus="loop">
<f:dropdownListBlock title="${descriptor.displayName}" value="${loop.index}"
selected="${descriptor==instance.captchaSupport.descriptor}" staplerClass="${descriptor.clazz.name}">
<!-- Include config.jelly for this Captcha Support -->
<st:include page="${descriptor.configPage}" from="${descriptor}" optional="true" />
</f:dropdownListBlock>
</j:forEach>
</f:dropdownList>
</j:if>
</j:jelly>
Expand Up @@ -27,5 +27,5 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<local:_entryForm host="${app}" title="${%Sign up}" action="createAccount" captcha="${true}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</j:jelly>
<local:_entryForm host="${app}" title="${%Sign up}" action="createAccount" captcha="${it.isEnableCaptcha()}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</j:jelly>

0 comments on commit 8007d83

Please sign in to comment.