public boolean isSystemAdministrator() {
return false;
}
+
+ @Override
+ public boolean isActive() {
+ return false;
+ }
}
return true;
}
+ @Override
+ public boolean isActive() {
+ return true;
+ }
+
}
private void start() {
return isSystemAdministrator;
}
+ @Override
+ public boolean isActive() {
+ return userDto.isActive();
+ }
+
private boolean loadIsSystemAdministrator() {
if (isRoot()) {
return true;
return this;
}
+ @Override
+ public boolean isActive() {
+ return get().isActive();
+ }
+
@Override
public boolean hasComponentPermission(String permission, ComponentDto component) {
return get().hasComponentPermission(permission, component);
* otherwise throws {@link org.sonar.server.exceptions.ForbiddenException}.
*/
UserSession checkIsSystemAdministrator();
+
+ boolean isActive();
}
assertThat(underTest.shouldResetPassword()).isFalse();
assertThat(underTest.getName()).isNull();
assertThat(underTest.getGroups()).isEmpty();
+ assertThat(underTest.isActive()).isFalse();
}
@Test
assertThat(catcher.userSession.hasComponentPermission("any permission", new ComponentDto())).isTrue();
assertThat(catcher.userSession.isSystemAdministrator()).isTrue();
assertThat(catcher.userSession.shouldResetPassword()).isFalse();
+ assertThat(catcher.userSession.isActive()).isTrue();
assertThat(catcher.userSession.hasChildProjectsPermission(USER, new ComponentDto())).isTrue();
assertThat(catcher.userSession.hasPortfolioChildProjectsPermission(USER, new ComponentDto())).isTrue();
assertThat(session.getGroups()).extracting(GroupDto::getUuid).containsOnly(group1.getUuid());
}
+ @Test
+ public void isActive_redirectsValueFromUserDto() {
+ UserDto active = db.users().insertUser();
+ active.setActive(true);
+ assertThat(newUserSession(active).isActive()).isTrue();
+
+ UserDto notActive = db.users().insertUser();
+ notActive.setActive(false);
+ assertThat(newUserSession(notActive).isActive()).isFalse();
+ }
+
@Test
public void isRoot_is_false_is_flag_root_is_false_on_UserDto() {
UserDto root = db.users().insertUser();
assertThat(threadLocalUserSession.getLogin()).isEqualTo("karadoc");
assertThat(threadLocalUserSession.getUuid()).isEqualTo("karadoc-uuid");
assertThat(threadLocalUserSession.isLoggedIn()).isTrue();
+ assertThat(threadLocalUserSession.isActive()).isTrue();
assertThat(threadLocalUserSession.shouldResetPassword()).isTrue();
assertThat(threadLocalUserSession.getGroups()).extracting(GroupDto::getUuid).containsOnly(group.getUuid());
assertThat(threadLocalUserSession.hasChildProjectsPermission(USER, new ComponentDto())).isFalse();
return false;
}
+ @Override
+ public boolean isActive() {
+ return false;
+ }
+
@Override
public String getLogin() {
return null;
return root;
}
+ @Override
+ public boolean isActive() {
+ return true;
+ }
+
public void setRoot(boolean root) {
this.root = root;
}
currentUserSession.checkIsSystemAdministrator();
return this;
}
+
+ @Override
+ public boolean isActive() {
+ return currentUserSession.isActive();
+ }
}
throw notImplemented();
}
+ @Override
+ public boolean isActive() {
+ throw notImplemented();
+ }
+
private static RuntimeException notImplemented() {
return new UnsupportedOperationException("not implemented");
}
close();
}
- private synchronized void close() {
+ public synchronized void close() {
startedHeartbeat.cancel(false);
- asyncContext.complete();
+ try {
+ asyncContext.complete();
+ } catch (IllegalStateException ex) {
+ LOG.trace("Push connection was already closed");
+ }
}
private ServletOutputStream output() throws IOException {
package org.sonar.server.pushapi;
import org.sonar.core.platform.Module;
+import org.sonar.server.pushapi.sonarlint.SonarLintClientPermissionsValidator;
import org.sonar.server.pushapi.sonarlint.SonarLintClientsRegistry;
import org.sonar.server.pushapi.sonarlint.SonarLintPushAction;
protected void configureModule() {
add(
ServerPushWs.class,
-
+ SonarLintClientPermissionsValidator.class,
SonarLintClientsRegistry.class,
SonarLintPushAction.class);
}
private final Set<String> languages;
private final Set<String> projectKeys;
- public SonarLintClient(AsyncContext asyncContext, Set<String> projectKeys, Set<String> languages) {
+ private final String userUuid;
+
+ public SonarLintClient(AsyncContext asyncContext, Set<String> projectKeys, Set<String> languages, String userUuid) {
super(scheduledExecutorService, asyncContext);
this.projectKeys = projectKeys;
this.languages = languages;
+ this.userUuid = userUuid;
}
public Set<String> getLanguages() {
public int hashCode() {
return Objects.hash(languages, projectKeys);
}
+
+ public String getUserUuid() {
+ return userUuid;
+ }
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.pushapi.sonarlint;
+
+import java.util.List;
+import java.util.Set;
+import org.sonar.api.server.ServerSide;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.user.UserSession;
+import org.sonar.server.user.UserSessionFactory;
+
+@ServerSide
+public class SonarLintClientPermissionsValidator {
+
+ private final DbClient dbClient;
+ private final UserSessionFactory userSessionFactory;
+
+ public SonarLintClientPermissionsValidator(DbClient dbClient, UserSessionFactory userSessionFactory) {
+ this.dbClient = dbClient;
+ this.userSessionFactory = userSessionFactory;
+ }
+
+ public void validateUserCanReceivePushEventForProjects(UserSession userSession, Set<String> projectKeys) {
+ List<ProjectDto> projectDtos;
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ projectDtos = dbClient.projectDao().selectProjectsByKeys(dbSession, projectKeys);
+ }
+ validateUsersDeactivationStatus(userSession);
+ for (ProjectDto projectDto : projectDtos) {
+ userSession.checkProjectPermission(UserRole.USER, projectDto);
+ }
+ }
+
+ public void validateUserCanReceivePushEventForProjects(String userUUID, Set<String> projectKeys) {
+ UserDto userDto;
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ userDto = dbClient.userDao().selectByUuid(dbSession, userUUID);
+ }
+ if (userDto == null) {
+ throw new ForbiddenException("User does not exist");
+ }
+ validateUserCanReceivePushEventForProjects(userSessionFactory.create(userDto), projectKeys);
+ }
+
+ private static void validateUsersDeactivationStatus(UserSession userSession) {
+ if (!userSession.isActive()) {
+ throw new ForbiddenException("User doesn't have rights to requested resource anymore.");
+ }
+ }
+}
import java.io.IOException;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Predicate;
import javax.servlet.AsyncEvent;
import org.sonar.core.util.RuleChange;
import org.sonar.core.util.RuleSetChangeEvent;
import org.sonar.server.pushapi.qualityprofile.RuleActivatorEventsDistributor;
+import org.sonar.server.exceptions.ForbiddenException;
import static java.util.Arrays.asList;
private static final Logger LOG = Loggers.get(SonarLintClientsRegistry.class);
private final RuleActivatorEventsDistributor ruleActivatorEventsDistributor;
+ private final SonarLintClientPermissionsValidator sonarLintClientPermissionsValidator;
- public SonarLintClientsRegistry(RuleActivatorEventsDistributor ruleActivatorEventsDistributor) {
+ public SonarLintClientsRegistry(RuleActivatorEventsDistributor ruleActivatorEventsDistributor, SonarLintClientPermissionsValidator permissionsValidator) {
this.ruleActivatorEventsDistributor = ruleActivatorEventsDistributor;
+ this.sonarLintClientPermissionsValidator = permissionsValidator;
}
-
private final List<SonarLintClient> clients = new CopyOnWriteArrayList<>();
public void registerClient(SonarLintClient sonarLintClient) {
}
public void unregisterClient(SonarLintClient client) {
+ client.close();
clients.remove(client);
LOG.debug("Removing SonarLint client");
}
private static Predicate<SonarLintClient> getFilterForEvent(RuleSetChangeEvent ruleChangeEvent) {
List<String> affectedProjects = asList(ruleChangeEvent.getProjects());
- return f -> !Collections.disjoint(f.getClientProjectKeys(), affectedProjects) && f.getLanguages().contains(ruleChangeEvent.getLanguage());
+ return client -> {
+ Set<String> clientProjectKeys = client.getClientProjectKeys();
+ Set<String> languages = client.getLanguages();
+ return !Collections.disjoint(clientProjectKeys, affectedProjects) && languages.contains(ruleChangeEvent.getLanguage());
+ };
}
-
- public void broadcastMessage(RuleSetChangeEvent event, Predicate<SonarLintClient> projectsFilter) {
- clients.stream().filter(projectsFilter).forEach(c -> {
+ public void broadcastMessage(RuleSetChangeEvent message, Predicate<SonarLintClient> filter) {
+ clients.stream().filter(filter).forEach(c -> {
+ Set<String> projectKeysInterestingForClient = new HashSet<>(c.getClientProjectKeys());
+ projectKeysInterestingForClient.retainAll(Set.of(message.getProjects()));
try {
- String jsonString = getJSONString(event);
+ sonarLintClientPermissionsValidator.validateUserCanReceivePushEventForProjects(c.getUserUuid(), projectKeysInterestingForClient);
+ RuleSetChangeEvent personalizedEvent = new RuleSetChangeEvent(projectKeysInterestingForClient.toArray(String[]::new), message.getActivatedRules(),
+ message.getDeactivatedRules());
+ String jsonString = getJSONString(personalizedEvent);
c.writeAndFlush(jsonString);
+ } catch (ForbiddenException forbiddenException) {
+ unregisterClient(c);
} catch (IOException e) {
LOG.error("Unable to send message to a client: " + e.getMessage());
}
@Override
public void onStartAsync(AsyncEvent event) {
- //nothing to do on start
+ // nothing to do on start
}
@Override
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
-import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.project.ProjectDto;
private static final String PROJECT_PARAM_KEY = "projectKeys";
private static final String LANGUAGE_PARAM_KEY = "languages";
private final SonarLintClientsRegistry clientsRegistry;
+ private final SonarLintClientPermissionsValidator permissionsValidator;
private final UserSession userSession;
private final DbClient dbClient;
- public SonarLintPushAction(SonarLintClientsRegistry sonarLintClientRegistry, UserSession userSession, DbClient dbClient) {
+ public SonarLintPushAction(SonarLintClientsRegistry sonarLintClientRegistry, UserSession userSession, DbClient dbClient,
+ SonarLintClientPermissionsValidator permissionsValidator) {
this.clientsRegistry = sonarLintClientRegistry;
this.userSession = userSession;
this.dbClient = dbClient;
+ this.permissionsValidator = permissionsValidator;
}
@Override
ServletResponse servletResponse = (ServletResponse) response;
var params = new SonarLintPushActionParamsValidator(request);
- params.validateProjectsPermissions();
+ params.validateParams();
+
+ permissionsValidator.validateUserCanReceivePushEventForProjects(userSession, params.projectKeys);
if (!isServerSideEventsRequest(servletRequest)) {
servletResponse.stream().setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
AsyncContext asyncContext = servletRequest.startAsync();
asyncContext.setTimeout(0);
- SonarLintClient sonarLintClient = new SonarLintClient(asyncContext, params.getProjectKeys(), params.getLanguages());
+ SonarLintClient sonarLintClient = new SonarLintClient(asyncContext, params.getProjectKeys(), params.getLanguages(), userSession.getUuid());
clientsRegistry.registerClient(sonarLintClient);
}
private final Set<String> projectKeys;
private final Set<String> languages;
- private List<ProjectDto> projectDtos;
-
SonarLintPushActionParamsValidator(Request request) {
this.request = request;
this.projectKeys = parseParam(PROJECT_PARAM_KEY);
return Set.of(paramProjectKeys.trim().split(","));
}
- public void validateProjectsPermissions() {
- if (projectDtos == null) {
- try (DbSession dbSession = dbClient.openSession(false)) {
- projectDtos = dbClient.projectDao().selectProjectsByKeys(dbSession, projectKeys);
- }
+ private void validateParams() {
+ List<ProjectDto> projectDtos;
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ projectDtos = dbClient.projectDao().selectProjectsByKeys(dbSession, projectKeys);
}
if (projectDtos.size() < projectKeys.size() || projectDtos.isEmpty()) {
throw new IllegalArgumentException("Param " + PROJECT_PARAM_KEY + " is invalid.");
}
- for (ProjectDto projectDto : projectDtos) {
- userSession.checkProjectPermission(UserRole.USER, projectDto);
- }
}
+
}
}
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
+import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
private final ServerPushClient underTest = new ServerPushClient(executorService, asyncContext) {};
private final ServletOutputStream outputStream = mock(ServletOutputStream.class);
+ private final ScheduledFuture task = mock(ScheduledFuture.class);
private ServletResponse servletResponse;
@Before
@Test
public void write_exceptionCausesConnectionToClose() throws IOException {
when(servletResponse.getOutputStream()).thenThrow(new IOException("mock exception"));
- ScheduledFuture task = mock(ScheduledFuture.class);
when(executorService.schedule(any(HeartbeatTask.class), anyLong(), any(TimeUnit.class))).thenReturn(task);
underTest.scheduleHeartbeat();
@Test
public void flush_exceptionCausesConnectionToClose() throws IOException {
when(servletResponse.getOutputStream()).thenThrow(new IOException("mock exception"));
- ScheduledFuture task = mock(ScheduledFuture.class);
when(executorService.schedule(any(HeartbeatTask.class), anyLong(), any(TimeUnit.class))).thenReturn(task);
underTest.scheduleHeartbeat();
verify(asyncContext).complete();
}
+
+ @Test
+ public void close_exceptionOnComplete_doesNotThrowException() {
+ when(executorService.schedule(any(HeartbeatTask.class), anyLong(), any(TimeUnit.class))).thenReturn(task);
+ doThrow(new IllegalStateException()).when(asyncContext).complete();
+ underTest.scheduleHeartbeat();
+
+ Assertions.assertThatCode(underTest::close)
+ .doesNotThrowAnyException();
+ }
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.pushapi.sonarlint;
+
+import java.util.List;
+import java.util.Set;
+import org.assertj.core.api.Assertions;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.db.DbClient;
+import org.sonar.db.project.ProjectDao;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.db.user.UserDao;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.user.UserSession;
+import org.sonar.server.user.UserSessionFactory;
+
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class SonarLintClientPermissionsValidatorTest {
+
+ private final static String USER_UUID = "USER_UUID";
+
+ private final Set<String> exampleProjectKeys = Set.of("project1", "project2");
+ private final List<ProjectDto> projectDtos = List.of(mock(ProjectDto.class), mock(ProjectDto.class));
+ private final DbClient dbClient = mock(DbClient.class);
+ private final UserSessionFactory userSessionFactory = mock(UserSessionFactory.class);
+ private final UserDao userDao = mock(UserDao.class);
+ private final ProjectDao projectDao = mock(ProjectDao.class);
+ private final UserSession userSession = mock(UserSession.class);
+
+ private SonarLintClientPermissionsValidator underTest = new SonarLintClientPermissionsValidator(dbClient, userSessionFactory);
+
+ @Before
+ public void before() {
+ when(dbClient.userDao()).thenReturn(userDao);
+ when(dbClient.projectDao()).thenReturn(projectDao);
+ when(userSessionFactory.create(any())).thenReturn(userSession);
+ when(projectDao.selectProjectsByKeys(any(), any())).thenReturn(projectDtos);
+ }
+
+ @Test
+ public void validate_givenUserActivatedAndWithRequiredPermissions_dontThrowException() {
+ UserDto userDto = new UserDto();
+ when(userDao.selectByUuid(any(), any())).thenReturn(userDto);
+ when(userSession.isActive()).thenReturn(true);
+
+ assertThatCode(() -> underTest.validateUserCanReceivePushEventForProjects(USER_UUID, exampleProjectKeys))
+ .doesNotThrowAnyException();
+ }
+
+ @Test
+ public void validate_givenUserNotActivated_throwException() {
+ UserDto userDto = new UserDto();
+ when(userDao.selectByUuid(any(), any())).thenReturn(userDto);
+ when(userSession.isActive()).thenReturn(false);
+
+ assertThrows(ForbiddenException.class,
+ () -> underTest.validateUserCanReceivePushEventForProjects(USER_UUID, exampleProjectKeys));
+ }
+
+ @Test
+ public void validate_givenUserNotGrantedProjectPermissions_throwException() {
+ UserDto userDto = new UserDto();
+ when(userDao.selectByUuid(any(), any())).thenReturn(userDto);
+ when(userSession.isActive()).thenReturn(true);
+ when(userSession.checkProjectPermission(any(), any())).thenThrow(ForbiddenException.class);
+
+ assertThrows(ForbiddenException.class,
+ () -> underTest.validateUserCanReceivePushEventForProjects(USER_UUID, exampleProjectKeys));
+ }
+}
private final AsyncContext firstContext = mock(AsyncContext.class);
private final AsyncContext secondContext = mock(AsyncContext.class);
+ private final String USER_UUID = "userUUID";
+
@Test
public void equals_twoClientsWithSameArgumentsAreEqual() {
- SonarLintClient first = new SonarLintClient(firstContext, Set.of(), Set.of());
- SonarLintClient second = new SonarLintClient(firstContext, Set.of(), Set.of());
+ SonarLintClient first = new SonarLintClient(firstContext, Set.of(), Set.of(), USER_UUID);
+ SonarLintClient second = new SonarLintClient(firstContext, Set.of(), Set.of(), USER_UUID);
assertThat(first).isEqualTo(second);
}
@Test
public void equals_twoClientsWithDifferentAsyncObjects() {
- SonarLintClient first = new SonarLintClient(firstContext, Set.of(), Set.of());
- SonarLintClient second = new SonarLintClient(secondContext, Set.of(), Set.of());
+ SonarLintClient first = new SonarLintClient(firstContext, Set.of(), Set.of(), USER_UUID);
+ SonarLintClient second = new SonarLintClient(secondContext, Set.of(), Set.of(), USER_UUID);
assertThat(first).isNotEqualTo(second);
}
@Test
public void equals_twoClientsWithDifferentLanguages() {
- SonarLintClient first = new SonarLintClient(firstContext, Set.of(), Set.of("java"));
- SonarLintClient second = new SonarLintClient(firstContext, Set.of(), Set.of("cobol"));
+ SonarLintClient first = new SonarLintClient(firstContext, Set.of(), Set.of("java"), USER_UUID);
+ SonarLintClient second = new SonarLintClient(firstContext, Set.of(), Set.of("cobol"), USER_UUID);
assertThat(first).isNotEqualTo(second);
}
@Test
public void equals_twoClientsWithDifferentProjectKeys() {
- SonarLintClient first = new SonarLintClient(firstContext, Set.of("project1", "project2"), Set.of());
- SonarLintClient second = new SonarLintClient(firstContext, Set.of("project1"), Set.of());
+ SonarLintClient first = new SonarLintClient(firstContext, Set.of("project1", "project2"), Set.of(), USER_UUID);
+ SonarLintClient second = new SonarLintClient(firstContext, Set.of("project1"), Set.of(), USER_UUID);
assertThat(first).isNotEqualTo(second);
}
@Test
public void equals_secondClientIsNull() {
- SonarLintClient first = new SonarLintClient(firstContext, Set.of("project1", "project2"), Set.of());
+ SonarLintClient first = new SonarLintClient(firstContext, Set.of("project1", "project2"), Set.of(), USER_UUID);
assertThat(first).isNotEqualTo(null);
}
@Test
public void hashCode_producesSameHashesForEqualObjects() {
- SonarLintClient first = new SonarLintClient(firstContext, Set.of(), Set.of());
- SonarLintClient second = new SonarLintClient(firstContext, Set.of(), Set.of());
+ SonarLintClient first = new SonarLintClient(firstContext, Set.of(), Set.of(), USER_UUID);
+ SonarLintClient second = new SonarLintClient(firstContext, Set.of(), Set.of(), USER_UUID);
assertThat(first).hasSameHashCodeAs(second);
}
import org.mockito.ArgumentCaptor;
import org.sonar.core.util.RuleChange;
import org.sonar.core.util.RuleSetChangeEvent;
+import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.pushapi.qualityprofile.StandaloneRuleActivatorEventsDistributor;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anySet;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
private final AsyncContext defaultAsyncContext = mock(AsyncContext.class);
private final Set<String> exampleKeys = Set.of("project1", "project2", "project3");
-
private final Set<String> languageKeys = Set.of("language1", "language2", "language3");
+ private final String USER_UUID = "userUuid";
+ private final ServletResponse response = mock(ServletResponse.class);
+ private final ServletOutputStream outputStream = mock(ServletOutputStream.class);
+
+ private final SonarLintClientPermissionsValidator permissionsValidator = mock(SonarLintClientPermissionsValidator.class);
private SonarLintClientsRegistry underTest;
@Before
public void before() {
- underTest = new SonarLintClientsRegistry(mock(StandaloneRuleActivatorEventsDistributor.class));
+ underTest = new SonarLintClientsRegistry(mock(StandaloneRuleActivatorEventsDistributor.class), permissionsValidator);
}
@Test
public void registerClientAndUnregister_changesNumberOfClients() {
- SonarLintClient sonarLintClient = new SonarLintClient(defaultAsyncContext, exampleKeys, languageKeys);
+ SonarLintClient sonarLintClient = new SonarLintClient(defaultAsyncContext, exampleKeys, languageKeys, USER_UUID);
underTest.registerClient(sonarLintClient);
public void registering10Clients_10ClientsAreRegistered() {
for (int i = 0; i < 10; i++) {
AsyncContext newAsyncContext = mock(AsyncContext.class);
- SonarLintClient sonarLintClient = new SonarLintClient(newAsyncContext, exampleKeys, languageKeys);
+ SonarLintClient sonarLintClient = new SonarLintClient(newAsyncContext, exampleKeys, languageKeys, USER_UUID);
underTest.registerClient(sonarLintClient);
}
@Test
public void listen_givenOneClientInterestedInJavaEvents_sendOneJavaEvent() throws IOException {
Set<String> javaLanguageKey = Set.of("java");
- ServletResponse response = mock(ServletResponse.class);
when(defaultAsyncContext.getResponse()).thenReturn(response);
- ServletOutputStream outputStream = mock(ServletOutputStream.class);
when(response.getOutputStream()).thenReturn(outputStream);
- SonarLintClient sonarLintClient = new SonarLintClient(defaultAsyncContext, exampleKeys, javaLanguageKey);
+ SonarLintClient sonarLintClient = new SonarLintClient(defaultAsyncContext, exampleKeys, javaLanguageKey, USER_UUID);
underTest.registerClient(sonarLintClient);
@Test
public void listen_givenOneClientInterestedInJsEventsAndJavaEventGenerated_sendZeroEvents() throws IOException {
Set<String> jsLanguageKey = Set.of("js");
- ServletResponse response = mock(ServletResponse.class);
when(defaultAsyncContext.getResponse()).thenReturn(response);
- ServletOutputStream outputStream = mock(ServletOutputStream.class);
when(response.getOutputStream()).thenReturn(outputStream);
- SonarLintClient sonarLintClient = new SonarLintClient(defaultAsyncContext, exampleKeys, jsLanguageKey);
+ SonarLintClient sonarLintClient = new SonarLintClient(defaultAsyncContext, exampleKeys, jsLanguageKey, USER_UUID);
underTest.registerClient(sonarLintClient);
verifyNoInteractions(outputStream);
}
+ @Test
+ public void listen_givenOneClientInterestedInProjA_DontCheckPermissionsForProjB() throws IOException {
+ when(defaultAsyncContext.getResponse()).thenReturn(response);
+ when(response.getOutputStream()).thenReturn(outputStream);
+ Set<String> clientProjectKeys = Set.of("projA");
+ Set<String> eventProjectKeys = Set.of("projA", "projB");
+ SonarLintClient sonarLintClient = new SonarLintClient(defaultAsyncContext, clientProjectKeys, Set.of("java"), USER_UUID);
+
+ underTest.registerClient(sonarLintClient);
+
+ RuleChange javaRuleChange = createRuleChange("java");
+
+ RuleChange[] activatedRules = {};
+ RuleChange[] deactivatedRules = {javaRuleChange};
+ RuleSetChangeEvent ruleChangeEvent = new RuleSetChangeEvent(eventProjectKeys.toArray(String[]::new), activatedRules, deactivatedRules);
+ underTest.listen(ruleChangeEvent);
+
+ ArgumentCaptor<Set<String>> argument = ArgumentCaptor.forClass(Set.class);
+ verify(permissionsValidator).validateUserCanReceivePushEventForProjects(anyString(), argument.capture());
+ assertThat(argument.getValue()).isEqualTo(clientProjectKeys);
+ }
+
+ @Test
+ public void listen_givenUserNotPermittedToReceiveEvent_closeConnection() {
+ RuleChange javaRuleChange = createRuleChange("java");
+ RuleChange[] activatedRules = {};
+ RuleChange[] deactivatedRules = {javaRuleChange};
+ RuleSetChangeEvent ruleChangeEvent = new RuleSetChangeEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules);
+
+ SonarLintClient sonarLintClient = createSampleSLClient();
+ underTest.registerClient(sonarLintClient);
+ doThrow(new ForbiddenException("Access forbidden")).when(permissionsValidator).validateUserCanReceivePushEventForProjects(anyString(), anySet());
+
+ underTest.listen(ruleChangeEvent);
+
+ verify(sonarLintClient).close();
+ }
+
+ private SonarLintClient createSampleSLClient() {
+ SonarLintClient mock = mock(SonarLintClient.class);
+ when(mock.getLanguages()).thenReturn(Set.of("java"));
+ when(mock.getClientProjectKeys()).thenReturn(exampleKeys);
+ when(mock.getUserUuid()).thenReturn("userUuid");
+ return mock;
+ }
+
private RuleChange createRuleChange(String language) {
RuleChange javaRule = new RuleChange();
javaRule.setLanguage(language);
private final UserSession userSession = mock(UserSession.class);
private final DbClient dbClient = mock(DbClient.class);
private final ProjectDao projectDao = mock(ProjectDao.class);
+ private final SonarLintClientPermissionsValidator permissionsValidator = mock(SonarLintClientPermissionsValidator.class);
- private final WsPushActionTester ws = new WsPushActionTester(new SonarLintPushAction(registry, userSession, dbClient));
+ private final WsPushActionTester ws = new WsPushActionTester(new SonarLintPushAction(registry, userSession, dbClient, permissionsValidator));
@Before
public void before() {