CrumbIssuer.java 5.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
/**
 * Copyright (c) 2008-2009 Yahoo! Inc.
 * All rights reserved.
 * The copyrights to the contents of this file are licensed under the MIT License (http://www.opensource.org/licenses/mit-license.php)
 */
package hudson.security.csrf;

import javax.servlet.ServletRequest;

import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;

import hudson.DescriptorExtensionList;
import hudson.ExtensionPoint;
import hudson.model.Api;
import hudson.model.Describable;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import hudson.util.MultipartFormDataParser;

/**
 * A CrumbIssuer represents an algorithm to generate a nonce value, known as a
 * crumb, to counter cross site request forgery exploits. Crumbs are typically
 * hashes incorporating information that uniquely identifies an agent that sends
 * a request, along with a guarded secret so that the crumb value cannot be
 * forged by a third party.
 *
 * @author dty
 * @see http://en.wikipedia.org/wiki/XSRF
 */
@ExportedBean
public abstract class CrumbIssuer implements Describable<CrumbIssuer>, ExtensionPoint {

    private static final String CRUMB_ATTRIBUTE = CrumbIssuer.class.getName() + "_crumb";

    /**
     * Get the name of the request parameter the crumb will be stored in. Exposed
     * here for the remote API.
     */
    @Exported
    public String getCrumbRequestField() {
        return getDescriptor().getCrumbRequestField();
    }

    /**
     * Get a crumb value based on user specific information in the current request.
     * Intended for use only by the remote API.
     * @return
     */
    @Exported
    public String getCrumb() {
        return getCrumb(Stapler.getCurrentRequest());
    }

    /**
     * Get a crumb value based on user specific information in the request.
     * @param request
     * @return
     */
    public String getCrumb(ServletRequest request) {
        String crumb = null;
        if (request != null) {
            crumb = (String) request.getAttribute(CRUMB_ATTRIBUTE);
        }
        if (crumb == null) {
            crumb = issueCrumb(request, getDescriptor().getCrumbSalt());
            if (request != null) {
                if ((crumb != null) && !crumb.isEmpty()) {
                    request.setAttribute(CRUMB_ATTRIBUTE, crumb);
                } else {
                    request.removeAttribute(CRUMB_ATTRIBUTE);
                }
            }
        }

        return crumb;
    }

    /**
     * Create a crumb value based on user specific information in the request.
     * The crumb should be generated by building a cryptographic hash of:
     * <ul>
     *  <li>relevant information in the request that can uniquely identify the client
     *  <li>the salt value
     *  <li>an implementation specific guarded secret.
     * </ul>
     *
     * @param request
     * @param salt
     * @return
     */
    protected abstract String issueCrumb(ServletRequest request, String salt);

    /**
     * Get a crumb from a request parameter and validate it against other data
     * in the current request. The salt and request parameter that is used is
     * defined by the current configuration.
     *
     * @param request
     * @return
     */
    public boolean validateCrumb(ServletRequest request) {
        CrumbIssuerDescriptor<CrumbIssuer> desc = getDescriptor();
        String crumbField = desc.getCrumbRequestField();
        String crumbSalt = desc.getCrumbSalt();

        return validateCrumb(request, crumbSalt, request.getParameter(crumbField));
    }

    /**
     * Get a crumb from multipart form data and validate it against other data
     * in the current request. The salt and request parameter that is used is
     * defined by the current configuration.
     *
     * @param request
     * @param parser
     * @return
     */
    public boolean validateCrumb(ServletRequest request, MultipartFormDataParser parser) {
        CrumbIssuerDescriptor<CrumbIssuer> desc = getDescriptor();
        String crumbField = desc.getCrumbRequestField();
        String crumbSalt = desc.getCrumbSalt();

        return validateCrumb(request, crumbSalt, parser.get(crumbField));
    }

    /**
     * Validate a previously created crumb against information in the current request.
     *
     * @param request
     * @param salt
     * @param crumb The previously generated crumb to validate against information in the current request
     * @return
     */
    public abstract boolean validateCrumb(ServletRequest request, String salt, String crumb);

    /**
     * Access global configuration for the crumb issuer.
     */
    public CrumbIssuerDescriptor<CrumbIssuer> getDescriptor() {
        return (CrumbIssuerDescriptor<CrumbIssuer>) Hudson.getInstance().getDescriptor(getClass());
    }

    /**
     * Returns all the registered {@link CrumbIssuer} descriptors.
     */
    public static DescriptorExtensionList<CrumbIssuer, Descriptor<CrumbIssuer>> all() {
        return Hudson.getInstance().getDescriptorList(CrumbIssuer.class);
    }

    public Api getApi() {
        return new Api(this);
    }
}