001package org.kohsuke.stapler;
002
003import javax.servlet.http.HttpSession;
004import java.util.UUID;
005
006/**
007 * Generates a nonce value that allows us to protect against cross-site request forgery (CSRF) attacks.
008 *
009 * <p>
010 * We send this with each JavaScript proxy and verify them when we receive a request.
011 *
012 * @author Kohsuke Kawaguchi
013 * @see WebApp#getCrumbIssuer()
014 * @see WebApp#setCrumbIssuer(CrumbIssuer)  
015 */
016public abstract class CrumbIssuer {
017    /**
018     * Issues a crumb for the given request.
019     */
020    public abstract String issueCrumb(StaplerRequest request);
021
022    public final String issueCrumb() {
023        return issueCrumb(Stapler.getCurrentRequest());
024    }
025
026    /**
027     * Sends the crumb value in plain text, enabling retrieval through XmlHttpRequest.
028     */
029    public HttpResponse doCrumb() {
030        return HttpResponses.text(issueCrumb());
031    }
032
033    /**
034     * Validates a crumb that was submitted along with the request.
035     *
036     * @param request
037     *      The request that submitted the crumb
038     * @param submittedCrumb
039     *      The submitted crumb value to be validated.
040     *
041     * @throws SecurityException
042     *      If the crumb doesn't match and the request processing should abort.
043     */
044    public void validateCrumb(StaplerRequest request, String submittedCrumb) {
045        if (!issueCrumb(request).equals(submittedCrumb)) {
046            throw new SecurityException("Request failed to pass the crumb test (try clearing your cookies)");
047        }
048    }
049
050    /**
051     * Default crumb issuer.
052     */
053    public static final CrumbIssuer DEFAULT = new CrumbIssuer() {
054        @Override
055        public String issueCrumb(StaplerRequest request) {
056            HttpSession s = request.getSession();
057            String v = (String)s.getAttribute(ATTRIBUTE_NAME);
058            if (v!=null)    return v;
059            v = UUID.randomUUID().toString();
060            s.setAttribute(ATTRIBUTE_NAME,v);
061            return v;
062        }
063    };
064    private static final String ATTRIBUTE_NAME = CrumbIssuer.class.getName();
065}