summaryrefslogtreecommitdiffstats
path: root/java/com/google/gerrit/server/events/EventBroker.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/gerrit/server/events/EventBroker.java')
-rw-r--r--java/com/google/gerrit/server/events/EventBroker.java228
1 files changed, 228 insertions, 0 deletions
diff --git a/java/com/google/gerrit/server/events/EventBroker.java b/java/com/google/gerrit/server/events/EventBroker.java
new file mode 100644
index 0000000000..94e9bb1fe4
--- /dev/null
+++ b/java/com/google/gerrit/server/events/EventBroker.java
@@ -0,0 +1,228 @@
+// Copyright (C) 2016 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.server.events;
+
+import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.extensions.registration.DynamicItem;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.permissions.ChangePermission;
+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.permissions.RefPermission;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
+import com.google.gerrit.server.plugincontext.PluginSetEntryContext;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+/** Distributes Events to listeners if they are allowed to see them */
+@Singleton
+public class EventBroker implements EventDispatcher {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ public static class Module extends LifecycleModule {
+ @Override
+ protected void configure() {
+ DynamicItem.itemOf(binder(), EventDispatcher.class);
+ DynamicItem.bind(binder(), EventDispatcher.class).to(EventBroker.class);
+ }
+ }
+
+ /** Listeners to receive changes as they happen (limited by visibility of user). */
+ protected final PluginSetContext<UserScopedEventListener> listeners;
+
+ /** Listeners to receive all changes as they happen. */
+ protected final PluginSetContext<EventListener> unrestrictedListeners;
+
+ private final PermissionBackend permissionBackend;
+ protected final ProjectCache projectCache;
+
+ protected final ChangeNotes.Factory notesFactory;
+
+ protected final Provider<ReviewDb> dbProvider;
+
+ @Inject
+ public EventBroker(
+ PluginSetContext<UserScopedEventListener> listeners,
+ PluginSetContext<EventListener> unrestrictedListeners,
+ PermissionBackend permissionBackend,
+ ProjectCache projectCache,
+ ChangeNotes.Factory notesFactory,
+ Provider<ReviewDb> dbProvider) {
+ this.listeners = listeners;
+ this.unrestrictedListeners = unrestrictedListeners;
+ this.permissionBackend = permissionBackend;
+ this.projectCache = projectCache;
+ this.notesFactory = notesFactory;
+ this.dbProvider = dbProvider;
+ }
+
+ @Override
+ public void postEvent(Change change, ChangeEvent event)
+ throws OrmException, PermissionBackendException {
+ fireEvent(change, event);
+ }
+
+ @Override
+ public void postEvent(Branch.NameKey branchName, RefEvent event)
+ throws PermissionBackendException {
+ fireEvent(branchName, event);
+ }
+
+ @Override
+ public void postEvent(Project.NameKey projectName, ProjectEvent event) {
+ fireEvent(projectName, event);
+ }
+
+ @Override
+ public void postEvent(Event event) throws OrmException, PermissionBackendException {
+ fireEvent(event);
+ }
+
+ protected void fireEventForUnrestrictedListeners(Event event) {
+ unrestrictedListeners.runEach(l -> l.onEvent(event));
+ }
+
+ protected void fireEvent(Change change, ChangeEvent event)
+ throws OrmException, PermissionBackendException {
+ for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
+ CurrentUser user = c.call(l -> l.getUser());
+ if (isVisibleTo(change, user)) {
+ c.run(l -> l.onEvent(event));
+ }
+ }
+ fireEventForUnrestrictedListeners(event);
+ }
+
+ protected void fireEvent(Project.NameKey project, ProjectEvent event) {
+ for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
+ CurrentUser user = c.call(l -> l.getUser());
+ if (isVisibleTo(project, user)) {
+ c.run(l -> l.onEvent(event));
+ }
+ }
+ fireEventForUnrestrictedListeners(event);
+ }
+
+ protected void fireEvent(Branch.NameKey branchName, RefEvent event)
+ throws PermissionBackendException {
+ for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
+ CurrentUser user = c.call(l -> l.getUser());
+ if (isVisibleTo(branchName, user)) {
+ c.run(l -> l.onEvent(event));
+ }
+ }
+ fireEventForUnrestrictedListeners(event);
+ }
+
+ protected void fireEvent(Event event) throws OrmException, PermissionBackendException {
+ for (PluginSetEntryContext<UserScopedEventListener> c : listeners) {
+ CurrentUser user = c.call(l -> l.getUser());
+ if (isVisibleTo(event, user)) {
+ c.run(l -> l.onEvent(event));
+ }
+ }
+ fireEventForUnrestrictedListeners(event);
+ }
+
+ protected boolean isVisibleTo(Project.NameKey project, CurrentUser user) {
+ try {
+ ProjectState state = projectCache.get(project);
+ if (state == null || !state.statePermitsRead()) {
+ return false;
+ }
+
+ permissionBackend.user(user).project(project).check(ProjectPermission.ACCESS);
+ return true;
+ } catch (AuthException | PermissionBackendException e) {
+ return false;
+ }
+ }
+
+ protected boolean isVisibleTo(Change change, CurrentUser user)
+ throws OrmException, PermissionBackendException {
+ if (change == null) {
+ return false;
+ }
+ ProjectState pe = projectCache.get(change.getProject());
+ if (pe == null || !pe.statePermitsRead()) {
+ return false;
+ }
+ ReviewDb db = dbProvider.get();
+ try {
+ permissionBackend
+ .user(user)
+ .change(notesFactory.createChecked(db, change))
+ .database(db)
+ .check(ChangePermission.READ);
+ return true;
+ } catch (AuthException e) {
+ return false;
+ }
+ }
+
+ protected boolean isVisibleTo(Branch.NameKey branchName, CurrentUser user)
+ throws PermissionBackendException {
+ ProjectState pe = projectCache.get(branchName.getParentKey());
+ if (pe == null || !pe.statePermitsRead()) {
+ return false;
+ }
+
+ try {
+ permissionBackend.user(user).ref(branchName).check(RefPermission.READ);
+ return true;
+ } catch (AuthException e) {
+ return false;
+ }
+ }
+
+ protected boolean isVisibleTo(Event event, CurrentUser user)
+ throws OrmException, PermissionBackendException {
+ if (event instanceof RefEvent) {
+ RefEvent refEvent = (RefEvent) event;
+ String ref = refEvent.getRefName();
+ if (PatchSet.isChangeRef(ref)) {
+ Change.Id cid = PatchSet.Id.fromRef(ref).getParentKey();
+ try {
+ Change change =
+ notesFactory
+ .createChecked(dbProvider.get(), refEvent.getProjectNameKey(), cid)
+ .getChange();
+ return isVisibleTo(change, user);
+ } catch (NoSuchChangeException e) {
+ logger.atFine().log(
+ "Change %s cannot be found, falling back on ref visibility check", cid.id);
+ }
+ }
+ return isVisibleTo(refEvent.getBranchNameKey(), user);
+ } else if (event instanceof ProjectEvent) {
+ return isVisibleTo(((ProjectEvent) event).getProjectNameKey(), user);
+ }
+ return true;
+ }
+}