summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntonio Barone <syntonyze@gmail.com>2018-11-13 15:58:16 -0800
committerDavid Pursehouse <dpursehouse@collab.net>2018-11-14 00:43:58 +0000
commit98ecbb1cfe8e3b9074c375223110c6c4573d0add (patch)
tree69158054e5ce95fd3e5d1ab99a5df7b1f1ad526e
parentb22b8a7b12688e942e1c2c966a3c55ee45bbafed (diff)
parentd93736f81ba65edd33a5751ce3c7e8dafe2a9c23 (diff)
Merge branch 'stable-2.15' into stable-2.16
* stable-2.15: Set version to 2.15.7 Trigger audit for GIT over Http commands Change-Id: If5451ef6b21df9219b06dd045f1138c5367fc3b2
-rw-r--r--java/com/google/gerrit/acceptance/AbstractDaemonTest.java2
-rw-r--r--java/com/google/gerrit/acceptance/GerritServer.java2
-rw-r--r--java/com/google/gerrit/audit/AuditServiceImpl.java94
-rw-r--r--java/com/google/gerrit/common/audit/Audit.java2
-rw-r--r--java/com/google/gerrit/httpd/GitOverHttpServlet.java72
-rw-r--r--java/com/google/gerrit/httpd/HttpLogoutServlet.java2
-rw-r--r--java/com/google/gerrit/pgm/Daemon.java12
-rw-r--r--java/com/google/gerrit/server/AuditEvent.java (renamed from java/com/google/gerrit/server/audit/AuditEvent.java)3
-rw-r--r--java/com/google/gerrit/server/audit/AuditListener.java1
-rw-r--r--java/com/google/gerrit/server/audit/AuditService.java2
-rw-r--r--java/com/google/gerrit/server/audit/HttpAuditEvent.java1
-rw-r--r--java/com/google/gerrit/server/audit/SshAuditEvent.java1
-rw-r--r--java/com/google/gerrit/server/group/GroupAuditService.java2
-rw-r--r--java/com/google/gerrit/server/schema/GroupRebuilder.java3
-rw-r--r--java/com/google/gerrit/testing/FakeGroupAuditService.java112
-rw-r--r--javatests/com/google/gerrit/acceptance/git/GitOverHttpServletIT.java68
16 files changed, 370 insertions, 9 deletions
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 496ee5b481..a4b5a945e7 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -128,6 +128,7 @@ import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.testing.ConfigSuite;
import com.google.gerrit.testing.FakeEmailSender;
import com.google.gerrit.testing.FakeEmailSender.Message;
+import com.google.gerrit.testing.FakeGroupAuditService;
import com.google.gerrit.testing.NoteDbMode;
import com.google.gerrit.testing.SshMode;
import com.google.gerrit.testing.TempFileUtil;
@@ -244,6 +245,7 @@ public abstract class AbstractDaemonTest {
@Inject protected ChangeNoteUtil changeNoteUtil;
@Inject protected ChangeResource.Factory changeResourceFactory;
@Inject protected FakeEmailSender sender;
+ @Inject protected FakeGroupAuditService auditService;
@Inject protected GerritApi gApi;
@Inject protected GitRepositoryManager repoManager;
@Inject protected GroupBackend groupBackend;
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index 9f9cbf937b..be8932e3a6 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -43,6 +43,7 @@ import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.server.util.SocketUtil;
import com.google.gerrit.server.util.SystemLog;
import com.google.gerrit.testing.FakeEmailSender;
+import com.google.gerrit.testing.FakeGroupAuditService;
import com.google.gerrit.testing.InMemoryDatabase;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import com.google.gerrit.testing.NoteDbChecker;
@@ -355,6 +356,7 @@ public class GerritServer implements AutoCloseable {
},
site);
daemon.setEmailModuleForTesting(new FakeEmailSender.Module());
+ daemon.setAuditEventModuleForTesting(new FakeGroupAuditService.Module());
daemon.setAdditionalSysModuleForTesting(testSysModule);
daemon.setEnableSshd(desc.useSsh());
daemon.setSlave(isSlave(baseConfig));
diff --git a/java/com/google/gerrit/audit/AuditServiceImpl.java b/java/com/google/gerrit/audit/AuditServiceImpl.java
new file mode 100644
index 0000000000..940742fc9d
--- /dev/null
+++ b/java/com/google/gerrit/audit/AuditServiceImpl.java
@@ -0,0 +1,94 @@
+// Copyright (C) 2018 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.audit;
+
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroupById;
+import com.google.gerrit.reviewdb.client.AccountGroupMember;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.Collection;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class AuditServiceImpl implements AuditService {
+ private static final Logger log = LoggerFactory.getLogger(AuditServiceImpl.class);
+
+ private final DynamicSet<AuditListener> auditListeners;
+ private final DynamicSet<GroupMemberAuditListener> groupMemberAuditListeners;
+
+ @Inject
+ public AuditServiceImpl(
+ DynamicSet<AuditListener> auditListeners,
+ DynamicSet<GroupMemberAuditListener> groupMemberAuditListeners) {
+ this.auditListeners = auditListeners;
+ this.groupMemberAuditListeners = groupMemberAuditListeners;
+ }
+
+ @Override
+ public void dispatch(AuditEvent action) {
+ for (AuditListener auditListener : auditListeners) {
+ auditListener.onAuditableAction(action);
+ }
+ }
+
+ @Override
+ public void dispatchAddAccountsToGroup(Account.Id actor, Collection<AccountGroupMember> added) {
+ for (GroupMemberAuditListener auditListener : groupMemberAuditListeners) {
+ try {
+ auditListener.onAddAccountsToGroup(actor, added);
+ } catch (RuntimeException e) {
+ log.error("failed to log add accounts to group event", e);
+ }
+ }
+ }
+
+ @Override
+ public void dispatchDeleteAccountsFromGroup(
+ Account.Id actor, Collection<AccountGroupMember> removed) {
+ for (GroupMemberAuditListener auditListener : groupMemberAuditListeners) {
+ try {
+ auditListener.onDeleteAccountsFromGroup(actor, removed);
+ } catch (RuntimeException e) {
+ log.error("failed to log delete accounts from group event", e);
+ }
+ }
+ }
+
+ @Override
+ public void dispatchAddGroupsToGroup(Account.Id actor, Collection<AccountGroupById> added) {
+ for (GroupMemberAuditListener auditListener : groupMemberAuditListeners) {
+ try {
+ auditListener.onAddGroupsToGroup(actor, added);
+ } catch (RuntimeException e) {
+ log.error("failed to log add groups to group event", e);
+ }
+ }
+ }
+
+ @Override
+ public void dispatchDeleteGroupsFromGroup(
+ Account.Id actor, Collection<AccountGroupById> removed) {
+ for (GroupMemberAuditListener auditListener : groupMemberAuditListeners) {
+ try {
+ auditListener.onDeleteGroupsFromGroup(actor, removed);
+ } catch (RuntimeException e) {
+ log.error("failed to log delete groups from group event", e);
+ }
+ }
+ }
+}
diff --git a/java/com/google/gerrit/common/audit/Audit.java b/java/com/google/gerrit/common/audit/Audit.java
index 25e4caf67f..a791e97429 100644
--- a/java/com/google/gerrit/common/audit/Audit.java
+++ b/java/com/google/gerrit/common/audit/Audit.java
@@ -23,7 +23,7 @@ import java.lang.annotation.Target;
* Audit annotation for JSON/RPC interfaces.
*
* <p>Flag with @Audit all the JSON/RPC methods to be traced in audit-trail and submitted to the
- * AuditService.
+ * GroupAuditService.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
diff --git a/java/com/google/gerrit/httpd/GitOverHttpServlet.java b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
index 77ce983240..08ff8a74f7 100644
--- a/java/com/google/gerrit/httpd/GitOverHttpServlet.java
+++ b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
@@ -15,6 +15,8 @@
package com.google.gerrit.httpd;
import com.google.common.cache.Cache;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.Capable;
import com.google.gerrit.extensions.registration.DynamicSet;
@@ -23,6 +25,7 @@ import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.audit.HttpAuditEvent;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.git.DefaultAdvertiseRefsHook;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -30,12 +33,14 @@ import com.google.gerrit.server.git.TransferConfig;
import com.google.gerrit.server.git.UploadPackInitializer;
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.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
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;
@@ -141,6 +146,30 @@ public class GitOverHttpServlet extends GitServlet {
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) {
+
+ ListMultimap<String, String> multiMap = ArrayListMultimap.create();
+ if (request.getQueryString() != null) {
+ request
+ .getParameterMap()
+ .forEach(
+ (k, v) -> {
+ for (int i = 0; i < v.length; i++) {
+ multiMap.put(k, v[i]);
+ }
+ });
+ }
+ return multiMap;
+ }
+
static class Resolver implements RepositoryResolver<HttpServletRequest> {
private final GitRepositoryManager manager;
private final PermissionBackend permissionBackend;
@@ -240,12 +269,19 @@ public class GitOverHttpServlet extends GitServlet {
static class UploadFilter implements Filter {
private final UploadValidators.Factory uploadValidatorsFactory;
private final PermissionBackend permissionBackend;
+ private final Provider<CurrentUser> userProvider;
+ private final GroupAuditService groupAuditService;
@Inject
UploadFilter(
- UploadValidators.Factory uploadValidatorsFactory, PermissionBackend permissionBackend) {
+ UploadValidators.Factory uploadValidatorsFactory,
+ PermissionBackend permissionBackend,
+ Provider<CurrentUser> userProvider,
+ GroupAuditService groupAuditService) {
this.uploadValidatorsFactory = uploadValidatorsFactory;
this.permissionBackend = permissionBackend;
+ this.userProvider = userProvider;
+ this.groupAuditService = groupAuditService;
}
@Override
@@ -268,7 +304,22 @@ public class GitOverHttpServlet extends GitServlet {
return;
} catch (PermissionBackendException e) {
throw new ServletException(e);
+ } finally {
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+ groupAuditService.dispatch(
+ new HttpAuditEvent(
+ httpRequest.getSession().getId(),
+ userProvider.get(),
+ extractWhat(httpRequest),
+ TimeUtil.nowMs(),
+ extractParameters(httpRequest),
+ httpRequest.getMethod(),
+ httpRequest,
+ httpResponse.getStatus(),
+ httpResponse));
}
+
// 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 =
@@ -326,15 +377,18 @@ public class GitOverHttpServlet extends GitServlet {
private final Cache<AdvertisedObjectsCacheKey, Set<ObjectId>> cache;
private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> userProvider;
+ private final GroupAuditService groupAuditService;
@Inject
ReceiveFilter(
@Named(ID_CACHE) Cache<AdvertisedObjectsCacheKey, Set<ObjectId>> cache,
PermissionBackend permissionBackend,
- Provider<CurrentUser> userProvider) {
+ Provider<CurrentUser> userProvider,
+ GroupAuditService groupAuditService) {
this.cache = cache;
this.permissionBackend = permissionBackend;
this.userProvider = userProvider;
+ this.groupAuditService = groupAuditService;
}
@Override
@@ -365,6 +419,20 @@ public class GitOverHttpServlet extends GitServlet {
return;
} catch (PermissionBackendException e) {
throw new RuntimeException(e);
+ } finally {
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+ groupAuditService.dispatch(
+ new HttpAuditEvent(
+ httpRequest.getSession().getId(),
+ userProvider.get(),
+ extractWhat(httpRequest),
+ TimeUtil.nowMs(),
+ extractParameters(httpRequest),
+ httpRequest.getMethod(),
+ httpRequest,
+ httpResponse.getStatus(),
+ httpResponse));
}
if (canUpload != Capable.OK) {
diff --git a/java/com/google/gerrit/httpd/HttpLogoutServlet.java b/java/com/google/gerrit/httpd/HttpLogoutServlet.java
index abfcc22df4..ab7bfdfee5 100644
--- a/java/com/google/gerrit/httpd/HttpLogoutServlet.java
+++ b/java/com/google/gerrit/httpd/HttpLogoutServlet.java
@@ -17,8 +17,8 @@ package com.google.gerrit.httpd;
import com.google.common.base.Strings;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.registration.DynamicItem;
+import com.google.gerrit.server.AuditEvent;
import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.audit.AuditEvent;
import com.google.gerrit.server.audit.AuditService;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.CanonicalWebUrl;
diff --git a/java/com/google/gerrit/pgm/Daemon.java b/java/com/google/gerrit/pgm/Daemon.java
index 2249c762ad..a777a1cb29 100644
--- a/java/com/google/gerrit/pgm/Daemon.java
+++ b/java/com/google/gerrit/pgm/Daemon.java
@@ -199,6 +199,7 @@ public class Daemon extends SiteProgram {
private AbstractModule luceneModule;
private Module emailModule;
private Module testSysModule;
+ private Module auditEventModule;
private Runnable serverStarted;
private IndexType indexType;
@@ -320,6 +321,11 @@ public class Daemon extends SiteProgram {
}
@VisibleForTesting
+ public void setAuditEventModuleForTesting(Module module) {
+ auditEventModule = module;
+ }
+
+ @VisibleForTesting
public void setLuceneModule(LuceneIndexModule m) {
luceneModule = m;
inMemoryTest = true;
@@ -425,7 +431,6 @@ public class Daemon extends SiteProgram {
modules.add(cfgInjector.getInstance(GerritGlobalModule.class));
modules.add(new GerritApiModule());
modules.add(new PluginApiModule());
- modules.add(new AuditModule());
modules.add(new SearchingChangeCacheImpl.Module(slave));
modules.add(new InternalAccountDirectory.Module());
@@ -438,6 +443,11 @@ public class Daemon extends SiteProgram {
} else {
modules.add(new SmtpEmailSender.Module());
}
+ if (auditEventModule != null) {
+ modules.add(auditEventModule);
+ } else {
+ modules.add(new AuditModule());
+ }
modules.add(new SignedTokenEmailTokenVerifier.Module());
modules.add(new RestApiModule());
modules.add(new GpgModule(config));
diff --git a/java/com/google/gerrit/server/audit/AuditEvent.java b/java/com/google/gerrit/server/AuditEvent.java
index 46b28449b9..773a307aa6 100644
--- a/java/com/google/gerrit/server/audit/AuditEvent.java
+++ b/java/com/google/gerrit/server/AuditEvent.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.audit;
+package com.google.gerrit.server;
import static java.util.Objects.requireNonNull;
@@ -20,7 +20,6 @@ import com.google.auto.value.AutoValue;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ListMultimap;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.util.time.TimeUtil;
public class AuditEvent {
diff --git a/java/com/google/gerrit/server/audit/AuditListener.java b/java/com/google/gerrit/server/audit/AuditListener.java
index 3f8c298d03..f555bbd1ff 100644
--- a/java/com/google/gerrit/server/audit/AuditListener.java
+++ b/java/com/google/gerrit/server/audit/AuditListener.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.audit;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
+import com.google.gerrit.server.AuditEvent;
@ExtensionPoint
public interface AuditListener {
diff --git a/java/com/google/gerrit/server/audit/AuditService.java b/java/com/google/gerrit/server/audit/AuditService.java
index cbca65b9ff..425e22a7ec 100644
--- a/java/com/google/gerrit/server/audit/AuditService.java
+++ b/java/com/google/gerrit/server/audit/AuditService.java
@@ -17,6 +17,7 @@ package com.google.gerrit.server.audit;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.AuditEvent;
import com.google.gerrit.server.audit.group.GroupAuditListener;
import com.google.gerrit.server.audit.group.GroupMemberAuditEvent;
import com.google.gerrit.server.audit.group.GroupSubgroupAuditEvent;
@@ -39,6 +40,7 @@ public class AuditService implements GroupAuditService {
this.groupAuditListeners = groupAuditListeners;
}
+ @Override
public void dispatch(AuditEvent action) {
auditListeners.runEach(l -> l.onAuditableAction(action));
}
diff --git a/java/com/google/gerrit/server/audit/HttpAuditEvent.java b/java/com/google/gerrit/server/audit/HttpAuditEvent.java
index 11a6b63088..5ea248531b 100644
--- a/java/com/google/gerrit/server/audit/HttpAuditEvent.java
+++ b/java/com/google/gerrit/server/audit/HttpAuditEvent.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.audit;
import com.google.common.collect.ListMultimap;
+import com.google.gerrit.server.AuditEvent;
import com.google.gerrit.server.CurrentUser;
public class HttpAuditEvent extends AuditEvent {
diff --git a/java/com/google/gerrit/server/audit/SshAuditEvent.java b/java/com/google/gerrit/server/audit/SshAuditEvent.java
index 89f01acf51..fee959eda2 100644
--- a/java/com/google/gerrit/server/audit/SshAuditEvent.java
+++ b/java/com/google/gerrit/server/audit/SshAuditEvent.java
@@ -15,6 +15,7 @@
package com.google.gerrit.server.audit;
import com.google.common.collect.ListMultimap;
+import com.google.gerrit.server.AuditEvent;
import com.google.gerrit.server.CurrentUser;
public class SshAuditEvent extends AuditEvent {
diff --git a/java/com/google/gerrit/server/group/GroupAuditService.java b/java/com/google/gerrit/server/group/GroupAuditService.java
index c543a6e409..4b851ea0b8 100644
--- a/java/com/google/gerrit/server/group/GroupAuditService.java
+++ b/java/com/google/gerrit/server/group/GroupAuditService.java
@@ -18,9 +18,11 @@ import com.google.common.collect.ImmutableSet;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Account.Id;
import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.AuditEvent;
import java.sql.Timestamp;
public interface GroupAuditService {
+ void dispatch(AuditEvent action);
void dispatchAddMembers(
Account.Id actor,
diff --git a/java/com/google/gerrit/server/schema/GroupRebuilder.java b/java/com/google/gerrit/server/schema/GroupRebuilder.java
index 54cbb86ac4..0157025a87 100644
--- a/java/com/google/gerrit/server/schema/GroupRebuilder.java
+++ b/java/com/google/gerrit/server/schema/GroupRebuilder.java
@@ -275,8 +275,7 @@ class GroupRebuilder {
* Distinct event types.
*
* <p>Events at the same time by the same user are batched together by type. The types should
- * correspond to the possible batch operations supported by {@link
- * com.google.gerrit.server.audit.AuditService}.
+ * correspond to the possible batch operations supported by AuditService.
*/
enum Type {
ADD_MEMBER,
diff --git a/java/com/google/gerrit/testing/FakeGroupAuditService.java b/java/com/google/gerrit/testing/FakeGroupAuditService.java
new file mode 100644
index 0000000000..7c6674b4c6
--- /dev/null
+++ b/java/com/google/gerrit/testing/FakeGroupAuditService.java
@@ -0,0 +1,112 @@
+// Copyright (C) 2018 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.testing;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.server.AuditEvent;
+import com.google.gerrit.server.audit.AuditListener;
+import com.google.gerrit.server.audit.group.GroupAuditListener;
+import com.google.gerrit.server.audit.group.GroupMemberAuditEvent;
+import com.google.gerrit.server.audit.group.GroupSubgroupAuditEvent;
+import com.google.gerrit.server.group.GroupAuditService;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.List;
+
+@Singleton
+public class FakeGroupAuditService implements GroupAuditService {
+
+ private final PluginSetContext<GroupAuditListener> groupAuditListeners;
+ private final PluginSetContext<AuditListener> auditListeners;
+
+ public static class Module extends AbstractModule {
+ @Override
+ public void configure() {
+ DynamicSet.setOf(binder(), GroupAuditListener.class);
+ DynamicSet.setOf(binder(), AuditListener.class);
+ bind(GroupAuditService.class).to(FakeGroupAuditService.class);
+ }
+ }
+
+ @Inject
+ public FakeGroupAuditService(
+ PluginSetContext<GroupAuditListener> groupAuditListeners,
+ PluginSetContext<AuditListener> auditListeners) {
+ this.groupAuditListeners = groupAuditListeners;
+ this.auditListeners = auditListeners;
+ }
+
+ public List<AuditEvent> auditEvents = new ArrayList<>();
+
+ public void clearEvents() {
+ auditEvents.clear();
+ }
+
+ @Override
+ public void dispatch(AuditEvent action) {
+ auditEvents.add(action);
+ }
+
+ @Override
+ public void dispatchAddMembers(
+ Account.Id actor,
+ AccountGroup.UUID updatedGroup,
+ ImmutableSet<Account.Id> addedMembers,
+ Timestamp addedOn) {
+ GroupMemberAuditEvent event =
+ GroupMemberAuditEvent.create(actor, updatedGroup, addedMembers, addedOn);
+ groupAuditListeners.runEach(l -> l.onAddMembers(event));
+ }
+
+ @Override
+ public void dispatchDeleteMembers(
+ Account.Id actor,
+ AccountGroup.UUID updatedGroup,
+ ImmutableSet<Account.Id> deletedMembers,
+ Timestamp deletedOn) {
+ GroupMemberAuditEvent event =
+ GroupMemberAuditEvent.create(actor, updatedGroup, deletedMembers, deletedOn);
+ groupAuditListeners.runEach(l -> l.onDeleteMembers(event));
+ }
+
+ @Override
+ public void dispatchAddSubgroups(
+ Account.Id actor,
+ AccountGroup.UUID updatedGroup,
+ ImmutableSet<AccountGroup.UUID> addedSubgroups,
+ Timestamp addedOn) {
+ GroupSubgroupAuditEvent event =
+ GroupSubgroupAuditEvent.create(actor, updatedGroup, addedSubgroups, addedOn);
+ groupAuditListeners.runEach(l -> l.onAddSubgroups(event));
+ }
+
+ @Override
+ public void dispatchDeleteSubgroups(
+ Account.Id actor,
+ AccountGroup.UUID updatedGroup,
+ ImmutableSet<AccountGroup.UUID> deletedSubgroups,
+ Timestamp deletedOn) {
+ GroupSubgroupAuditEvent event =
+ GroupSubgroupAuditEvent.create(actor, updatedGroup, deletedSubgroups, deletedOn);
+ groupAuditListeners.runEach(l -> l.onDeleteSubgroups(event));
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/git/GitOverHttpServletIT.java b/javatests/com/google/gerrit/acceptance/git/GitOverHttpServletIT.java
new file mode 100644
index 0000000000..42e046ad02
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/git/GitOverHttpServletIT.java
@@ -0,0 +1,68 @@
+// Copyright (C) 2018 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.acceptance.git;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.server.AuditEvent;
+import java.util.Collections;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
+import org.junit.Before;
+import org.junit.Test;
+
+public class GitOverHttpServletIT extends AbstractPushForReview {
+
+ @Before
+ public void beforeEach() throws Exception {
+ CredentialsProvider.setDefault(
+ new UsernamePasswordCredentialsProvider(admin.username, admin.httpPassword));
+ selectProtocol(AbstractPushForReview.Protocol.HTTP);
+ auditService.clearEvents();
+ }
+
+ @Test
+ public void receivePackAuditEventLog() throws Exception {
+ testRepo
+ .git()
+ .push()
+ .setRemote("origin")
+ .setRefSpecs(new RefSpec("HEAD:refs/for/master"))
+ .call();
+
+ // Git smart protocol makes two requests:
+ // https://github.com/git/git/blob/master/Documentation/technical/http-protocol.txt
+ assertThat(auditService.auditEvents.size()).isEqualTo(2);
+
+ AuditEvent e = auditService.auditEvents.get(1);
+ assertThat(e.who.getAccountId()).isEqualTo(admin.id);
+ assertThat(e.what).endsWith("/git-receive-pack");
+ assertThat(e.params).isEmpty();
+ }
+
+ @Test
+ public void uploadPackAuditEventLog() throws Exception {
+ testRepo.git().fetch().call();
+
+ assertThat(auditService.auditEvents.size()).isEqualTo(1);
+
+ AuditEvent e = auditService.auditEvents.get(0);
+ assertThat(e.who.toString()).isEqualTo("ANONYMOUS");
+ assertThat(e.params.get("service"))
+ .containsExactlyElementsIn(Collections.singletonList("git-upload-pack"));
+ assertThat(e.what).endsWith("service=git-upload-pack");
+ }
+}