@@ -0,0 +1,2763 @@
+package net.kencochrane.raven.sentrystub;%0A%0Aimport net.kencochrane.raven.sentrystub.auth.AuthValidator;%0Aimport net.kencochrane.raven.sentrystub.auth.InvalidAuthException;%0A%0Aimport javax.servlet.*;%0Aimport javax.servlet.annotation.WebFilter;%0Aimport javax.servlet.http.HttpServletRequest;%0Aimport javax.servlet.http.HttpServletResponse;%0Aimport java.io.IOException;%0Aimport java.io.PrintWriter;%0Aimport java.util.Collections;%0Aimport java.util.HashMap;%0Aimport java.util.Map;%0A%0A@WebFilter(servletNames = %22SentryHttpServlet%22)%0Apublic class SentryAuthenticationFilter implements Filter %7B%0A    private static final String SENTRY_AUTH = %22X-Sentry-Auth%22;%0A    private AuthValidator authValidator;%0A%0A    @Override%0A    public void init(FilterConfig filterConfig) throws ServletException %7B%0A        authValidator = new AuthValidator();%0A        //TODO: Load filter config to add users!%0A    %7D%0A%0A    @Override%0A    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException %7B%0A        HttpServletRequest req = (HttpServletRequest) request;%0A        HttpServletResponse resp = (HttpServletResponse) response;%0A%0A        //TODO: respond to everything that isn't %22https://example.com/sentry/api/project-id/store/%22%0A        String pathInfo = req.getPathInfo();%0A%0A        if (!validateAuth(req, resp)) return;%0A%0A        chain.doFilter(request, response);%0A    %7D%0A%0A    private boolean validateAuth(HttpServletRequest req, HttpServletResponse resp) throws IOException %7B%0A        Map%3CString, String%3E sentryAuthDetails = extractSentryAuthDetails(req);%0A        String projectId = req.getPathInfo().substring(1, req.getPathInfo().indexOf('/', 1));%0A%0A        try %7B%0A            authValidator.validateSentryAuth(sentryAuthDetails, projectId);%0A        %7D catch (InvalidAuthException iae) %7B%0A            resp.setStatus(HttpServletResponse.SC_FORBIDDEN);%0A            PrintWriter writer = resp.getWriter();%0A            for (String message : iae.getDetailedMessages()) %7B%0A                writer.println(message);%0A            %7D%0A            return false;%0A        %7D%0A        return true;%0A    %7D%0A%0A    private Map%3CString, String%3E extractSentryAuthDetails(HttpServletRequest request) %7B%0A        String sentryAuth = request.getHeader(SENTRY_AUTH);%0A        if (sentryAuth == null) %7B%0A            return Collections.emptyMap();%0A        %7D%0A%0A        String%5B%5D authParameters = sentryAuth.split(%22,%22);%0A        Map%3CString, String%3E authDetails = new HashMap%3CString, String%3E(authParameters.length);%0A        for (String authParameter : authParameters) %7B%0A            String%5B%5D splitParameter = authParameter.split(%22=%22);%0A            authDetails.put(splitParameter%5B0%5D, splitParameter%5B1%5D);%0A        %7D%0A%0A        return authDetails;%0A    %7D%0A%0A    @Override%0A    public void destroy() %7B%0A    %7D%0A%7D%0A

Add a filter checking the Authorisation to access the API