]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8607 Changing the type of an issue should require "Administer Issues" permission 1524/head
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 9 Jan 2017 13:31:25 +0000 (14:31 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 12 Jan 2017 14:46:46 +0000 (15:46 +0100)
18 files changed:
server/sonar-server/src/main/java/org/sonar/server/issue/ActionFinder.java
server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java
server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/SetTypeAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/BulkChangeAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SetSeverityAction.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SetTypeAction.java
server/sonar-server/src/test/java/org/sonar/server/issue/ActionFinderTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/SetSeverityActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/SetTypeActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/BulkChangeActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetSeverityActionTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTypeActionTest.java
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/load_additional_fields.json
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/load_additional_fields_with_issue_admin_permission.json [new file with mode: 0644]

index 79476e3f3b0f33ad9e19a6a6da1a9a1052aeea27..32705f15c3d09c11fba9b743a297902fcb87f6cf 100644 (file)
  */
 package org.sonar.server.issue;
 
+import java.util.Collections;
 import java.util.List;
 import org.sonar.db.issue.IssueDto;
 import org.sonar.server.user.UserSession;
 
 import static com.google.common.collect.Lists.newArrayList;
+import static java.util.Objects.requireNonNull;
 import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
+import static org.sonar.server.issue.AssignAction.ASSIGN_KEY;
+import static org.sonar.server.issue.CommentAction.COMMENT_KEY;
+import static org.sonar.server.issue.SetSeverityAction.SET_SEVERITY_KEY;
+import static org.sonar.server.issue.SetTypeAction.SET_TYPE_KEY;
 
-/**
- * @since 3.6
- */
 public class ActionFinder {
 
   private final UserSession userSession;
@@ -40,20 +43,22 @@ public class ActionFinder {
   public List<String> listAvailableActions(IssueDto issue) {
     List<String> availableActions = newArrayList();
     String login = userSession.getLogin();
-    if (login != null) {
-      availableActions.add("comment");
-      if (issue.getResolution() == null) {
-        availableActions.add("assign");
-        availableActions.add("set_tags");
-        availableActions.add("set_type");
-        if (!login.equals(issue.getAssignee())) {
-          availableActions.add("assign_to_me");
-        }
-        String projectUuid = issue.getProjectUuid();
-        if (projectUuid != null && userSession.hasComponentUuidPermission(ISSUE_ADMIN, projectUuid)) {
-          availableActions.add("set_severity");
-        }
-      }
+    if (login == null) {
+      return Collections.emptyList();
+    }
+    availableActions.add(COMMENT_KEY);
+    if (issue.getResolution() != null) {
+      return availableActions;
+    }
+    availableActions.add(ASSIGN_KEY);
+    availableActions.add("set_tags");
+    if (!login.equals(issue.getAssignee())) {
+      // This action will be removed by
+      availableActions.add("assign_to_me");
+    }
+    if (userSession.hasComponentUuidPermission(ISSUE_ADMIN, requireNonNull(issue.getProjectUuid()))) {
+      availableActions.add(SET_TYPE_KEY);
+      availableActions.add(SET_SEVERITY_KEY);
     }
     return availableActions;
   }
index 48605ed27d000c88ea0d53c2a0b1e68c309ea0b8..3be220a9cbecdf2e2b5aaad60758ce04f1f957aa 100644 (file)
@@ -26,11 +26,9 @@ import java.util.List;
 import java.util.Map;
 import javax.annotation.Nullable;
 import org.sonar.api.ce.ComputeEngineSide;
-import org.sonar.api.rules.RuleType;
 import org.sonar.api.server.ServerSide;
 import org.sonar.api.user.User;
 import org.sonar.api.user.UserFinder;
-import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.core.issue.IssueChangeContext;
 import org.sonar.db.DbClient;
@@ -86,40 +84,6 @@ public class IssueService {
     }
   }
 
-  public void setSeverity(String issueKey, String severity) {
-    userSession.checkLoggedIn();
-
-    DbSession session = dbClient.openSession(false);
-    try {
-      DefaultIssue issue = issueFinder.getByKey(session, issueKey).toDefaultIssue();
-      userSession.checkComponentUuidPermission(UserRole.ISSUE_ADMIN, issue.projectUuid());
-
-      IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.getLogin());
-      if (issueFieldsSetter.setManualSeverity(issue, severity, context)) {
-        issueUpdater.saveIssue(session, issue, context, null);
-      }
-    } finally {
-      session.close();
-    }
-  }
-
-  public void setType(String issueKey, RuleType type) {
-    userSession.checkLoggedIn();
-
-    DbSession session = dbClient.openSession(false);
-    try {
-      DefaultIssue issue = issueFinder.getByKey(session, issueKey).toDefaultIssue();
-      userSession.checkComponentUuidPermission(UserRole.ISSUE_ADMIN, issue.projectUuid());
-
-      IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.getLogin());
-      if (issueFieldsSetter.setType(issue, type, context)) {
-        issueUpdater.saveIssue(session, issue, context, null);
-      }
-    } finally {
-      session.close();
-    }
-  }
-
   /**
    * Search for all tags, whatever issue resolution or user access rights
    */
index 620919d45ca0a830c486506a805eda9d6cc0bec2..93feb260275aac8cb81c51f76637c9643a27fbb4 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonar.server.issue;
 
-import com.google.common.base.Strings;
 import java.util.Collection;
 import java.util.Map;
 import org.sonar.api.issue.condition.IsUnResolved;
@@ -28,6 +27,9 @@ import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.server.user.UserSession;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Strings.isNullOrEmpty;
+
 @ServerSide
 public class SetSeverityAction extends Action {
 
@@ -50,20 +52,18 @@ public class SetSeverityAction extends Action {
 
   @Override
   public boolean verify(Map<String, Object> properties, Collection<DefaultIssue> issues, UserSession userSession) {
-    severity(properties);
+    verifySeverityParameter(properties);
     return true;
   }
 
   @Override
   public boolean execute(Map<String, Object> properties, Context context) {
-    return issueUpdater.setManualSeverity(context.issue(), severity(properties), context.issueChangeContext());
+    return issueUpdater.setManualSeverity(context.issue(), verifySeverityParameter(properties), context.issueChangeContext());
   }
 
-  private static String severity(Map<String, Object> properties) {
+  private static String verifySeverityParameter(Map<String, Object> properties) {
     String param = (String) properties.get(SEVERITY_PARAMETER);
-    if (Strings.isNullOrEmpty(param)) {
-      throw new IllegalArgumentException("Missing parameter : 'severity'");
-    }
+    checkArgument(!isNullOrEmpty(param), "Missing parameter : '%s'", SEVERITY_PARAMETER);
     return param;
   }
 }
index d3f0f8e2cc8f868cd56c173f541802868340083b..1913c92da06b0ce30b812adb7a9cf485fb3e04b6 100644 (file)
  */
 package org.sonar.server.issue;
 
-import com.google.common.base.Preconditions;
 import java.util.Collection;
 import java.util.Map;
 import org.sonar.api.issue.condition.IsUnResolved;
 import org.sonar.api.rules.RuleType;
+import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.server.user.UserSession;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Strings.isNullOrEmpty;
 
 public class SetTypeAction extends Action {
@@ -35,29 +36,35 @@ public class SetTypeAction extends Action {
   public static final String TYPE_PARAMETER = "type";
 
   private final IssueFieldsSetter issueUpdater;
+  private final UserSession userSession;
 
-  public SetTypeAction(IssueFieldsSetter issueUpdater) {
+  public SetTypeAction(IssueFieldsSetter issueUpdater, UserSession userSession) {
     super(SET_TYPE_KEY);
     this.issueUpdater = issueUpdater;
-    super.setConditions(new IsUnResolved());
+    this.userSession = userSession;
+    super.setConditions(new IsUnResolved(), issue -> isCurrentUserIssueAdmin(issue.projectUuid()));
+  }
+
+  private boolean isCurrentUserIssueAdmin(String projectUuid) {
+    return userSession.hasComponentUuidPermission(UserRole.ISSUE_ADMIN, projectUuid);
   }
 
   @Override
   public boolean verify(Map<String, Object> properties, Collection<DefaultIssue> issues, UserSession userSession) {
-    newValue(properties);
+    verifyTypeParameter(properties);
     return true;
   }
 
   @Override
   public boolean execute(Map<String, Object> properties, Context context) {
-    String type = newValue(properties);
+    String type = verifyTypeParameter(properties);
     return issueUpdater.setType(context.issue(), RuleType.valueOf(type), context.issueChangeContext());
   }
 
-  private static String newValue(Map<String, Object> properties) {
+  private static String verifyTypeParameter(Map<String, Object> properties) {
     String type = (String) properties.get(TYPE_PARAMETER);
-    Preconditions.checkArgument(!isNullOrEmpty(type), "Missing parameter: '%s'", TYPE_PARAMETER);
-    Preconditions.checkArgument(RuleType.names().contains(type), "Unknown type: %s", type);
+    checkArgument(!isNullOrEmpty(type), "Missing parameter : '%s'", TYPE_PARAMETER);
+    checkArgument(RuleType.names().contains(type), "Unknown type : %s", type);
     return type;
   }
 }
index f29e8ce5b84d861c9352754aeb689e501ed6db19..5214b3285faf4ba4169bd5105de6b601e8cc81a0 100644 (file)
@@ -256,11 +256,11 @@ public class BulkChangeAction implements IssuesWsAction {
       .build();
   }
 
-  private static class ActionContext implements Action.Context {
+  public static class ActionContext implements Action.Context {
     private final DefaultIssue issue;
     private final IssueChangeContext changeContext;
 
-    ActionContext(DefaultIssue issue, IssueChangeContext changeContext) {
+    public ActionContext(DefaultIssue issue, IssueChangeContext changeContext) {
       this.issue = issue;
       this.changeContext = changeContext;
     }
index 5486d7dd0bc5f99737ab4d2da344491b9acaa09c..929ac86b2a5acfa5d37ee0a7d2569d5dcdc8f4b2 100644 (file)
 package org.sonar.server.issue.ws;
 
 import com.google.common.io.Resources;
+import java.util.Date;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
 import org.sonar.core.util.Uuids;
-import org.sonar.server.issue.IssueService;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.server.issue.IssueFieldsSetter;
+import org.sonar.server.issue.IssueFinder;
+import org.sonar.server.issue.IssueUpdater;
+import org.sonar.server.user.UserSession;
 
+import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.ACTION_SET_SEVERITY;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ISSUE;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SEVERITY;
 
 public class SetSeverityAction implements IssuesWsAction {
 
-  private final IssueService issueService;
+  private final UserSession userSession;
+  private final DbClient dbClient;
+  private final IssueFinder issueFinder;
+  private final IssueFieldsSetter issueFieldsSetter;
+  private final IssueUpdater issueUpdater;
   private final OperationResponseWriter responseWriter;
 
-  public SetSeverityAction(IssueService issueService, OperationResponseWriter responseWriter) {
-    this.issueService = issueService;
+  public SetSeverityAction(UserSession userSession, DbClient dbClient, IssueFinder issueFinder, IssueFieldsSetter issueFieldsSetter, IssueUpdater issueUpdater,
+    OperationResponseWriter responseWriter) {
+    this.userSession = userSession;
+    this.dbClient = dbClient;
+    this.issueFinder = issueFinder;
+    this.issueFieldsSetter = issueFieldsSetter;
+    this.issueUpdater = issueUpdater;
     this.responseWriter = responseWriter;
   }
 
   @Override
   public void define(WebService.NewController controller) {
     WebService.NewAction action = controller.createAction(ACTION_SET_SEVERITY)
-      .setDescription("Change severity. Requires authentication and Browse permission on project")
+      .setDescription("Change severity.<br/>" +
+        "Requires the following permissions:" +
+        "<ul>" +
+        "  <li>'Authentication'</li>" +
+        "  <li>'Browse' rights on project of the specified issue</li>" +
+        "  <li>'Administer Issues' rights on project of the specified issue</li>" +
+        "</ul>")
       .setSince("3.6")
       .setHandler(this)
       .setResponseExample(Resources.getResource(this.getClass(), "set_severity-example.json"))
@@ -62,9 +86,22 @@ public class SetSeverityAction implements IssuesWsAction {
 
   @Override
   public void handle(Request request, Response response) throws Exception {
-    String key = request.mandatoryParam(PARAM_ISSUE);
-    issueService.setSeverity(key, request.mandatoryParam(PARAM_SEVERITY));
+    userSession.checkLoggedIn();
+    String issueKey = request.mandatoryParam(PARAM_ISSUE);
+    String severity = request.mandatoryParam(PARAM_SEVERITY);
+    try (DbSession session = dbClient.openSession(false)) {
+      setType(session, issueKey, severity);
+    }
+    responseWriter.write(issueKey, request, response);
+  }
+
+  private void setType(DbSession session, String issueKey, String severity) {
+    DefaultIssue issue = issueFinder.getByKey(session, issueKey).toDefaultIssue();
+    userSession.checkComponentUuidPermission(ISSUE_ADMIN, issue.projectUuid());
 
-    responseWriter.write(key, request, response);
+    IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.getLogin());
+    if (issueFieldsSetter.setManualSeverity(issue, severity, context)) {
+      issueUpdater.saveIssue(session, issue, context, null);
+    }
   }
 }
index 5f860e8484dd774dfd9c068eaa0d8169a633d78f..df38a3dc447e295e8fc747f1c8e3a6580d0fe4d0 100644 (file)
 package org.sonar.server.issue.ws;
 
 import com.google.common.io.Resources;
+import java.util.Date;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
 import org.sonar.core.util.Uuids;
-import org.sonar.server.issue.IssueService;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.server.issue.IssueFieldsSetter;
+import org.sonar.server.issue.IssueFinder;
+import org.sonar.server.issue.IssueUpdater;
+import org.sonar.server.user.UserSession;
 
+import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.ACTION_SET_TYPE;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ISSUE;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TYPE;
 
 public class SetTypeAction implements IssuesWsAction {
 
-  private final IssueService issueService;
+  private final UserSession userSession;
+  private final DbClient dbClient;
+  private final IssueFinder issueFinder;
+  private final IssueFieldsSetter issueFieldsSetter;
+  private final IssueUpdater issueUpdater;
   private final OperationResponseWriter responseWriter;
 
-  public SetTypeAction(IssueService issueService, OperationResponseWriter responseWriter) {
-    this.issueService = issueService;
+  public SetTypeAction(UserSession userSession, DbClient dbClient, IssueFinder issueFinder, IssueFieldsSetter issueFieldsSetter, IssueUpdater issueUpdater,
+    OperationResponseWriter responseWriter) {
+    this.userSession = userSession;
+    this.dbClient = dbClient;
+    this.issueFinder = issueFinder;
+    this.issueFieldsSetter = issueFieldsSetter;
+    this.issueUpdater = issueUpdater;
     this.responseWriter = responseWriter;
   }
 
   @Override
   public void define(WebService.NewController controller) {
     WebService.NewAction action = controller.createAction(ACTION_SET_TYPE)
-      .setDescription("Change type of issue, for instance from 'code smell' to 'bug'. Requires authentication and Browse permission on project.")
+      .setDescription("Change type of issue, for instance from 'code smell' to 'bug'.<br/>" +
+        "Requires the following permissions:" +
+        "<ul>" +
+        "  <li>'Authentication'</li>" +
+        "  <li>'Browse' rights on project of the specified issue</li>" +
+        "  <li>'Administer Issues' rights on project of the specified issue</li>" +
+        "</ul>")
       .setSince("5.5")
       .setHandler(this)
       .setResponseExample(Resources.getResource(this.getClass(), "set_type-example.json"))
@@ -62,9 +86,23 @@ public class SetTypeAction implements IssuesWsAction {
 
   @Override
   public void handle(Request request, Response response) throws Exception {
-    String key = request.mandatoryParam(PARAM_ISSUE);
-    issueService.setType(key, RuleType.valueOf(request.mandatoryParam(PARAM_TYPE)));
+    userSession.checkLoggedIn();
+    String issueKey = request.mandatoryParam(PARAM_ISSUE);
+    RuleType ruleType = RuleType.valueOf(request.mandatoryParam(PARAM_TYPE));
+    try (DbSession session = dbClient.openSession(false)) {
+      setType(session, issueKey, ruleType);
+    }
+    responseWriter.write(issueKey, request, response);
+  }
+
+  private void setType(DbSession session, String issueKey, RuleType ruleType) {
+    DefaultIssue issue = issueFinder.getByKey(session, issueKey).toDefaultIssue();
+    userSession.checkComponentUuidPermission(ISSUE_ADMIN, issue.projectUuid());
 
-    responseWriter.write(key, request, response);
+    IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.getLogin());
+    if (issueFieldsSetter.setType(issue, ruleType, context)) {
+      issueUpdater.saveIssue(session, issue, context, null);
+    }
   }
+
 }
index fd7b4c1380ad3bac96a76d3d5e3746b1d2871562..21c7fb0907fa60077fa48db36884f2ef8e492aea 100644 (file)
@@ -53,12 +53,12 @@ public class ActionFinderTest {
   private ActionFinder underTest = new ActionFinder(userSession);
 
   @Test
-  public void return_provided_actions_without_set_severity_when_not_issue_admin() {
-    assertThat(underTest.listAvailableActions(issue)).containsOnly("comment", "assign", "set_tags", "set_type", "assign_to_me");
+  public void return_provided_actions_without_set_severity_and_set_tpye_when_not_issue_admin() {
+    assertThat(underTest.listAvailableActions(issue)).containsOnly("comment", "assign", "set_tags", "assign_to_me");
   }
 
   @Test
-  public void return_provided_actions_with_set_severity_when_issue_admin() {
+  public void return_provided_actions_with_set_severity_and_set_type_when_issue_admin() {
     userSession.addProjectUuidPermissions(ISSUE_ADMIN, PROJECT_UUID);
     assertThat(underTest.listAvailableActions(issue)).containsOnly("comment", "assign", "set_tags", "set_type", "assign_to_me", "set_severity");
   }
index bf226c06faf3e816c80869928fdb374053cd8f59..e8cc3544714faf55d4ecfa1325e461208b85d455 100644 (file)
@@ -126,8 +126,9 @@ public class AssignActionTest {
   }
 
   @Test
-  public void should_support_only_unresolved_issues() {
+  public void support_only_unresolved_issues() {
     assertThat(action.supports(new DefaultIssue().setResolution(null))).isTrue();
     assertThat(action.supports(new DefaultIssue().setResolution(Issue.RESOLUTION_FIXED))).isFalse();
   }
+
 }
index d9fefa240387ac8cefa66a3b4edcc96ee604c58a..64a7ef883529f8208878cb2fcb0a26e62b49fde3 100644 (file)
@@ -27,8 +27,6 @@ import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.issue.Issue;
-import org.sonar.api.rule.Severity;
-import org.sonar.api.rules.RuleType;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.permission.GlobalPermissions;
 import org.sonar.db.DbClient;
@@ -152,42 +150,6 @@ public class IssueServiceMediumTest {
     }
   }
 
-  @Test
-  public void set_severity() {
-    RuleDto rule = newRule();
-    ComponentDto project = newProject();
-    ComponentDto file = newFile(project);
-    userSessionRule.login("john")
-      .addProjectUuidPermissions(UserRole.USER, project.uuid())
-      .addProjectUuidPermissions(UserRole.ISSUE_ADMIN, project.uuid());
-
-    IssueDto issue = saveIssue(IssueTesting.newDto(rule, file, project).setSeverity(Severity.BLOCKER));
-
-    assertThat(issueIndex.getByKey(issue.getKey()).severity()).isEqualTo(Severity.BLOCKER);
-
-    service.setSeverity(issue.getKey(), Severity.MINOR);
-
-    assertThat(issueIndex.getByKey(issue.getKey()).severity()).isEqualTo(Severity.MINOR);
-  }
-
-  @Test
-  public void set_type() {
-    RuleDto rule = newRule();
-    ComponentDto project = newProject();
-    ComponentDto file = newFile(project);
-    userSessionRule.login("john")
-      .addProjectUuidPermissions(UserRole.USER, project.uuid())
-      .addProjectUuidPermissions(UserRole.ISSUE_ADMIN, project.uuid());
-
-    IssueDto issue = saveIssue(IssueTesting.newDto(rule, file, project).setType(RuleType.CODE_SMELL));
-
-    assertThat(issueIndex.getByKey(issue.getKey()).type()).isEqualTo(RuleType.CODE_SMELL);
-
-    service.setType(issue.getKey(), RuleType.BUG);
-
-    assertThat(issueIndex.getByKey(issue.getKey()).type()).isEqualTo(RuleType.BUG);
-  }
-
   @Test
   public void list_tags() {
     RuleDto rule = newRule();
index fef895b2b4652dec4def2bf40d452306802e6c0d..c23858d3f81a2675a2c497774702a8c65bd88d6b 100644 (file)
  */
 package org.sonar.server.issue;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
+import java.util.Date;
 import java.util.Map;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.sonar.api.issue.Issue;
-import org.sonar.api.web.UserRole;
 import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.FieldDiffs;
 import org.sonar.core.issue.IssueChangeContext;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.issue.IssueDto;
+import org.sonar.db.rule.RuleDto;
+import org.sonar.server.issue.ws.BulkChangeAction;
 import org.sonar.server.tester.AnonymousMockUserSession;
 import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.user.UserSession;
 
-import static com.google.common.collect.Maps.newHashMap;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
+import static org.sonar.api.rule.Severity.MAJOR;
+import static org.sonar.api.rule.Severity.MINOR;
+import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
+import static org.sonar.api.web.UserRole.USER;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.issue.IssueTesting.newDto;
+import static org.sonar.db.rule.RuleTesting.newRuleDto;
 
 public class SetSeverityActionTest {
 
+  private static final Date NOW = new Date(10_000_000_000L);
+  private static final String USER_LOGIN = "john";
+
   @Rule
-  public UserSessionRule userSessionRule = UserSessionRule.standalone();
+  public ExpectedException expectedException = ExpectedException.none();
 
-  private UserSession userSessionMock = mock(UserSession.class);
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
 
-  private SetSeverityAction action;
+  @Rule
+  public DbTester db = DbTester.create();
 
-  private IssueFieldsSetter issueUpdater = mock(IssueFieldsSetter.class);
+  private IssueFieldsSetter issueUpdater = new IssueFieldsSetter();
 
-  @Before
-  public void before() {
-    action = new SetSeverityAction(issueUpdater, userSessionRule);
-    userSessionRule.set(userSessionMock);
-  }
+  private SetSeverityAction action = new SetSeverityAction(issueUpdater, userSession);
 
   @Test
-  public void should_execute() {
-    String severity = "MINOR";
-    Map<String, Object> properties = newHashMap();
-    properties.put("severity", severity);
-    DefaultIssue issue = mock(DefaultIssue.class);
+  public void set_severity() {
+    DefaultIssue issue = newIssue().setSeverity(MAJOR).toDefaultIssue();
+    setUserWithBrowseAndAdministerIssuePermission(issue.projectUuid());
+    BulkChangeAction.ActionContext context = new BulkChangeAction.ActionContext(issue, IssueChangeContext.createUser(NOW, userSession.getLogin()));
 
-    Action.Context context = mock(Action.Context.class);
-    when(context.issue()).thenReturn(issue);
+    action.execute(ImmutableMap.of("severity", MINOR), context);
 
-    action.execute(properties, context);
-    verify(issueUpdater).setManualSeverity(eq(issue), eq(severity), any(IssueChangeContext.class));
+    assertThat(issue.severity()).isEqualTo(MINOR);
+    assertThat(issue.isChanged()).isTrue();
+    assertThat(issue.manualSeverity()).isTrue();
+    assertThat(issue.updateDate()).isEqualTo(NOW);
+    assertThat(issue.mustSendNotifications()).isTrue();
+    Map<String, FieldDiffs.Diff> change = issue.currentChange().diffs();
+    assertThat(change.get("severity").newValue()).isEqualTo(MINOR);
+    assertThat(change.get("severity").oldValue()).isEqualTo(MAJOR);
   }
 
   @Test
-  public void should_verify_fail_if_parameter_not_found() {
-    Map<String, Object> properties = newHashMap();
-    properties.put("unknwown", "unknown value");
-    try {
-      action.verify(properties, Lists.newArrayList(), new AnonymousMockUserSession());
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Missing parameter : 'severity'");
-    }
-    verifyZeroInteractions(issueUpdater);
+  public void fail_if_parameter_not_found() {
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Missing parameter : 'severity'");
+
+    action.verify(ImmutableMap.of("unknwown", MINOR), Lists.newArrayList(), new AnonymousMockUserSession());
   }
 
   @Test
-  public void should_support_only_unresolved_issues() {
-    when(userSessionMock.hasComponentUuidPermission(UserRole.ISSUE_ADMIN, "foo:bar")).thenReturn(true);
-    assertThat(action.supports(new DefaultIssue().setProjectUuid("foo:bar").setResolution(null))).isTrue();
-    assertThat(action.supports(new DefaultIssue().setProjectUuid("foo:bar").setResolution(Issue.RESOLUTION_FIXED))).isFalse();
+  public void support_only_unresolved_issues() {
+    DefaultIssue issue = newIssue().setSeverity(MAJOR).toDefaultIssue();
+    setUserWithBrowseAndAdministerIssuePermission(issue.projectUuid());
+
+    assertThat(action.supports(issue.setResolution(null))).isTrue();
+    assertThat(action.supports(issue.setResolution(Issue.RESOLUTION_FIXED))).isFalse();
   }
 
   @Test
-  public void should_support_only_issues_with_issue_admin_permission() {
-    when(userSessionMock.hasComponentUuidPermission(UserRole.ISSUE_ADMIN, "foo:bar")).thenReturn(true);
-    assertThat(action.supports(new DefaultIssue().setProjectUuid("foo:bar").setResolution(null))).isTrue();
-    assertThat(action.supports(new DefaultIssue().setProjectUuid("foo:bar2").setResolution(null))).isFalse();
+  public void support_only_issues_with_issue_admin_permission() {
+    DefaultIssue authorizedIssue = newIssue().setSeverity(MAJOR).toDefaultIssue();
+    setUserWithBrowseAndAdministerIssuePermission(authorizedIssue.projectUuid());
+    DefaultIssue unauthorizedIssue = newIssue().setSeverity(MAJOR).toDefaultIssue();
+
+    assertThat(action.supports(authorizedIssue.setResolution(null))).isTrue();
+    assertThat(action.supports(unauthorizedIssue.setResolution(null))).isFalse();
+  }
+
+  private void setUserWithBrowseAndAdministerIssuePermission(String projectUuid) {
+    userSession.login(USER_LOGIN)
+      .addProjectUuidPermissions(ISSUE_ADMIN, projectUuid)
+      .addProjectUuidPermissions(USER, projectUuid);
+  }
+
+  private IssueDto newIssue() {
+    RuleDto rule = db.rules().insertRule(newRuleDto());
+    ComponentDto project = db.components().insertProject();
+    ComponentDto file = db.components().insertComponent(newFileDto(project));
+    return newDto(rule, file, project);
   }
 
 }
index 1a6517e9e364bf892ec442f834869fc81d6ccf93..50bcc5e619b23fd2a958e1e966329acb4bf9de2a 100644 (file)
  */
 package org.sonar.server.issue;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
+import java.util.Date;
 import java.util.Map;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.sonar.api.issue.Issue;
-import org.sonar.api.rules.RuleType;
 import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.FieldDiffs;
 import org.sonar.core.issue.IssueChangeContext;
-import org.sonar.server.tester.AnonymousMockUserSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.issue.IssueDto;
+import org.sonar.db.rule.RuleDto;
+import org.sonar.server.issue.ws.BulkChangeAction;
 import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.user.UserSession;
 
-import static com.google.common.collect.Maps.newHashMap;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
+import static org.sonar.api.rules.RuleType.BUG;
+import static org.sonar.api.rules.RuleType.VULNERABILITY;
+import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
+import static org.sonar.api.web.UserRole.USER;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.issue.IssueTesting.newDto;
+import static org.sonar.db.rule.RuleTesting.newRuleDto;
 
 public class SetTypeActionTest {
 
+  private static final Date NOW = new Date(10_000_000_000L);
+  private static final String USER_LOGIN = "john";
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
+
   @Rule
-  public UserSessionRule userSessionRule = UserSessionRule.standalone();
+  public DbTester db = DbTester.create();
+
+  private IssueFieldsSetter issueUpdater = new IssueFieldsSetter();
+
+  private SetTypeAction action = new SetTypeAction(issueUpdater, userSession);
+
+  @Test
+  public void set_type() {
+    DefaultIssue issue = newIssue().setType(BUG).toDefaultIssue();
+    setUserWithBrowseAndAdministerIssuePermission(issue.projectUuid());
 
-  UserSession userSessionMock = mock(UserSession.class);
-  IssueFieldsSetter issueUpdater = mock(IssueFieldsSetter.class);
-  SetTypeAction underTest;
+    action.execute(ImmutableMap.of("type", VULNERABILITY.name()), new BulkChangeAction.ActionContext(issue, IssueChangeContext.createUser(NOW, userSession.getLogin())));
 
-  @Before
-  public void before() {
-    underTest = new SetTypeAction(issueUpdater);
-    userSessionRule.set(userSessionMock);
+    assertThat(issue.type()).isEqualTo(VULNERABILITY);
+    assertThat(issue.isChanged()).isTrue();
+    assertThat(issue.updateDate()).isEqualTo(NOW);
+    assertThat(issue.mustSendNotifications()).isFalse();
+    Map<String, FieldDiffs.Diff> change = issue.currentChange().diffs();
+    assertThat(change.get("type").newValue()).isEqualTo(VULNERABILITY);
+    assertThat(change.get("type").oldValue()).isEqualTo(BUG);
   }
 
   @Test
-  public void should_execute() {
-    String type = "BUG";
-    Map<String, Object> properties = newHashMap();
-    properties.put("type", "BUG");
-    DefaultIssue issue = mock(DefaultIssue.class);
+  public void verify_fail_if_parameter_not_found() {
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Missing parameter : 'type'");
 
-    Action.Context context = mock(Action.Context.class);
-    when(context.issue()).thenReturn(issue);
+    action.verify(ImmutableMap.of("unknwown", VULNERABILITY.name()), Lists.newArrayList(), userSession);
+  }
+
+  @Test
+  public void verify_fail_if_type_is_invalid() {
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Unknown type : unknown");
 
-    underTest.execute(properties, context);
-    verify(issueUpdater).setType(eq(issue), eq(RuleType.BUG), any(IssueChangeContext.class));
+    action.verify(ImmutableMap.of("type", "unknown"), Lists.newArrayList(), userSession);
   }
 
   @Test
-  public void should_verify_fail_if_parameter_not_found() {
-    Map<String, Object> properties = newHashMap();
-    try {
-      underTest.verify(properties, Lists.newArrayList(), new AnonymousMockUserSession());
-      fail();
-    } catch (Exception e) {
-      assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Missing parameter: 'type'");
-    }
-    verifyZeroInteractions(issueUpdater);
+  public void support_only_unresolved_issues() {
+    DefaultIssue issue = newIssue().setType(BUG).toDefaultIssue();
+    setUserWithBrowseAndAdministerIssuePermission(issue.projectUuid());
+
+    assertThat(action.supports(issue.setResolution(null))).isTrue();
+    assertThat(action.supports(issue.setResolution(Issue.RESOLUTION_FIXED))).isFalse();
   }
 
   @Test
-  public void should_support_only_unresolved_issues() {
-    assertThat(underTest.supports(new DefaultIssue().setResolution(null))).isTrue();
-    assertThat(underTest.supports(new DefaultIssue().setResolution(Issue.RESOLUTION_FIXED))).isFalse();
+  public void support_only_issues_with_issue_admin_permission() {
+    DefaultIssue authorizedIssue = newIssue().setType(BUG).toDefaultIssue();
+    setUserWithBrowseAndAdministerIssuePermission(authorizedIssue.projectUuid());
+    DefaultIssue unauthorizedIssue = newIssue().setType(BUG).toDefaultIssue();
+
+    assertThat(action.supports(authorizedIssue.setResolution(null))).isTrue();
+    assertThat(action.supports(unauthorizedIssue.setResolution(null))).isFalse();
+  }
+
+  private void setUserWithBrowseAndAdministerIssuePermission(String projectUuid) {
+    userSession.login(USER_LOGIN)
+      .addProjectUuidPermissions(ISSUE_ADMIN, projectUuid)
+      .addProjectUuidPermissions(USER, projectUuid);
+  }
+
+  private IssueDto newIssue() {
+    RuleDto rule = db.rules().insertRule(newRuleDto());
+    ComponentDto project = db.components().insertProject();
+    ComponentDto file = db.components().insertComponent(newFileDto(project));
+    return newDto(rule, file, project);
   }
 
 }
index 1c50b22773cd38e2135960a7ce3c8b7543489157..f44f0f643fae30957b61ca3edafe78e22668d4ee 100644 (file)
@@ -107,6 +107,7 @@ public class BulkChangeActionTest {
   private DbClient dbClient = db.getDbClient();
 
   private IssueFieldsSetter issueFieldsSetter = new IssueFieldsSetter();
+  private IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter);
   private IssueStorage issueStorage = new ServerIssueStorage(system2, new DefaultRuleFinder(dbClient), dbClient, new IssueIndexer(system2, dbClient, es.client()));
   private NotificationManager notificationManager = mock(NotificationManager.class);
   private List<Action> actions = new ArrayList<>();
@@ -120,6 +121,7 @@ public class BulkChangeActionTest {
 
   @Before
   public void setUp() throws Exception {
+    issueWorkflow.start();
     rule = db.rules().insertRule(newRuleDto());
     project = db.components().insertProject();
     file = db.components().insertComponent(newFileDto(project));
@@ -130,7 +132,7 @@ public class BulkChangeActionTest {
 
   @Test
   public void set_type() throws Exception {
-    setUserPermissions(USER);
+    setUserProjectPermissions(USER, ISSUE_ADMIN);
     IssueDto issueDto = db.issues().insertIssue(newUnresolvedIssue().setType(BUG));
 
     BulkChangeWsResponse response = call(BulkChangeRequest.builder()
@@ -146,7 +148,7 @@ public class BulkChangeActionTest {
 
   @Test
   public void set_severity() throws Exception {
-    setUserPermissions(USER, ISSUE_ADMIN);
+    setUserProjectPermissions(USER, ISSUE_ADMIN);
     IssueDto issueDto = db.issues().insertIssue(newUnresolvedIssue().setSeverity(MAJOR));
 
     BulkChangeWsResponse response = call(BulkChangeRequest.builder()
@@ -162,7 +164,7 @@ public class BulkChangeActionTest {
 
   @Test
   public void add_tags() throws Exception {
-    setUserPermissions(USER, ISSUE_ADMIN);
+    setUserProjectPermissions(USER, ISSUE_ADMIN);
     IssueDto issueDto = db.issues().insertIssue(newUnresolvedIssue().setTags(asList("tag1", "tag2")));
 
     BulkChangeWsResponse response = call(BulkChangeRequest.builder()
@@ -178,7 +180,7 @@ public class BulkChangeActionTest {
 
   @Test
   public void remove_assignee() throws Exception {
-    setUserPermissions(USER);
+    setUserProjectPermissions(USER);
     IssueDto issueDto = db.issues().insertIssue(newUnresolvedIssue().setAssignee("arthur"));
 
     BulkChangeWsResponse response = call(BulkChangeRequest.builder()
@@ -194,12 +196,12 @@ public class BulkChangeActionTest {
 
   @Test
   public void bulk_change_with_comment() throws Exception {
-    setUserPermissions(USER);
+    setUserProjectPermissions(USER);
     IssueDto issueDto = db.issues().insertIssue(newUnresolvedIssue().setType(BUG));
 
     BulkChangeWsResponse response = call(BulkChangeRequest.builder()
       .setIssues(singletonList(issueDto.getKey()))
-      .setSetType(RuleType.CODE_SMELL.name())
+      .setDoTransition("confirm")
       .setComment("type was badly defined")
       .build());
 
@@ -211,7 +213,7 @@ public class BulkChangeActionTest {
 
   @Test
   public void bulk_change_many_issues() throws Exception {
-    setUserPermissions(USER, ISSUE_ADMIN);
+    setUserProjectPermissions(USER, ISSUE_ADMIN);
     UserDto userToAssign = db.users().insertUser("arthur");
     IssueDto issue1 = db.issues().insertIssue(newUnresolvedIssue().setAssignee(user.getLogin())).setType(BUG).setSeverity(MINOR);
     IssueDto issue2 = db.issues().insertIssue(newUnresolvedIssue().setAssignee(userToAssign.getLogin())).setType(BUG).setSeverity(MAJOR);
@@ -235,13 +237,13 @@ public class BulkChangeActionTest {
 
   @Test
   public void send_notification() throws Exception {
-    setUserPermissions(USER);
+    setUserProjectPermissions(USER);
     IssueDto issueDto = db.issues().insertIssue(newUnresolvedIssue().setType(BUG));
     ArgumentCaptor<IssueChangeNotification> issueChangeNotificationCaptor = ArgumentCaptor.forClass(IssueChangeNotification.class);
 
     BulkChangeWsResponse response = call(BulkChangeRequest.builder()
       .setIssues(singletonList(issueDto.getKey()))
-      .setSetType(RuleType.CODE_SMELL.name())
+      .setDoTransition("confirm")
       .setSendNotifications(true)
       .build());
 
@@ -257,7 +259,7 @@ public class BulkChangeActionTest {
 
   @Test
   public void send_notification_only_on_changed_issues() throws Exception {
-    setUserPermissions(USER);
+    setUserProjectPermissions(USER, ISSUE_ADMIN);
     IssueDto issue1 = db.issues().insertIssue(newUnresolvedIssue().setType(BUG));
     IssueDto issue2 = db.issues().insertIssue(newUnresolvedIssue().setType(BUG));
     IssueDto issue3 = db.issues().insertIssue(newUnresolvedIssue().setType(VULNERABILITY));
@@ -277,7 +279,7 @@ public class BulkChangeActionTest {
 
   @Test
   public void ignore_issues_when_condition_does_not_match() throws Exception {
-    setUserPermissions(USER);
+    setUserProjectPermissions(USER, ISSUE_ADMIN);
     IssueDto issue1 = db.issues().insertIssue(newUnresolvedIssue().setType(BUG));
     // These 2 issues will be ignored as they are resolved, changing type is not possible
     IssueDto issue2 = db.issues().insertIssue(newResolvedIssue().setType(BUG));
@@ -299,7 +301,7 @@ public class BulkChangeActionTest {
 
   @Test
   public void ignore_issues_when_there_is_nothing_to_do() throws Exception {
-    setUserPermissions(USER);
+    setUserProjectPermissions(USER, ISSUE_ADMIN);
     IssueDto issue1 = db.issues().insertIssue(newUnresolvedIssue().setType(BUG).setSeverity(MINOR));
     // These 2 issues will be ignored as there's nothing to do
     IssueDto issue2 = db.issues().insertIssue(newUnresolvedIssue().setType(VULNERABILITY));
@@ -321,7 +323,7 @@ public class BulkChangeActionTest {
 
   @Test
   public void add_comment_only_on_changed_issues() throws Exception {
-    setUserPermissions(USER);
+    setUserProjectPermissions(USER, ISSUE_ADMIN);
     IssueDto issue1 = db.issues().insertIssue(newUnresolvedIssue().setType(BUG).setSeverity(MINOR));
     // These 2 issues will be ignored as there's nothing to do
     IssueDto issue2 = db.issues().insertIssue(newUnresolvedIssue().setType(VULNERABILITY));
@@ -340,12 +342,12 @@ public class BulkChangeActionTest {
   }
 
   @Test
-  public void not_authorized_issues_are_not_taking_into_account() throws Exception {
-    setUserPermissions(USER);
+  public void issues_on_which_user_has_not_browse_permission_are_ignored() throws Exception {
+    setUserProjectPermissions(USER, ISSUE_ADMIN);
     ComponentDto anotherProject = db.components().insertProject();
     ComponentDto anotherFile = db.components().insertComponent(newFileDto(anotherProject));
     IssueDto authorizedIssue = db.issues().insertIssue(newUnresolvedIssue(rule, file, project).setType(BUG));
-    // User has no permission on these 2 issues
+    // User has not browse permission on these 2 issues
     IssueDto notAuthorizedIssue1 = db.issues().insertIssue(newUnresolvedIssue(rule, anotherFile, anotherProject).setType(BUG));
     IssueDto notAuthorizedIssue2 = db.issues().insertIssue(newUnresolvedIssue(rule, anotherFile, anotherProject).setType(BUG));
 
@@ -363,9 +365,61 @@ public class BulkChangeActionTest {
         tuple(notAuthorizedIssue2.getKey(), BUG.getDbConstant(), notAuthorizedIssue2.getUpdatedAt()));
   }
 
+  @Test
+  public void does_not_update_type_when_no_issue_admin_permission() throws Exception {
+    setUserProjectPermissions(USER, ISSUE_ADMIN);
+    ComponentDto anotherProject = db.components().insertProject();
+    ComponentDto anotherFile = db.components().insertComponent(newFileDto(anotherProject));
+    addUserProjectPermissions(anotherProject, USER);
+
+    IssueDto authorizedIssue1 = db.issues().insertIssue(newUnresolvedIssue().setType(BUG));
+    // User has not issue admin permission on these 2 issues
+    IssueDto notAuthorizedIssue1 = db.issues().insertIssue(newUnresolvedIssue(rule, anotherFile, anotherProject).setType(BUG));
+    IssueDto notAuthorizedIssue2 = db.issues().insertIssue(newUnresolvedIssue(rule, anotherFile, anotherProject).setType(BUG));
+
+    BulkChangeWsResponse response = call(BulkChangeRequest.builder()
+      .setIssues(asList(authorizedIssue1.getKey(), notAuthorizedIssue1.getKey(), notAuthorizedIssue2.getKey()))
+      .setSetType(VULNERABILITY.name())
+      .build());
+
+    checkResponse(response, 3, 1, 2, 0);
+    assertThat(getIssueByKeys(authorizedIssue1.getKey(), notAuthorizedIssue1.getKey(), notAuthorizedIssue2.getKey()))
+      .extracting(IssueDto::getKey, IssueDto::getType, IssueDto::getUpdatedAt)
+      .containsOnly(
+        tuple(authorizedIssue1.getKey(), VULNERABILITY.getDbConstant(), NOW),
+        tuple(notAuthorizedIssue1.getKey(), BUG.getDbConstant(), notAuthorizedIssue1.getUpdatedAt()),
+        tuple(notAuthorizedIssue2.getKey(), BUG.getDbConstant(), notAuthorizedIssue2.getUpdatedAt()));
+  }
+
+  @Test
+  public void does_not_update_severity_when_no_issue_admin_permission() throws Exception {
+    setUserProjectPermissions(USER, ISSUE_ADMIN);
+    ComponentDto anotherProject = db.components().insertProject();
+    ComponentDto anotherFile = db.components().insertComponent(newFileDto(anotherProject));
+    addUserProjectPermissions(anotherProject, USER);
+
+    IssueDto authorizedIssue1 = db.issues().insertIssue(newUnresolvedIssue().setSeverity(MAJOR));
+    // User has not issue admin permission on these 2 issues
+    IssueDto notAuthorizedIssue1 = db.issues().insertIssue(newUnresolvedIssue(rule, anotherFile, anotherProject).setSeverity(MAJOR));
+    IssueDto notAuthorizedIssue2 = db.issues().insertIssue(newUnresolvedIssue(rule, anotherFile, anotherProject).setSeverity(MAJOR));
+
+    BulkChangeWsResponse response = call(BulkChangeRequest.builder()
+      .setIssues(asList(authorizedIssue1.getKey(), notAuthorizedIssue1.getKey(), notAuthorizedIssue2.getKey()))
+      .setSetSeverity(MINOR)
+      .build());
+
+    checkResponse(response, 3, 1, 2, 0);
+    assertThat(getIssueByKeys(authorizedIssue1.getKey(), notAuthorizedIssue1.getKey(), notAuthorizedIssue2.getKey()))
+      .extracting(IssueDto::getKey, IssueDto::getSeverity, IssueDto::getUpdatedAt)
+      .containsOnly(
+        tuple(authorizedIssue1.getKey(), MINOR, NOW),
+        tuple(notAuthorizedIssue1.getKey(), MAJOR, notAuthorizedIssue1.getUpdatedAt()),
+        tuple(notAuthorizedIssue2.getKey(), MAJOR, notAuthorizedIssue2.getUpdatedAt()));
+  }
+
   @Test
   public void fail_when_only_comment_action() throws Exception {
-    setUserPermissions(USER);
+    setUserProjectPermissions(USER);
     IssueDto issueDto = db.issues().insertIssue(newUnresolvedIssue().setType(BUG));
     expectedException.expectMessage("At least one action must be provided");
     expectedException.expect(IllegalArgumentException.class);
@@ -427,19 +481,23 @@ public class BulkChangeActionTest {
     }
   }
 
-  private void setUserPermissions(String... permissions) {
-    UserSessionRule userSessionRule = userSession.login(user);
+  private void setUserProjectPermissions(String... permissions) {
+    userSession.login(user);
+    addUserProjectPermissions(project, permissions);
+  }
+
+  private void addUserProjectPermissions(ComponentDto project, String... permissions) {
     for (String permission : permissions) {
       db.users().insertProjectPermissionOnUser(user, permission, project);
-      userSessionRule.addProjectUuidPermissions(permission, project.uuid());
+      userSession.addProjectUuidPermissions(permission, project.uuid());
     }
   }
 
-  private void checkResponse(BulkChangeWsResponse response, int total, int success, int ignored, int failure) {
-    assertThat(response.getTotal()).isEqualTo(total);
-    assertThat(response.getSuccess()).isEqualTo(success);
-    assertThat(response.getIgnored()).isEqualTo(ignored);
-    assertThat(response.getFailures()).isEqualTo(failure);
+  private void checkResponse(BulkChangeWsResponse response, long total, long success, long ignored, long failure) {
+    assertThat(response)
+      .extracting(BulkChangeWsResponse::getTotal, BulkChangeWsResponse::getSuccess, BulkChangeWsResponse::getIgnored, BulkChangeWsResponse::getFailures)
+      .as("Total, success, ignored, failure")
+      .containsOnly(total, success, ignored, failure);
   }
 
   private List<IssueDto> getIssueByKeys(String... issueKeys) {
@@ -461,8 +519,8 @@ public class BulkChangeActionTest {
   private void addActions() {
     actions.add(new org.sonar.server.issue.AssignAction(new DefaultUserFinder(db.getDbClient().userDao()), issueFieldsSetter));
     actions.add(new org.sonar.server.issue.SetSeverityAction(issueFieldsSetter, userSession));
-    actions.add(new org.sonar.server.issue.SetTypeAction(issueFieldsSetter));
-    actions.add(new org.sonar.server.issue.TransitionAction(new TransitionService(userSession, new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter))));
+    actions.add(new org.sonar.server.issue.SetTypeAction(issueFieldsSetter, userSession));
+    actions.add(new org.sonar.server.issue.TransitionAction(new TransitionService(userSession, issueWorkflow)));
     actions.add(new org.sonar.server.issue.AddTagsAction(issueFieldsSetter));
     actions.add(new org.sonar.server.issue.RemoveTagsAction(issueFieldsSetter));
     actions.add(new org.sonar.server.issue.CommentAction(issueFieldsSetter));
index dea887532cd79f2a27020e729b347766b9bde5de..08780513b0e139d5c489813a36ba654f6259b244 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.issue.ws;
 
+import java.util.Arrays;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
@@ -31,6 +32,7 @@ import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.core.util.stream.Collectors;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
@@ -58,6 +60,8 @@ import org.sonar.server.ws.WsTester;
 
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
+import static org.sonar.api.web.UserRole.USER;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.ACTION_SEARCH;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.CONTROLLER_ISSUES;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.DEPRECATED_FACET_MODE_DEBT;
@@ -223,8 +227,8 @@ public class SearchActionMediumTest {
     db.userDao().insert(session, new UserDto().setLogin("simon").setName("Simon").setEmail("simon@email.com"));
     db.userDao().insert(session, new UserDto().setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));
     ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("PROJECT_KEY").setLanguage("java"));
-    setDefaultProjectPermission(project);
     ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setKey("FILE_KEY").setLanguage("js"));
+    setProjectPermission(project, USER);
 
     IssueDto issue = IssueTesting.newDto(newRule(), file, project)
       .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
@@ -234,12 +238,32 @@ public class SearchActionMediumTest {
     session.commit();
     tester.get(IssueIndexer.class).indexAll();
 
-    userSessionRule.login("john");
     WsTester.Result result = wsTester.newGetRequest(CONTROLLER_ISSUES, ACTION_SEARCH)
       .setParam("additionalFields", "_all").execute();
     result.assertJson(this.getClass(), "load_additional_fields.json");
   }
 
+  @Test
+  public void load_additional_fields_with_issue_admin_permission() throws Exception {
+    db.userDao().insert(session, new UserDto().setLogin("simon").setName("Simon").setEmail("simon@email.com"));
+    db.userDao().insert(session, new UserDto().setLogin("fabrice").setName("Fabrice").setEmail("fabrice@email.com"));
+    ComponentDto project = insertComponent(ComponentTesting.newProjectDto("PROJECT_ID").setKey("PROJECT_KEY").setLanguage("java"));
+    ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, null, "FILE_ID").setKey("FILE_KEY").setLanguage("js"));
+    setProjectPermission(project, USER, ISSUE_ADMIN);
+
+    IssueDto issue = IssueTesting.newDto(newRule(), file, project)
+      .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
+      .setAuthorLogin("John")
+      .setAssignee("simon");
+    db.issueDao().insert(session, issue);
+    session.commit();
+    tester.get(IssueIndexer.class).indexAll();
+
+    WsTester.Result result = wsTester.newGetRequest(CONTROLLER_ISSUES, ACTION_SEARCH)
+      .setParam("additionalFields", "_all").execute();
+    result.assertJson(this.getClass(), "load_additional_fields_with_issue_admin_permission.json");
+  }
+
   @Test
   public void issue_on_removed_file() throws Exception {
     RuleDto rule = newRule();
@@ -667,6 +691,16 @@ public class SearchActionMediumTest {
     tester.get(PermissionUpdater.class).apply(session, asList(permissionChange));
   }
 
+  private void setProjectPermission(ComponentDto project, String... permissions) {
+    // project can be seen by anyone and by code viewer
+    userSessionRule.login("admin");
+    Arrays.stream(permissions).forEach(permission -> userSessionRule.addProjectUuidPermissions(permission, project.uuid()));
+    tester.get(PermissionUpdater.class).apply(session, Arrays.stream(permissions)
+      // TODO correctly feed default organization. Not a problem as long as issues search does not support "anyone" for each organization
+      .map(permission -> new GroupPermissionChange(PermissionChange.Operation.ADD, permission, new ProjectId(project), GroupIdOrAnyone.forAnyone("TODO")))
+      .collect(Collectors.toList()));
+  }
+
   private ComponentDto insertComponent(ComponentDto component) {
     db.componentDao().insert(session, component);
     session.commit();
index 57c1ca954192a68cb769872f36884674aa0cf839..1302fa66b9d37ba5056baed9203ebc5629019f61 100644 (file)
  */
 package org.sonar.server.issue.ws;
 
+import java.util.List;
+import javax.annotation.Nullable;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.sonar.api.config.MapSettings;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
-import org.sonar.server.issue.IssueService;
-import org.sonar.server.ws.WsAction;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.core.issue.FieldDiffs;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.issue.IssueDbTester;
+import org.sonar.db.issue.IssueDto;
+import org.sonar.db.rule.RuleDto;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.issue.IssueFieldsSetter;
+import org.sonar.server.issue.IssueFinder;
+import org.sonar.server.issue.IssueUpdater;
+import org.sonar.server.issue.ServerIssueStorage;
+import org.sonar.server.issue.index.IssueIndexDefinition;
+import org.sonar.server.issue.index.IssueIndexer;
+import org.sonar.server.notification.NotificationManager;
+import org.sonar.server.rule.DefaultRuleFinder;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.TestResponse;
 import org.sonar.server.ws.WsActionTester;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.sonar.api.rule.Severity.MAJOR;
+import static org.sonar.api.rule.Severity.MINOR;
+import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
+import static org.sonar.api.web.UserRole.USER;
+import static org.sonar.core.util.Protobuf.setNullable;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.issue.IssueTesting.newDto;
+import static org.sonar.db.rule.RuleTesting.newRuleDto;
 
 public class SetSeverityActionTest {
 
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
 
-  IssueService issueService = mock(IssueService.class);
-  OperationResponseWriter responseWriter = mock(OperationResponseWriter.class);
-  WsAction underTest = new SetSeverityAction(issueService, responseWriter);
-  WsActionTester tester = new WsActionTester(underTest);
+  @Rule
+  public DbTester dbTester = DbTester.create();
+
+  @Rule
+  public EsTester esTester = new EsTester(new IssueIndexDefinition(new MapSettings()));
+
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
+
+  private System2 system2 = mock(System2.class);
+
+  private DbClient dbClient = dbTester.getDbClient();
+
+  private IssueDbTester issueDbTester = new IssueDbTester(dbTester);
+
+  private OperationResponseWriter responseWriter = mock(OperationResponseWriter.class);
+
+  private WsActionTester tester = new WsActionTester(new SetSeverityAction(userSession, dbClient, new IssueFinder(dbClient, userSession), new IssueFieldsSetter(),
+    new IssueUpdater(dbClient,
+      new ServerIssueStorage(system2, new DefaultRuleFinder(dbClient), dbClient, new IssueIndexer(system2, dbClient, esTester.client())), mock(NotificationManager.class)),
+    responseWriter));
 
   @Test
   public void set_severity() throws Exception {
-    tester.newRequest()
-      .setParam("issue", "ABC")
-      .setParam("severity", "BLOCKER")
-      .execute();
+    IssueDto issueDto = issueDbTester.insertIssue(newIssue().setSeverity(MAJOR));
+    setUserWithBrowseAndAdministerIssuePermission(issueDto.getProjectUuid());
+
+    call(issueDto.getKey(), MINOR);
 
-    verify(issueService).setSeverity("ABC", "BLOCKER");
-    verify(responseWriter).write(eq("ABC"), any(Request.class), any(Response.class));
+    verify(responseWriter).write(eq(issueDto.getKey()), any(Request.class), any(Response.class));
+    IssueDto issueReloaded = dbClient.issueDao().selectByKey(dbTester.getSession(), issueDto.getKey()).get();
+    assertThat(issueReloaded.getSeverity()).isEqualTo(MINOR);
+    assertThat(issueReloaded.isManualSeverity()).isTrue();
   }
 
   @Test
-  public void fail_if_bad_severity_value() {
+  public void insert_entry_in_changelog_when_setting_severity() throws Exception {
+    IssueDto issueDto = issueDbTester.insertIssue(newIssue().setSeverity(MAJOR));
+    setUserWithBrowseAndAdministerIssuePermission(issueDto.getProjectUuid());
+
+    call(issueDto.getKey(), MINOR);
+
+    List<FieldDiffs> fieldDiffs = dbClient.issueChangeDao().selectChangelogByIssue(dbTester.getSession(), issueDto.getKey());
+    assertThat(fieldDiffs).hasSize(1);
+    assertThat(fieldDiffs.get(0).diffs()).hasSize(1);
+    assertThat(fieldDiffs.get(0).diffs().get("severity").newValue()).isEqualTo(MINOR);
+    assertThat(fieldDiffs.get(0).diffs().get("severity").oldValue()).isEqualTo(MAJOR);
+  }
+
+  @Test
+  public void fail_if_bad_severity() {
+    IssueDto issueDto = issueDbTester.insertIssue(newIssue().setSeverity("unknown"));
+    setUserWithBrowseAndAdministerIssuePermission(issueDto.getProjectUuid());
+
     expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Value of parameter 'severity' (unknown) must be one of: [INFO, MINOR, MAJOR, CRITICAL, BLOCKER]");
+    call(issueDto.getKey(), "unknown");
+  }
+
+  @Test
+  public void fail_when_not_authenticated() throws Exception {
+    expectedException.expect(UnauthorizedException.class);
+    call("ABCD", MAJOR);
+  }
+
+  @Test
+  public void fail_when_missing_browse_permission() throws Exception {
+    IssueDto issueDto = issueDbTester.insertIssue();
+    userSession.login("john").addProjectUuidPermissions(ISSUE_ADMIN, issueDto.getProjectUuid());
+
+    expectedException.expect(ForbiddenException.class);
+    call(issueDto.getKey(), MAJOR);
+  }
+
+  @Test
+  public void fail_when_missing_administer_issue_permission() throws Exception {
+    IssueDto issueDto = issueDbTester.insertIssue();
+    userSession.login("john").addProjectUuidPermissions(USER, issueDto.getProjectUuid());
+
+    expectedException.expect(ForbiddenException.class);
+    call(issueDto.getKey(), MAJOR);
+  }
+
+  @Test
+  public void test_definition() {
+    WebService.Action action = tester.getDef();
+    assertThat(action.key()).isEqualTo("set_severity");
+    assertThat(action.isPost()).isTrue();
+    assertThat(action.isInternal()).isFalse();
+    assertThat(action.params()).hasSize(2);
+    assertThat(action.responseExample()).isNotNull();
+  }
+
+  private TestResponse call(@Nullable String issueKey, @Nullable String severity) {
+    TestRequest request = tester.newRequest();
+    setNullable(issueKey, issue -> request.setParam("issue", issue));
+    setNullable(severity, value -> request.setParam("severity", value));
+    return request.execute();
+  }
+
+  private IssueDto newIssue() {
+    RuleDto rule = dbTester.rules().insertRule(newRuleDto());
+    ComponentDto project = dbTester.components().insertProject();
+    ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
+    return newDto(rule, file, project);
+  }
 
-    tester.newRequest()
-      .setParam("issue", "ABC")
-      .setParam("severity", "WAT")
-      .execute();
+  private void setUserWithBrowseAndAdministerIssuePermission(String projectUuid) {
+    userSession.login("john")
+      .addProjectUuidPermissions(ISSUE_ADMIN, projectUuid)
+      .addProjectUuidPermissions(USER, projectUuid);
   }
 }
index bf162fc24e91fd145d2f4439740d394a7afcf79d..a1cb52cc4372992e66893a81e96687225de570f9 100644 (file)
  */
 package org.sonar.server.issue.ws;
 
+import java.util.List;
+import javax.annotation.Nullable;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
-import org.sonar.api.rules.RuleType;
+import org.sonar.api.config.MapSettings;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
-import org.sonar.server.issue.IssueService;
-import org.sonar.server.ws.WsAction;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.core.issue.FieldDiffs;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.issue.IssueDbTester;
+import org.sonar.db.issue.IssueDto;
+import org.sonar.db.rule.RuleDto;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.issue.IssueFieldsSetter;
+import org.sonar.server.issue.IssueFinder;
+import org.sonar.server.issue.IssueUpdater;
+import org.sonar.server.issue.ServerIssueStorage;
+import org.sonar.server.issue.index.IssueIndexDefinition;
+import org.sonar.server.issue.index.IssueIndexer;
+import org.sonar.server.notification.NotificationManager;
+import org.sonar.server.rule.DefaultRuleFinder;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.TestResponse;
 import org.sonar.server.ws.WsActionTester;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.sonar.api.rules.RuleType.BUG;
+import static org.sonar.api.rules.RuleType.CODE_SMELL;
+import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
+import static org.sonar.api.web.UserRole.USER;
+import static org.sonar.core.util.Protobuf.setNullable;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.issue.IssueTesting.newDto;
+import static org.sonar.db.rule.RuleTesting.newRuleDto;
 
 public class SetTypeActionTest {
 
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
 
-  IssueService issueService = mock(IssueService.class);
-  OperationResponseWriter responseWriter = mock(OperationResponseWriter.class);
-  WsAction underTest = new SetTypeAction(issueService, responseWriter);
-  WsActionTester tester = new WsActionTester(underTest);
+  @Rule
+  public DbTester dbTester = DbTester.create();
+
+  @Rule
+  public EsTester esTester = new EsTester(new IssueIndexDefinition(new MapSettings()));
+
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
+
+  private System2 system2 = mock(System2.class);
+
+  private DbClient dbClient = dbTester.getDbClient();
+
+  private IssueDbTester issueDbTester = new IssueDbTester(dbTester);
+
+  private OperationResponseWriter responseWriter = mock(OperationResponseWriter.class);
+
+  private WsActionTester tester = new WsActionTester(new SetTypeAction(userSession, dbClient, new IssueFinder(dbClient, userSession), new IssueFieldsSetter(),
+    new IssueUpdater(dbClient,
+      new ServerIssueStorage(system2, new DefaultRuleFinder(dbClient), dbClient, new IssueIndexer(system2, dbClient, esTester.client())), mock(NotificationManager.class)),
+    responseWriter));
 
   @Test
   public void set_type() throws Exception {
-    tester.newRequest()
-      .setParam("issue", "ABC")
-      .setParam("type", "BUG")
-      .execute();
+    IssueDto issueDto = issueDbTester.insertIssue(newIssue().setType(CODE_SMELL));
+    setUserWithBrowseAndAdministerIssuePermission(issueDto.getProjectUuid());
+
+    call(issueDto.getKey(), BUG.name());
 
-    verify(issueService).setType("ABC", RuleType.BUG);
-    verify(responseWriter).write(eq("ABC"), any(Request.class), any(Response.class));
+    verify(responseWriter).write(eq(issueDto.getKey()), any(Request.class), any(Response.class));
+    IssueDto issueReloaded = dbClient.issueDao().selectByKey(dbTester.getSession(), issueDto.getKey()).get();
+    assertThat(issueReloaded.getType()).isEqualTo(BUG.getDbConstant());
+  }
+
+  @Test
+  public void insert_entry_in_changelog_when_setting_type() throws Exception {
+    IssueDto issueDto = issueDbTester.insertIssue(newIssue().setType(CODE_SMELL));
+    setUserWithBrowseAndAdministerIssuePermission(issueDto.getProjectUuid());
+
+    call(issueDto.getKey(), BUG.name());
+
+    List<FieldDiffs> fieldDiffs = dbClient.issueChangeDao().selectChangelogByIssue(dbTester.getSession(), issueDto.getKey());
+    assertThat(fieldDiffs).hasSize(1);
+    assertThat(fieldDiffs.get(0).diffs()).hasSize(1);
+    assertThat(fieldDiffs.get(0).diffs().get("type").newValue()).isEqualTo(BUG.name());
+    assertThat(fieldDiffs.get(0).diffs().get("type").oldValue()).isEqualTo(CODE_SMELL.name());
   }
 
   @Test
   public void fail_if_bad_type_value() {
+    IssueDto issueDto = issueDbTester.insertIssue(newIssue().setType(CODE_SMELL));
+    setUserWithBrowseAndAdministerIssuePermission(issueDto.getProjectUuid());
+
     expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Value of parameter 'type' (unknown) must be one of: [CODE_SMELL, BUG, VULNERABILITY]");
+    call(issueDto.getKey(), "unknown");
+  }
+
+  @Test
+  public void fail_when_not_authenticated() throws Exception {
+    expectedException.expect(UnauthorizedException.class);
+    call("ABCD", BUG.name());
+  }
+
+  @Test
+  public void fail_when_missing_browse_permission() throws Exception {
+    IssueDto issueDto = issueDbTester.insertIssue();
+    userSession.login("john").addProjectUuidPermissions(ISSUE_ADMIN, issueDto.getProjectUuid());
+
+    expectedException.expect(ForbiddenException.class);
+    call(issueDto.getKey(), BUG.name());
+  }
+
+  @Test
+  public void fail_when_missing_administer_issue_permission() throws Exception {
+    IssueDto issueDto = issueDbTester.insertIssue();
+    userSession.login("john").addProjectUuidPermissions(USER, issueDto.getProjectUuid());
+
+    expectedException.expect(ForbiddenException.class);
+    call(issueDto.getKey(), BUG.name());
+  }
+
+  @Test
+  public void test_definition() {
+    WebService.Action action = tester.getDef();
+    assertThat(action.key()).isEqualTo("set_type");
+    assertThat(action.isPost()).isTrue();
+    assertThat(action.isInternal()).isFalse();
+    assertThat(action.params()).hasSize(2);
+    assertThat(action.responseExample()).isNotNull();
+  }
+
+  private TestResponse call(@Nullable String issueKey, @Nullable String type) {
+    TestRequest request = tester.newRequest();
+    setNullable(issueKey, issue -> request.setParam("issue", issue));
+    setNullable(type, t -> request.setParam("type", t));
+    return request.execute();
+  }
+
+  private IssueDto newIssue() {
+    RuleDto rule = dbTester.rules().insertRule(newRuleDto());
+    ComponentDto project = dbTester.components().insertProject();
+    ComponentDto file = dbTester.components().insertComponent(newFileDto(project));
+    return newDto(rule, file, project);
+  }
 
-    tester.newRequest()
-      .setParam("issue", "ABC")
-      .setParam("severity", "WAT")
-      .execute();
+  private void setUserWithBrowseAndAdministerIssuePermission(String projectUuid) {
+    userSession.login("john")
+      .addProjectUuidPermissions(ISSUE_ADMIN, projectUuid)
+      .addProjectUuidPermissions(USER, projectUuid);
   }
 }
index c90e5bfaabec1dff452bd27102be382837444333..ab964dfb2411da0547ecc852478e575ec86ebf75 100644 (file)
@@ -4,10 +4,14 @@
       "key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
       "assignee": "simon",
       "actions": [
-        "comment", "assign", "set_tags", "set_type", "assign_to_me"
+        "comment",
+        "assign",
+        "set_tags",
+        "assign_to_me"
       ],
       "transitions": [
-        "confirm", "resolve"
+        "confirm",
+        "resolve"
       ]
     }
   ],
       "name": "Simon",
       "email": "simon@email.com",
       "active": true
+    },
+    {
+      "login": "admin",
+      "name": "Administrator",
+      "email": "",
+      "active": true
     }
   ]
 }
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/load_additional_fields_with_issue_admin_permission.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionMediumTest/load_additional_fields_with_issue_admin_permission.json
new file mode 100644 (file)
index 0000000..fadc226
--- /dev/null
@@ -0,0 +1,36 @@
+{
+  "issues": [
+    {
+      "key": "82fd47d4-b650-4037-80bc-7b112bd4eac2",
+      "assignee": "simon",
+      "actions": [
+        "comment",
+        "assign",
+        "set_tags",
+        "assign_to_me",
+        "set_type",
+        "set_severity"
+      ],
+      "transitions": [
+        "confirm",
+        "resolve",
+        "falsepositive",
+        "wontfix"
+      ],
+    }
+  ],
+  "users": [
+    {
+      "login": "simon",
+      "name": "Simon",
+      "email": "simon@email.com",
+      "active": true
+    },
+    {
+      "login": "admin",
+      "name": "Administrator",
+      "email": "",
+      "active": true
+    }
+  ]
+}