| // Copyright (C) 2010 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.google.gerrit.httpd; |
| |
| import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.cache.Cache; |
| import com.google.common.collect.ImmutableListMultimap; |
| import com.google.common.collect.ListMultimap; |
| import com.google.common.collect.Lists; |
| import com.google.common.flogger.FluentLogger; |
| import com.google.gerrit.common.data.Capable; |
| import com.google.gerrit.entities.Project; |
| import com.google.gerrit.extensions.registration.DynamicSet; |
| import com.google.gerrit.extensions.restapi.AuthException; |
| import com.google.gerrit.server.AccessPath; |
| import com.google.gerrit.server.AnonymousUser; |
| import com.google.gerrit.server.CurrentUser; |
| import com.google.gerrit.server.RequestInfo; |
| import com.google.gerrit.server.RequestListener; |
| import com.google.gerrit.server.audit.HttpAuditEvent; |
| import com.google.gerrit.server.cache.CacheModule; |
| import com.google.gerrit.server.git.GitRepositoryManager; |
| import com.google.gerrit.server.git.PermissionAwareRepositoryManager; |
| import com.google.gerrit.server.git.TracingHook; |
| import com.google.gerrit.server.git.TransferConfig; |
| import com.google.gerrit.server.git.UploadPackInitializer; |
| import com.google.gerrit.server.git.UsersSelfAdvertiseRefsHook; |
| import com.google.gerrit.server.git.receive.AsyncReceiveCommits; |
| import com.google.gerrit.server.git.validators.UploadValidators; |
| import com.google.gerrit.server.group.GroupAuditService; |
| import com.google.gerrit.server.logging.TraceContext; |
| import com.google.gerrit.server.permissions.PermissionBackend; |
| import com.google.gerrit.server.permissions.PermissionBackendException; |
| import com.google.gerrit.server.permissions.ProjectPermission; |
| import com.google.gerrit.server.plugincontext.PluginSetContext; |
| import com.google.gerrit.server.project.ProjectCache; |
| import com.google.gerrit.server.project.ProjectState; |
| import com.google.gerrit.server.util.time.TimeUtil; |
| import com.google.inject.AbstractModule; |
| import com.google.inject.Inject; |
| import com.google.inject.Provider; |
| import com.google.inject.Singleton; |
| import com.google.inject.TypeLiteral; |
| import com.google.inject.name.Named; |
| import java.io.IOException; |
| import java.text.MessageFormat; |
| import java.time.Duration; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicLong; |
| import javax.servlet.Filter; |
| import javax.servlet.FilterChain; |
| import javax.servlet.FilterConfig; |
| import javax.servlet.ServletException; |
| import javax.servlet.ServletRequest; |
| import javax.servlet.ServletResponse; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import javax.servlet.http.HttpServletResponseWrapper; |
| import org.eclipse.jgit.errors.PackProtocolException; |
| import org.eclipse.jgit.errors.RepositoryNotFoundException; |
| import org.eclipse.jgit.http.server.GitServlet; |
| import org.eclipse.jgit.http.server.GitSmartHttpTools; |
| import org.eclipse.jgit.http.server.HttpServerText; |
| import org.eclipse.jgit.http.server.ServletUtils; |
| import org.eclipse.jgit.http.server.UploadPackErrorHandler; |
| import org.eclipse.jgit.http.server.resolver.AsIsFileService; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.transport.PostUploadHook; |
| import org.eclipse.jgit.transport.PostUploadHookChain; |
| import org.eclipse.jgit.transport.PreUploadHook; |
| import org.eclipse.jgit.transport.PreUploadHookChain; |
| import org.eclipse.jgit.transport.ReceivePack; |
| import org.eclipse.jgit.transport.ServiceMayNotContinueException; |
| import org.eclipse.jgit.transport.UploadPack; |
| import org.eclipse.jgit.transport.resolver.ReceivePackFactory; |
| import org.eclipse.jgit.transport.resolver.RepositoryResolver; |
| import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; |
| import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; |
| import org.eclipse.jgit.transport.resolver.UploadPackFactory; |
| |
| /** Serves Git repositories over HTTP. */ |
| @Singleton |
| public class GitOverHttpServlet extends GitServlet { |
| private static final long serialVersionUID = 1L; |
| |
| private static final String ATT_STATE = ProjectState.class.getName(); |
| private static final String ATT_ARC = AsyncReceiveCommits.class.getName(); |
| private static final String ID_CACHE = "adv_bases"; |
| |
| public static final String URL_REGEX; |
| public static final String GIT_COMMAND_STATUS_HEADER = "X-git-command-status"; |
| |
| private enum GIT_COMMAND_STATUS { |
| OK(0), |
| FAIL(-1); |
| |
| private final int exitStatus; |
| |
| GIT_COMMAND_STATUS(int exitStatus) { |
| this.exitStatus = exitStatus; |
| } |
| |
| @Override |
| public String toString() { |
| return Integer.toString(exitStatus); |
| } |
| } |
| |
| static { |
| StringBuilder url = new StringBuilder(); |
| url.append("^(?:/a)?(?:/p/|/)(.*/(?:info/refs"); |
| for (String name : GitSmartHttpTools.VALID_SERVICES) { |
| url.append('|').append(name); |
| } |
| url.append("))$"); |
| URL_REGEX = url.toString(); |
| } |
| |
| static class GitOverHttpServletModule extends AbstractModule { |
| |
| private final boolean enableReceive; |
| |
| GitOverHttpServletModule(boolean enableReceive) { |
| this.enableReceive = enableReceive; |
| } |
| |
| @Override |
| protected void configure() { |
| bind(Resolver.class); |
| bind(UploadFactory.class); |
| bind(UploadFilter.class); |
| bind(new TypeLiteral<ReceivePackFactory<HttpServletRequest>>() {}) |
| .to(enableReceive ? ReceiveFactory.class : DisabledReceiveFactory.class); |
| bind(ReceiveFilter.class); |
| install( |
| new CacheModule() { |
| @Override |
| protected void configure() { |
| cache(ID_CACHE, AdvertisedObjectsCacheKey.class, new TypeLiteral<Set<ObjectId>>() {}) |
| .maximumWeight(4096) |
| .expireAfterWrite(Duration.ofMinutes(10)); |
| } |
| }); |
| |
| // Don't bind Metrics, which is bound in a parent injector in tests. |
| } |
| } |
| |
| @VisibleForTesting |
| @Singleton |
| public static class Metrics { |
| // Recording requests separately in this class is only necessary because of a bug in the |
| // implementation of the generic RequestMetricsFilter; see |
| // https://gerrit-review.googlesource.com/c/gerrit/+/211692 |
| private final AtomicLong requestsStarted = new AtomicLong(); |
| |
| void requestStarted() { |
| requestsStarted.incrementAndGet(); |
| } |
| |
| public long getRequestsStarted() { |
| return requestsStarted.get(); |
| } |
| } |
| |
| static class HttpServletResponseWithStatusWrapper extends HttpServletResponseWrapper { |
| private int responseStatus; |
| |
| HttpServletResponseWithStatusWrapper(HttpServletResponse response) { |
| super(response); |
| /* Even if we could read the status from response, we assume that it is all |
| * fine because we entered the filter without any prior issues. |
| * When Google will have upgraded to Servlet 3.0, we could actually |
| * call response.getStatus() and the code will be clearer. |
| */ |
| responseStatus = HttpServletResponse.SC_OK; |
| } |
| |
| @Override |
| public void setStatus(int sc) { |
| responseStatus = sc; |
| super.setStatus(sc); |
| } |
| |
| @SuppressWarnings("deprecation") |
| @Override |
| public void setStatus(int sc, String sm) { |
| responseStatus = sc; |
| super.setStatus(sc, sm); |
| } |
| |
| @Override |
| public void sendError(int sc) throws IOException { |
| this.responseStatus = sc; |
| super.sendError(sc); |
| } |
| |
| @Override |
| public void sendError(int sc, String msg) throws IOException { |
| this.responseStatus = sc; |
| super.sendError(sc, msg); |
| } |
| |
| @Override |
| public void sendRedirect(String location) throws IOException { |
| this.responseStatus = HttpServletResponse.SC_MOVED_TEMPORARILY; |
| super.sendRedirect(location); |
| } |
| |
| public int getResponseStatus() { |
| return responseStatus; |
| } |
| } |
| |
| @Inject |
| GitOverHttpServlet( |
| Resolver resolver, |
| UploadFactory upload, |
| UploadFilter uploadFilter, |
| GerritUploadPackErrorHandler uploadPackErrorHandler, |
| ReceivePackFactory<HttpServletRequest> receive, |
| ReceiveFilter receiveFilter) { |
| setRepositoryResolver(resolver); |
| setAsIsFileService(AsIsFileService.DISABLED); |
| |
| setUploadPackFactory(upload); |
| setUploadPackErrorHandler(uploadPackErrorHandler); |
| addUploadPackFilter(uploadFilter); |
| |
| setReceivePackFactory(receive); |
| addReceivePackFilter(receiveFilter); |
| } |
| |
| private static String extractWhat(HttpServletRequest request) { |
| StringBuilder commandName = new StringBuilder(request.getRequestURL()); |
| if (request.getQueryString() != null) { |
| commandName.append("?").append(request.getQueryString()); |
| } |
| return commandName.toString(); |
| } |
| |
| private static ListMultimap<String, String> extractParameters(HttpServletRequest request) { |
| if (request.getQueryString() == null) { |
| return ImmutableListMultimap.of(); |
| } |
| // Explicit cast is required to compile under Servlet API 2.5, where the return type is raw Map. |
| @SuppressWarnings("cast") |
| Map<String, String[]> parameterMap = (Map<String, String[]>) request.getParameterMap(); |
| ImmutableListMultimap.Builder<String, String> b = ImmutableListMultimap.builder(); |
| parameterMap.forEach(b::putAll); |
| return b.build(); |
| } |
| |
| static class Resolver implements RepositoryResolver<HttpServletRequest> { |
| private final GitRepositoryManager manager; |
| private final PermissionBackend permissionBackend; |
| private final Provider<CurrentUser> userProvider; |
| private final ProjectCache projectCache; |
| |
| @Inject |
| Resolver( |
| GitRepositoryManager manager, |
| PermissionBackend permissionBackend, |
| Provider<CurrentUser> userProvider, |
| ProjectCache projectCache) { |
| this.manager = manager; |
| this.permissionBackend = permissionBackend; |
| this.userProvider = userProvider; |
| this.projectCache = projectCache; |
| } |
| |
| @Override |
| public Repository open(HttpServletRequest req, String projectName) |
| throws RepositoryNotFoundException, ServiceNotAuthorizedException, |
| ServiceNotEnabledException, ServiceMayNotContinueException { |
| while (projectName.endsWith("/")) { |
| projectName = projectName.substring(0, projectName.length() - 1); |
| } |
| |
| if (projectName.endsWith(".git")) { |
| // Be nice and drop the trailing ".git" suffix, which we never keep |
| // in our database, but clients might mistakenly provide anyway. |
| // |
| projectName = projectName.substring(0, projectName.length() - 4); |
| while (projectName.endsWith("/")) { |
| projectName = projectName.substring(0, projectName.length() - 1); |
| } |
| } |
| |
| CurrentUser user = userProvider.get(); |
| user.setAccessPath(AccessPath.GIT); |
| |
| try { |
| Project.NameKey nameKey = Project.nameKey(projectName); |
| ProjectState state = |
| projectCache |
| .get(nameKey) |
| .orElseThrow(() -> new RepositoryNotFoundException(nameKey.get())); |
| if (!state.statePermitsRead()) { |
| throw new RepositoryNotFoundException(nameKey.get()); |
| } |
| req.setAttribute(ATT_STATE, state); |
| |
| try { |
| permissionBackend.user(user).project(nameKey).check(ProjectPermission.ACCESS); |
| } catch (AuthException e) { |
| if (user instanceof AnonymousUser) { |
| throw new ServiceNotAuthorizedException(); |
| } |
| throw new RepositoryNotFoundException(nameKey.get(), e); |
| } |
| |
| return manager.openRepository(nameKey); |
| } catch (IOException | PermissionBackendException err) { |
| throw new ServiceMayNotContinueException(projectName + " unavailable", err); |
| } |
| } |
| } |
| |
| static class UploadFactory implements UploadPackFactory<HttpServletRequest> { |
| private final TransferConfig config; |
| private final DynamicSet<PreUploadHook> preUploadHooks; |
| private final DynamicSet<PostUploadHook> postUploadHooks; |
| private final PluginSetContext<UploadPackInitializer> uploadPackInitializers; |
| private final PermissionBackend permissionBackend; |
| |
| @Inject |
| UploadFactory( |
| TransferConfig tc, |
| DynamicSet<PreUploadHook> preUploadHooks, |
| DynamicSet<PostUploadHook> postUploadHooks, |
| PluginSetContext<UploadPackInitializer> uploadPackInitializers, |
| PermissionBackend permissionBackend) { |
| this.config = tc; |
| this.preUploadHooks = preUploadHooks; |
| this.postUploadHooks = postUploadHooks; |
| this.uploadPackInitializers = uploadPackInitializers; |
| this.permissionBackend = permissionBackend; |
| } |
| |
| @Override |
| public UploadPack create(HttpServletRequest req, Repository repo) { |
| ProjectState state = (ProjectState) req.getAttribute(ATT_STATE); |
| UploadPack up = |
| new UploadPack( |
| PermissionAwareRepositoryManager.wrap( |
| repo, permissionBackend.currentUser().project(state.getNameKey()))); |
| up.setPackConfig(config.getPackConfig()); |
| up.setTimeout(config.getTimeout()); |
| up.setPreUploadHook(PreUploadHookChain.newChain(Lists.newArrayList(preUploadHooks))); |
| up.setPostUploadHook(PostUploadHookChain.newChain(Lists.newArrayList(postUploadHooks))); |
| String header = req.getHeader("Git-Protocol"); |
| if (header != null) { |
| String[] params = header.split(":"); |
| up.setExtraParameters(Arrays.asList(params)); |
| } |
| uploadPackInitializers.runEach(initializer -> initializer.init(state.getNameKey(), up)); |
| return up; |
| } |
| } |
| |
| static class UploadFilter implements Filter { |
| private final UploadValidators.Factory uploadValidatorsFactory; |
| private final PermissionBackend permissionBackend; |
| private final Provider<CurrentUser> userProvider; |
| private final GroupAuditService groupAuditService; |
| private final Metrics metrics; |
| private final PluginSetContext<RequestListener> requestListeners; |
| private final UsersSelfAdvertiseRefsHook usersSelfAdvertiseRefsHook; |
| private final Provider<WebSession> sessionProvider; |
| |
| @Inject |
| UploadFilter( |
| UploadValidators.Factory uploadValidatorsFactory, |
| PermissionBackend permissionBackend, |
| Provider<CurrentUser> userProvider, |
| GroupAuditService groupAuditService, |
| Metrics metrics, |
| PluginSetContext<RequestListener> requestListeners, |
| UsersSelfAdvertiseRefsHook usersSelfAdvertiseRefsHook, |
| Provider<WebSession> sessionProvider) { |
| this.uploadValidatorsFactory = uploadValidatorsFactory; |
| this.permissionBackend = permissionBackend; |
| this.userProvider = userProvider; |
| this.groupAuditService = groupAuditService; |
| this.metrics = metrics; |
| this.requestListeners = requestListeners; |
| this.usersSelfAdvertiseRefsHook = usersSelfAdvertiseRefsHook; |
| this.sessionProvider = sessionProvider; |
| } |
| |
| @Override |
| public void doFilter(ServletRequest request, ServletResponse response, FilterChain next) |
| throws IOException, ServletException { |
| metrics.requestStarted(); |
| // The Resolver above already checked READ access for us. |
| Repository repo = ServletUtils.getRepository(request); |
| ProjectState state = (ProjectState) request.getAttribute(ATT_STATE); |
| UploadPack up = (UploadPack) request.getAttribute(ServletUtils.ATTRIBUTE_HANDLER); |
| PermissionBackend.ForProject perm = |
| permissionBackend.currentUser().project(state.getNameKey()); |
| HttpServletResponseWithStatusWrapper responseWrapper = |
| new HttpServletResponseWithStatusWrapper((HttpServletResponse) response); |
| HttpServletRequest httpRequest = (HttpServletRequest) request; |
| String sessionId = getSessionIdOrNull(sessionProvider); |
| |
| try (TraceContext traceContext = TraceContext.open()) { |
| RequestInfo requestInfo = |
| RequestInfo.builder( |
| RequestInfo.RequestType.GIT_UPLOAD, userProvider.get(), traceContext) |
| .project(state.getNameKey()) |
| .build(); |
| requestListeners.runEach(l -> l.onRequest(requestInfo)); |
| |
| try { |
| if (!perm.test(ProjectPermission.RUN_UPLOAD_PACK)) { |
| GitSmartHttpTools.sendError( |
| (HttpServletRequest) request, |
| responseWrapper, |
| HttpServletResponse.SC_FORBIDDEN, |
| "upload-pack not permitted on this server"); |
| return; |
| } |
| } catch (PermissionBackendException e) { |
| responseWrapper.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); |
| throw new ServletException(e); |
| } |
| |
| // We use getRemoteHost() here instead of getRemoteAddr() because REMOTE_ADDR |
| // may have been overridden by a proxy server -- we'll try to avoid this. |
| UploadValidators uploadValidators = |
| uploadValidatorsFactory.create(state.getProject(), repo, request.getRemoteHost()); |
| up.setPreUploadHook( |
| PreUploadHookChain.newChain( |
| Lists.newArrayList(up.getPreUploadHook(), uploadValidators))); |
| if (state.isAllUsers()) { |
| up.setAdvertiseRefsHook(usersSelfAdvertiseRefsHook); |
| } |
| |
| try (TracingHook tracingHook = new TracingHook()) { |
| up.setProtocolV2Hook(tracingHook); |
| next.doFilter(httpRequest, responseWrapper); |
| } |
| } finally { |
| groupAuditService.dispatch( |
| new HttpAuditEvent( |
| sessionId, |
| userProvider.get(), |
| extractWhat(httpRequest), |
| TimeUtil.nowMs(), |
| extractParameters(httpRequest), |
| httpRequest.getMethod(), |
| httpRequest, |
| responseWrapper.getResponseStatus(), |
| responseWrapper)); |
| } |
| } |
| |
| @Override |
| public void init(FilterConfig config) {} |
| |
| @Override |
| public void destroy() {} |
| } |
| |
| static class GerritUploadPackErrorHandler implements UploadPackErrorHandler { |
| private static final FluentLogger logger = FluentLogger.forEnclosingClass(); |
| |
| @Override |
| public void upload(HttpServletRequest req, HttpServletResponse rsp, UploadPackRunnable r) |
| throws IOException { |
| rsp.setHeader(GIT_COMMAND_STATUS_HEADER, GIT_COMMAND_STATUS.FAIL.toString()); |
| try { |
| r.upload(); |
| rsp.setHeader(GIT_COMMAND_STATUS_HEADER, GIT_COMMAND_STATUS.OK.toString()); |
| } catch (ServiceMayNotContinueException e) { |
| if (!e.isOutput() && !rsp.isCommitted()) { |
| rsp.reset(); |
| sendError(req, rsp, e.getStatusCode(), e.getMessage()); |
| } |
| } catch (Throwable e) { |
| logger.atSevere().withCause(e).log( |
| "%s", |
| MessageFormat.format( |
| HttpServerText.get().internalErrorDuringUploadPack, |
| ServletUtils.getRepository(req))); |
| if (!rsp.isCommitted()) { |
| rsp.reset(); |
| String msg = e instanceof PackProtocolException ? e.getMessage() : null; |
| sendError(req, rsp, UploadPackErrorHandler.statusCodeForThrowable(e), msg); |
| } |
| } |
| } |
| } |
| |
| static class ReceiveFactory implements ReceivePackFactory<HttpServletRequest> { |
| private final AsyncReceiveCommits.Factory factory; |
| private final Provider<CurrentUser> userProvider; |
| |
| @Inject |
| ReceiveFactory(AsyncReceiveCommits.Factory factory, Provider<CurrentUser> userProvider) { |
| this.factory = factory; |
| this.userProvider = userProvider; |
| } |
| |
| @Override |
| public ReceivePack create(HttpServletRequest req, Repository db) |
| throws ServiceNotAuthorizedException { |
| final ProjectState state = (ProjectState) req.getAttribute(ATT_STATE); |
| |
| if (!userProvider.get().isIdentifiedUser()) { |
| // Anonymous users are not permitted to push. |
| throw new ServiceNotAuthorizedException(); |
| } |
| |
| AsyncReceiveCommits arc = |
| factory.create(state, userProvider.get().asIdentifiedUser(), db, null); |
| ReceivePack rp = arc.getReceivePack(); |
| req.setAttribute(ATT_ARC, arc); |
| return rp; |
| } |
| } |
| |
| static class DisabledReceiveFactory implements ReceivePackFactory<HttpServletRequest> { |
| @Override |
| public ReceivePack create(HttpServletRequest req, Repository db) |
| throws ServiceNotEnabledException { |
| throw new ServiceNotEnabledException(); |
| } |
| } |
| |
| static class ReceiveFilter implements Filter { |
| private final Cache<AdvertisedObjectsCacheKey, Set<ObjectId>> cache; |
| private final PermissionBackend permissionBackend; |
| private final Provider<CurrentUser> userProvider; |
| private final GroupAuditService groupAuditService; |
| private final Metrics metrics; |
| private final Provider<WebSession> sessionProvider; |
| |
| @Inject |
| ReceiveFilter( |
| @Named(ID_CACHE) Cache<AdvertisedObjectsCacheKey, Set<ObjectId>> cache, |
| PermissionBackend permissionBackend, |
| Provider<CurrentUser> userProvider, |
| GroupAuditService groupAuditService, |
| Metrics metrics, |
| Provider<WebSession> sessionProvider) { |
| this.cache = cache; |
| this.permissionBackend = permissionBackend; |
| this.userProvider = userProvider; |
| this.groupAuditService = groupAuditService; |
| this.metrics = metrics; |
| this.sessionProvider = sessionProvider; |
| } |
| |
| @Override |
| public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) |
| throws IOException, ServletException { |
| metrics.requestStarted(); |
| boolean isGet = "GET".equalsIgnoreCase(((HttpServletRequest) request).getMethod()); |
| |
| AsyncReceiveCommits arc = (AsyncReceiveCommits) request.getAttribute(ATT_ARC); |
| |
| // Send refs down the wire. |
| ReceivePack rp = arc.getReceivePack(); |
| rp.getAdvertiseRefsHook().advertiseRefs(rp); |
| |
| ProjectState state = (ProjectState) request.getAttribute(ATT_STATE); |
| HttpServletResponseWithStatusWrapper responseWrapper = |
| new HttpServletResponseWithStatusWrapper((HttpServletResponse) response); |
| HttpServletRequest httpRequest = (HttpServletRequest) request; |
| Capable canUpload; |
| try { |
| try { |
| if (!permissionBackend |
| .currentUser() |
| .project(state.getNameKey()) |
| .test(ProjectPermission.RUN_RECEIVE_PACK)) { |
| GitSmartHttpTools.sendError( |
| httpRequest, |
| responseWrapper, |
| HttpServletResponse.SC_FORBIDDEN, |
| "receive-pack not permitted on this server"); |
| return; |
| } |
| canUpload = arc.canUpload(); |
| } catch (PermissionBackendException e) { |
| throw new RuntimeException(e); |
| } |
| } finally { |
| groupAuditService.dispatch( |
| new HttpAuditEvent( |
| getSessionIdOrNull(sessionProvider), |
| userProvider.get(), |
| extractWhat(httpRequest), |
| TimeUtil.nowMs(), |
| extractParameters(httpRequest), |
| httpRequest.getMethod(), |
| httpRequest, |
| responseWrapper.getResponseStatus(), |
| responseWrapper)); |
| } |
| |
| if (canUpload != Capable.OK) { |
| GitSmartHttpTools.sendError( |
| httpRequest, |
| responseWrapper, |
| HttpServletResponse.SC_FORBIDDEN, |
| "\n" + canUpload.getMessage()); |
| return; |
| } |
| |
| if (!rp.isCheckReferencedObjectsAreReachable()) { |
| chain.doFilter(request, responseWrapper); |
| return; |
| } |
| |
| if (!userProvider.get().isIdentifiedUser()) { |
| chain.doFilter(request, responseWrapper); |
| return; |
| } |
| |
| AdvertisedObjectsCacheKey cacheKey = |
| AdvertisedObjectsCacheKey.create(userProvider.get().getAccountId(), state.getNameKey()); |
| |
| if (isGet) { |
| cache.invalidate(cacheKey); |
| } else { |
| Set<ObjectId> ids = cache.getIfPresent(cacheKey); |
| if (ids != null) { |
| rp.getAdvertisedObjects().addAll(ids); |
| cache.invalidate(cacheKey); |
| } |
| } |
| |
| chain.doFilter(request, responseWrapper); |
| |
| if (isGet) { |
| cache.put(cacheKey, Collections.unmodifiableSet(new HashSet<>(rp.getAdvertisedObjects()))); |
| } |
| } |
| |
| @Override |
| public void init(FilterConfig arg0) {} |
| |
| @Override |
| public void destroy() {} |
| } |
| |
| private static String getSessionIdOrNull(Provider<WebSession> sessionProvider) { |
| WebSession session = sessionProvider.get(); |
| if (session.isSignedIn()) { |
| return session.getSessionId(); |
| } |
| return null; |
| } |
| } |