]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12000 add secret to WS api/webhooks/create and api/webhooks/update
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Fri, 19 Apr 2019 11:18:24 +0000 (13:18 +0200)
committerSonarTech <sonartech@sonarsource.com>
Mon, 29 Apr 2019 18:21:07 +0000 (20:21 +0200)
server/sonar-server/src/main/java/org/sonar/server/webhook/ws/CreateAction.java
server/sonar-server/src/main/java/org/sonar/server/webhook/ws/DeleteAction.java
server/sonar-server/src/main/java/org/sonar/server/webhook/ws/UpdateAction.java
server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java
server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-webhook-create.json
server/sonar-server/src/test/java/org/sonar/server/webhook/ws/CreateActionTest.java
server/sonar-server/src/test/java/org/sonar/server/webhook/ws/UpdateActionTest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/CreateRequest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/UpdateRequest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/WebhooksService.java
sonar-ws/src/main/protobuf/ws-webhooks.proto

index 19c2fea77eb1b18e03de5421c09b3295cee69a05..9f485ee9e7da5e5e0d633f0c59d41972fa723f87 100644 (file)
@@ -32,11 +32,9 @@ import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.webhook.WebhookDto;
 import org.sonar.server.organization.DefaultOrganizationProvider;
 import org.sonar.server.user.UserSession;
-import org.sonarqube.ws.Webhooks;
 
 import static com.google.common.base.Preconditions.checkState;
 import static java.lang.String.format;
-import static java.util.Optional.ofNullable;
 import static org.apache.commons.lang.StringUtils.isNotBlank;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.ACTION_CREATE;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.NAME_PARAM;
@@ -44,7 +42,9 @@ import static org.sonar.server.webhook.ws.WebhooksWsParameters.NAME_PARAM_MAXIMU
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.ORGANIZATION_KEY_PARAM;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.ORGANIZATION_KEY_PARAM_MAXIMUM_LENGTH;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.PROJECT_KEY_PARAM;
-import static org.sonar.server.webhook.ws.WebhooksWsParameters.PROJECT_KEY_PARAM_MAXIMUN_LENGTH;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.PROJECT_KEY_PARAM_MAXIMUM_LENGTH;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.SECRET_PARAM;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.SECRET_PARAM_MAXIMUM_LENGTH;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.URL_PARAM;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.URL_PARAM_MAXIMUM_LENGTH;
 import static org.sonar.server.ws.KeyExamples.KEY_ORG_EXAMPLE_001;
@@ -68,7 +68,7 @@ public class CreateAction implements WebhooksWsAction {
   private final WebhookSupport webhookSupport;
 
   public CreateAction(DbClient dbClient, UserSession userSession, DefaultOrganizationProvider defaultOrganizationProvider,
-                      UuidFactory uuidFactory, WebhookSupport webhookSupport) {
+    UuidFactory uuidFactory, WebhookSupport webhookSupport) {
     this.dbClient = dbClient;
     this.userSession = userSession;
     this.defaultOrganizationProvider = defaultOrganizationProvider;
@@ -78,7 +78,6 @@ public class CreateAction implements WebhooksWsAction {
 
   @Override
   public void define(WebService.NewController controller) {
-
     WebService.NewAction action = controller.createAction(ACTION_CREATE)
       .setPost(true)
       .setDescription("Create a Webhook.<br>" +
@@ -103,7 +102,7 @@ public class CreateAction implements WebhooksWsAction {
 
     action.createParam(PROJECT_KEY_PARAM)
       .setRequired(false)
-      .setMaximumLength(PROJECT_KEY_PARAM_MAXIMUN_LENGTH)
+      .setMaximumLength(PROJECT_KEY_PARAM_MAXIMUM_LENGTH)
       .setDescription("The key of the project that will own the webhook")
       .setExampleValue(KEY_PROJECT_EXAMPLE_001);
 
@@ -114,20 +113,26 @@ public class CreateAction implements WebhooksWsAction {
       .setDescription("The key of the organization that will own the webhook")
       .setExampleValue(KEY_ORG_EXAMPLE_001);
 
+    action.createParam(SECRET_PARAM)
+      .setRequired(false)
+      .setMinimumLength(1)
+      .setMaximumLength(SECRET_PARAM_MAXIMUM_LENGTH)
+      .setDescription("If provided, secret will be used as the key to generate the HMAC hex (lowercase) digest value in the 'X-Sonar-Webhook-HMAC-SHA256' header")
+      .setExampleValue("your_secret")
+      .setSince("7.8");
   }
 
   @Override
   public void handle(Request request, Response response) throws Exception {
-
     userSession.checkLoggedIn();
 
     String name = request.mandatoryParam(NAME_PARAM);
     String url = request.mandatoryParam(URL_PARAM);
     String projectKey = request.param(PROJECT_KEY_PARAM);
     String organizationKey = request.param(ORGANIZATION_KEY_PARAM);
+    String secret = request.param(SECRET_PARAM);
 
     try (DbSession dbSession = dbClient.openSession(false)) {
-
       OrganizationDto organizationDto;
       if (isNotBlank(organizationKey)) {
         Optional<OrganizationDto> dtoOptional = dbClient.organizationDao().selectByKey(dbSession, organizationKey);
@@ -138,7 +143,7 @@ public class CreateAction implements WebhooksWsAction {
 
       ComponentDto projectDto = null;
       if (isNotBlank(projectKey)) {
-        Optional<ComponentDto> dtoOptional = ofNullable(dbClient.componentDao().selectByKey(dbSession, projectKey).orElse(null));
+        Optional<ComponentDto> dtoOptional = dbClient.componentDao().selectByKey(dbSession, projectKey);
         ComponentDto componentDto = checkFoundWithOptional(dtoOptional, "No project with key '%s'", projectKey);
         webhookSupport.checkThatProjectBelongsToOrganization(componentDto, organizationDto, "Project '%s' does not belong to organisation '%s'", projectKey, organizationKey);
         webhookSupport.checkPermission(componentDto);
@@ -149,18 +154,18 @@ public class CreateAction implements WebhooksWsAction {
 
       webhookSupport.checkUrlPattern(url, "Url parameter with value '%s' is not a valid url", url);
 
-      WebhookDto webhookDto = doHandle(dbSession, organizationDto, projectDto, name, url);
-
-      dbClient.webhookDao().insert(dbSession, webhookDto);
+      WebhookDto dto = doHandle(dbSession, organizationDto, projectDto, name, url, secret);
+      dbClient.webhookDao().insert(dbSession, dto);
 
       dbSession.commit();
 
-      writeResponse(request, response, webhookDto);
+      writeResponse(request, response, dto);
     }
 
   }
 
-  private WebhookDto doHandle(DbSession dbSession, @Nullable OrganizationDto organization, @Nullable ComponentDto project, String name, String url) {
+  private WebhookDto doHandle(DbSession dbSession, @Nullable OrganizationDto organization,
+    @Nullable ComponentDto project, String name, String url, @Nullable String secret) {
 
     checkState(organization != null || project != null,
       "A webhook can not be created if not linked to an organization or a project.");
@@ -168,7 +173,8 @@ public class CreateAction implements WebhooksWsAction {
     WebhookDto dto = new WebhookDto()
       .setUuid(uuidFactory.create())
       .setName(name)
-      .setUrl(url);
+      .setUrl(url)
+      .setSecret(secret);
 
     if (project != null) {
       checkNumberOfWebhook(numberOfWebhookOf(dbSession, project), "Maximum number of webhook reached for project '%s'", project.getKey());
@@ -181,18 +187,20 @@ public class CreateAction implements WebhooksWsAction {
     return dto;
   }
 
-  private static void writeResponse(Request request, Response response, WebhookDto element) {
-    Webhooks.CreateWsResponse.Builder responseBuilder = newBuilder();
-    responseBuilder.setWebhook(Webhook.newBuilder()
-      .setKey(element.getUuid())
-      .setName(element.getName())
-      .setUrl(element.getUrl()));
-
-    writeProtobuf(responseBuilder.build(), request, response);
+  private static void writeResponse(Request request, Response response, WebhookDto dto) {
+    Webhook.Builder webhookBuilder = Webhook.newBuilder();
+    webhookBuilder
+      .setKey(dto.getUuid())
+      .setName(dto.getName())
+      .setUrl(dto.getUrl());
+    if (dto.getSecret() != null) {
+      webhookBuilder.setSecret(dto.getSecret());
+    }
+    writeProtobuf(newBuilder().setWebhook(webhookBuilder).build(), request, response);
   }
 
   private static void checkNumberOfWebhook(int nbOfWebhooks, String message, Object... messageArguments) {
-    if (nbOfWebhooks >= MAX_NUMBER_OF_WEBHOOKS){
+    if (nbOfWebhooks >= MAX_NUMBER_OF_WEBHOOKS) {
       throw new IllegalArgumentException(format(message, messageArguments));
     }
   }
index 0ee82cf613a90141adba97c727c6e3c8eecaea13..d88edc6149d50104d82517c026eb56f57b40e082 100644 (file)
@@ -33,7 +33,7 @@ import org.sonar.server.user.UserSession;
 import static java.util.Optional.ofNullable;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.DELETE_ACTION;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM;
-import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM_MAXIMUN_LENGTH;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM_MAXIMUM_LENGTH;
 import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
 import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
 import static org.sonar.server.ws.WsUtils.checkStateWithOptional;
@@ -61,7 +61,7 @@ public class DeleteAction implements WebhooksWsAction {
 
     action.createParam(KEY_PARAM)
       .setRequired(true)
-      .setMaximumLength(KEY_PARAM_MAXIMUN_LENGTH)
+      .setMaximumLength(KEY_PARAM_MAXIMUM_LENGTH)
       .setDescription("The key of the webhook to be deleted, "+
         "auto-generated value can be obtained through api/webhooks/create or api/webhooks/list")
       .setExampleValue(KEY_PROJECT_EXAMPLE_001);
index 21ab7e79578f8a07233140de5596f333f6f954de..8ca9ac95c3538fc94daebd1d487e994eb44611bf 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.webhook.ws;
 
 import java.util.Optional;
+import javax.annotation.Nullable;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
@@ -32,9 +33,11 @@ import org.sonar.server.user.UserSession;
 
 import static java.util.Optional.ofNullable;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM;
-import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM_MAXIMUN_LENGTH;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM_MAXIMUM_LENGTH;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.NAME_PARAM;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.NAME_PARAM_MAXIMUM_LENGTH;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.SECRET_PARAM;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.SECRET_PARAM_MAXIMUM_LENGTH;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.UPDATE_ACTION;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.URL_PARAM;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.URL_PARAM_MAXIMUM_LENGTH;
@@ -58,7 +61,6 @@ public class UpdateAction implements WebhooksWsAction {
 
   @Override
   public void define(WebService.NewController controller) {
-
     WebService.NewAction action = controller.createAction(UPDATE_ACTION)
       .setPost(true)
       .setDescription("Update a Webhook.<br>" +
@@ -68,8 +70,8 @@ public class UpdateAction implements WebhooksWsAction {
 
     action.createParam(KEY_PARAM)
       .setRequired(true)
-      .setMaximumLength(KEY_PARAM_MAXIMUN_LENGTH)
-      .setDescription("The key of the webhook to be updated, "+
+      .setMaximumLength(KEY_PARAM_MAXIMUM_LENGTH)
+      .setDescription("The key of the webhook to be updated, " +
         "auto-generated value can be obtained through api/webhooks/create or api/webhooks/list")
       .setExampleValue(KEY_PROJECT_EXAMPLE_001);
 
@@ -85,6 +87,13 @@ public class UpdateAction implements WebhooksWsAction {
       .setDescription("new url to be called by the webhook")
       .setExampleValue(URL_WEBHOOK_EXAMPLE_001);
 
+    action.createParam(SECRET_PARAM)
+      .setRequired(false)
+      .setMinimumLength(1)
+      .setMaximumLength(SECRET_PARAM_MAXIMUM_LENGTH)
+      .setDescription("If provided, secret will be used as the key to generate the HMAC hex (lowercase) digest value in the 'X-Sonar-Webhook-HMAC-SHA256' header")
+      .setExampleValue("your_secret")
+      .setSince("7.8");
   }
 
   @Override
@@ -94,6 +103,7 @@ public class UpdateAction implements WebhooksWsAction {
     String webhookKey = request.param(KEY_PARAM);
     String name = request.mandatoryParam(NAME_PARAM);
     String url = request.mandatoryParam(URL_PARAM);
+    String secret = request.param(SECRET_PARAM);
 
     webhookSupport.checkUrlPattern(url, "Url parameter with value '%s' is not a valid url", url);
 
@@ -107,7 +117,7 @@ public class UpdateAction implements WebhooksWsAction {
         Optional<OrganizationDto> optionalDto = dbClient.organizationDao().selectByUuid(dbSession, organizationUuid);
         OrganizationDto organizationDto = checkStateWithOptional(optionalDto, "the requested organization '%s' was not found", organizationUuid);
         webhookSupport.checkPermission(organizationDto);
-        updateWebhook(dbSession, webhookDto, name, url);
+        updateWebhook(dbSession, webhookDto, name, url, secret);
       }
 
       String projectUuid = webhookDto.getProjectUuid();
@@ -115,7 +125,7 @@ public class UpdateAction implements WebhooksWsAction {
         Optional<ComponentDto> optionalDto = ofNullable(dbClient.componentDao().selectByUuid(dbSession, projectUuid).orElse(null));
         ComponentDto componentDto = checkStateWithOptional(optionalDto, "the requested project '%s' was not found", projectUuid);
         webhookSupport.checkPermission(componentDto);
-        updateWebhook(dbSession, webhookDto, name, url);
+        updateWebhook(dbSession, webhookDto, name, url, secret);
       }
 
       dbSession.commit();
@@ -124,8 +134,12 @@ public class UpdateAction implements WebhooksWsAction {
     response.noContent();
   }
 
-  private void updateWebhook(DbSession dbSession, WebhookDto webhookDto, String name, String url) {
-    dbClient.webhookDao().update(dbSession, webhookDto.setName(name).setUrl(url));
+  private void updateWebhook(DbSession dbSession, WebhookDto dto, String name, String url, @Nullable String secret) {
+    dto
+      .setName(name)
+      .setUrl(url)
+      .setSecret(secret);
+    dbClient.webhookDao().update(dbSession, dto);
   }
 
 }
index 4861bc43f3ebfe454cbd640171e22dd6704426ad..ec847a6d3e3d6d7263c8a727bf99fc07cee7f139 100644 (file)
@@ -33,13 +33,15 @@ class WebhooksWsParameters {
   static final String ORGANIZATION_KEY_PARAM = "organization";
   static final int ORGANIZATION_KEY_PARAM_MAXIMUM_LENGTH = 255;
   static final String PROJECT_KEY_PARAM = "project";
-  static final int PROJECT_KEY_PARAM_MAXIMUN_LENGTH = 100;
+  static final int PROJECT_KEY_PARAM_MAXIMUM_LENGTH = 100;
   static final String NAME_PARAM = "name";
   static final int NAME_PARAM_MAXIMUM_LENGTH = 100;
   static final String URL_PARAM = "url";
   static final int URL_PARAM_MAXIMUM_LENGTH = 512;
   static final String KEY_PARAM = "webhook";
-  static final int KEY_PARAM_MAXIMUN_LENGTH = 40;
+  static final int KEY_PARAM_MAXIMUM_LENGTH = 40;
+  static final String SECRET_PARAM = "secret";
+  static final int SECRET_PARAM_MAXIMUM_LENGTH = 200;
 
   private WebhooksWsParameters() {
     // prevent instantiation
index 28bb248cf7c42d026e075cc2208cc4046afbedc2..29b4ee4762ebdb0134e1ff09dfe2296ebb1172f8 100644 (file)
@@ -2,6 +2,7 @@
   "webhook": {
     "key": "uuid",
     "name": "My webhook",
-    "url": "https://www.my-webhook-listener.com/sonar"
+    "url": "https://www.my-webhook-listener.com/sonar",
+    "secret": "your_secret"
   }
-}
\ No newline at end of file
+}
index e3bfc36862e13caf5ec72241f559239c17e6b278..06fe6016c0fb7c0dabd6d21ba04f995e168b69fd 100644 (file)
@@ -80,7 +80,6 @@ public class CreateActionTest {
 
   @Test
   public void test_ws_definition() {
-
     WebService.Action action = wsActionTester.getDef();
     assertThat(action).isNotNull();
     assertThat(action.isInternal()).isFalse();
@@ -93,92 +92,104 @@ public class CreateActionTest {
         tuple("organization", false),
         tuple("project", false),
         tuple("name", true),
-        tuple("url", true));
+        tuple("url", true),
+        tuple("secret", false));
 
   }
 
   @Test
-  public void create_a_webhook_on_default_organization() {
-
+  public void create_a_webhook_with_secret() {
     userSession.logIn().addPermission(ADMINISTER, defaultOrganizationProvider.get().getUuid());
 
     CreateWsResponse response = wsActionTester.newRequest()
-      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
-      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", URL_WEBHOOK_EXAMPLE_001)
+      .setParam("secret", "a_secret")
       .executeProtobuf(CreateWsResponse.class);
 
     assertThat(response.getWebhook()).isNotNull();
     assertThat(response.getWebhook().getKey()).isNotNull();
     assertThat(response.getWebhook().getName()).isEqualTo(NAME_WEBHOOK_EXAMPLE_001);
     assertThat(response.getWebhook().getUrl()).isEqualTo(URL_WEBHOOK_EXAMPLE_001);
+    assertThat(response.getWebhook().getSecret()).isEqualTo("a_secret");
+  }
 
+  @Test
+  public void create_a_webhook_on_default_organization() {
+    userSession.logIn().addPermission(ADMINISTER, defaultOrganizationProvider.get().getUuid());
+
+    CreateWsResponse response = wsActionTester.newRequest()
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", URL_WEBHOOK_EXAMPLE_001)
+      .executeProtobuf(CreateWsResponse.class);
+
+    assertThat(response.getWebhook()).isNotNull();
+    assertThat(response.getWebhook().getKey()).isNotNull();
+    assertThat(response.getWebhook().getName()).isEqualTo(NAME_WEBHOOK_EXAMPLE_001);
+    assertThat(response.getWebhook().getUrl()).isEqualTo(URL_WEBHOOK_EXAMPLE_001);
+    assertThat(response.getWebhook().hasSecret()).isFalse();
   }
 
   @Test
   public void create_a_webhook_on_specific_organization() {
-
     OrganizationDto organization = organizationDbTester.insert();
-
     userSession.logIn().addPermission(ADMINISTER, organization.getUuid());
 
     CreateWsResponse response = wsActionTester.newRequest()
-      .setParam(ORGANIZATION_KEY_PARAM, organization.getKey())
-      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
-      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .setParam("organization", organization.getKey())
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", URL_WEBHOOK_EXAMPLE_001)
       .executeProtobuf(CreateWsResponse.class);
 
     assertThat(response.getWebhook()).isNotNull();
     assertThat(response.getWebhook().getKey()).isNotNull();
     assertThat(response.getWebhook().getName()).isEqualTo(NAME_WEBHOOK_EXAMPLE_001);
     assertThat(response.getWebhook().getUrl()).isEqualTo(URL_WEBHOOK_EXAMPLE_001);
-
+    assertThat(response.getWebhook().hasSecret()).isFalse();
   }
 
   @Test
   public void create_a_webhook_on_project() {
-
     ComponentDto project = componentDbTester.insertPrivateProject();
 
     userSession.logIn().addProjectPermission(ADMIN, project);
 
     CreateWsResponse response = wsActionTester.newRequest()
-      .setParam(PROJECT_KEY_PARAM, project.getKey())
-      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
-      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .setParam("project", project.getKey())
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", URL_WEBHOOK_EXAMPLE_001)
       .executeProtobuf(CreateWsResponse.class);
 
     assertThat(response.getWebhook()).isNotNull();
     assertThat(response.getWebhook().getKey()).isNotNull();
     assertThat(response.getWebhook().getName()).isEqualTo(NAME_WEBHOOK_EXAMPLE_001);
     assertThat(response.getWebhook().getUrl()).isEqualTo(URL_WEBHOOK_EXAMPLE_001);
-
+    assertThat(response.getWebhook().hasSecret()).isFalse();
   }
 
   @Test
   public void create_a_webhook_on_a_project_belonging_to_an_organization() {
-
     OrganizationDto organization = organizationDbTester.insert();
     ComponentDto project = componentDbTester.insertPrivateProject(organization);
 
     userSession.logIn().addProjectPermission(ADMIN, project);
 
     CreateWsResponse response = wsActionTester.newRequest()
-      .setParam(ORGANIZATION_KEY_PARAM, organization.getKey())
-      .setParam(PROJECT_KEY_PARAM, project.getKey())
-      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
-      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .setParam("organization", organization.getKey())
+      .setParam("project", project.getKey())
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", URL_WEBHOOK_EXAMPLE_001)
       .executeProtobuf(CreateWsResponse.class);
 
     assertThat(response.getWebhook()).isNotNull();
     assertThat(response.getWebhook().getKey()).isNotNull();
     assertThat(response.getWebhook().getName()).isEqualTo(NAME_WEBHOOK_EXAMPLE_001);
     assertThat(response.getWebhook().getUrl()).isEqualTo(URL_WEBHOOK_EXAMPLE_001);
-
+    assertThat(response.getWebhook().hasSecret()).isFalse();
   }
 
   @Test
   public void fail_if_project_does_not_belong_to_requested_organization() {
-
     OrganizationDto organization = organizationDbTester.insert();
     ComponentDto project = componentDbTester.insertPrivateProject();
 
@@ -188,17 +199,15 @@ public class CreateActionTest {
     userSession.logIn().addProjectPermission(ADMIN, project);
 
     wsActionTester.newRequest()
-      .setParam(ORGANIZATION_KEY_PARAM, organization.getKey())
-      .setParam(PROJECT_KEY_PARAM, project.getKey())
-      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
-      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .setParam("organization", organization.getKey())
+      .setParam("project", project.getKey())
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", URL_WEBHOOK_EXAMPLE_001)
       .execute();
-
   }
 
   @Test
-  public void fail_if_project_does_not_exists() {
-
+  public void fail_if_project_does_not_exist() {
     expectedException.expect(NotFoundException.class);
     expectedException.expectMessage("No project with key 'inexistent-project-uuid'");
 
@@ -209,12 +218,10 @@ public class CreateActionTest {
       .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
       .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
       .execute();
-
   }
 
   @Test
   public void fail_if_crossing_maximum_quantity_of_webhooks_on_this_project() {
-
     ComponentDto project = componentDbTester.insertPrivateProject();
 
     expectedException.expect(IllegalArgumentException.class);
@@ -230,12 +237,10 @@ public class CreateActionTest {
       .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
       .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
       .execute();
-
   }
 
   @Test
   public void fail_if_crossing_maximum_quantity_of_webhooks_on_this_organization() {
-
     OrganizationDto organization = organizationDbTester.insert();
 
     expectedException.expect(IllegalArgumentException.class);
@@ -254,8 +259,7 @@ public class CreateActionTest {
   }
 
   @Test
-  public void fail_if_organization_does_not_exists() {
-
+  public void fail_if_organization_does_not_exist() {
     expectedException.expect(NotFoundException.class);
     expectedException.expectMessage("No organization with key 'inexistent-organization-uuid'");
 
@@ -266,12 +270,10 @@ public class CreateActionTest {
       .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
       .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
       .execute();
-
   }
 
   @Test
-  public void fail_if_url_is_not_valid() throws Exception {
-
+  public void fail_if_url_is_not_valid() {
     userSession.logIn().addPermission(ADMINISTER, defaultOrganizationProvider.get().getUuid());
 
     expectedException.expect(IllegalArgumentException.class);
@@ -280,12 +282,10 @@ public class CreateActionTest {
       .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
       .setParam(URL_PARAM, "htp://www.wrong-protocol.com/")
       .execute();
-
   }
 
   @Test
-  public void fail_if_credential_in_url_is_have_a_wrong_format() throws Exception {
-
+  public void fail_if_credential_in_url_is_have_a_wrong_format() {
     userSession.logIn().addPermission(ADMINISTER, defaultOrganizationProvider.get().getUuid());
 
     expectedException.expect(IllegalArgumentException.class);
@@ -294,12 +294,10 @@ public class CreateActionTest {
       .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
       .setParam(URL_PARAM, "http://:www.wrong-protocol.com/")
       .execute();
-
   }
 
   @Test
-  public void return_UnauthorizedException_if_not_logged_in() throws Exception {
-
+  public void return_UnauthorizedException_if_not_logged_in() {
     userSession.anonymous();
     expectedException.expect(UnauthorizedException.class);
 
@@ -307,12 +305,10 @@ public class CreateActionTest {
       .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
       .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
       .execute();
-
   }
 
   @Test
   public void throw_ForbiddenException_if_no_organization_provided_and_user_is_not_system_administrator() {
-
     userSession.logIn();
 
     expectedException.expect(ForbiddenException.class);
@@ -326,7 +322,6 @@ public class CreateActionTest {
 
   @Test
   public void throw_ForbiddenException_if_organization_provided_but_user_is_not_organization_administrator() {
-
     OrganizationDto organization = organizationDbTester.insert();
 
     userSession.logIn();
@@ -339,14 +334,11 @@ public class CreateActionTest {
       .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
       .setParam(ORGANIZATION_KEY_PARAM, organization.getKey())
       .execute();
-
   }
 
   @Test
   public void throw_ForbiddenException_if_not_project_administrator() {
-
     ComponentDto project = componentDbTester.insertPrivateProject();
-
     userSession.logIn();
 
     expectedException.expect(ForbiddenException.class);
@@ -357,7 +349,6 @@ public class CreateActionTest {
       .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
       .setParam(PROJECT_KEY_PARAM, project.getKey())
       .execute();
-
   }
 
 }
index 9fb54f8cde2ed84d9dd146832d1ca8f9d178ce15..9dd2f5e9d11eec8608c6d8cbab10af987de7df53 100644 (file)
@@ -49,9 +49,6 @@ import static org.sonar.db.DbTester.create;
 import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
 import static org.sonar.server.organization.TestDefaultOrganizationProvider.from;
 import static org.sonar.server.tester.UserSessionRule.standalone;
-import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM;
-import static org.sonar.server.webhook.ws.WebhooksWsParameters.NAME_PARAM;
-import static org.sonar.server.webhook.ws.WebhooksWsParameters.URL_PARAM;
 import static org.sonar.server.ws.KeyExamples.NAME_WEBHOOK_EXAMPLE_001;
 import static org.sonar.server.ws.KeyExamples.URL_WEBHOOK_EXAMPLE_001;
 
@@ -78,7 +75,6 @@ public class UpdateActionTest {
 
   @Test
   public void test_ws_definition() {
-
     WebService.Action action = wsActionTester.getDef();
     assertThat(action).isNotNull();
     assertThat(action.isInternal()).isFalse();
@@ -89,21 +85,20 @@ public class UpdateActionTest {
       .containsExactlyInAnyOrder(
         tuple("webhook", true),
         tuple("name", true),
-        tuple("url", true));
-
+        tuple("url", true),
+        tuple("secret", false));
   }
 
   @Test
-  public void update_a_project_webhook() {
-
+  public void update_a_project_webhook_with_required_fields() {
     ComponentDto project = componentDbTester.insertPrivateProject();
     WebhookDto dto = webhookDbTester.insertWebhook(project);
     userSession.logIn().addProjectPermission(ADMIN, project);
 
     TestResponse response = wsActionTester.newRequest()
-      .setParam(KEY_PARAM, dto.getUuid())
-      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
-      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .setParam("webhook", dto.getUuid())
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", URL_WEBHOOK_EXAMPLE_001)
       .execute();
 
     assertThat(response.getStatus()).isEqualTo(HTTP_NO_CONTENT);
@@ -113,20 +108,43 @@ public class UpdateActionTest {
     assertThat(reloaded.get().getUrl()).isEqualTo(URL_WEBHOOK_EXAMPLE_001);
     assertThat(reloaded.get().getOrganizationUuid()).isNull();
     assertThat(reloaded.get().getProjectUuid()).isEqualTo(dto.getProjectUuid());
+    assertThat(reloaded.get().getSecret()).isNull();
+  }
+
+  @Test
+  public void update_a_project_webhook_with_all_fields() {
+    ComponentDto project = componentDbTester.insertPrivateProject();
+    WebhookDto dto = webhookDbTester.insertWebhook(project);
+    userSession.logIn().addProjectPermission(ADMIN, project);
+
+    TestResponse response = wsActionTester.newRequest()
+      .setParam("webhook", dto.getUuid())
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", URL_WEBHOOK_EXAMPLE_001)
+      .setParam("secret", "a_new_secret")
+      .execute();
 
+    assertThat(response.getStatus()).isEqualTo(HTTP_NO_CONTENT);
+    Optional<WebhookDto> reloaded = webhookDbTester.selectWebhook(dto.getUuid());
+    assertThat(reloaded.get()).isNotNull();
+    assertThat(reloaded.get().getName()).isEqualTo(NAME_WEBHOOK_EXAMPLE_001);
+    assertThat(reloaded.get().getUrl()).isEqualTo(URL_WEBHOOK_EXAMPLE_001);
+    assertThat(reloaded.get().getOrganizationUuid()).isNull();
+    assertThat(reloaded.get().getProjectUuid()).isEqualTo(dto.getProjectUuid());
+    assertThat(reloaded.get().getSecret()).isEqualTo("a_new_secret");
   }
 
   @Test
   public void update_an_organization_webhook() {
-
     OrganizationDto organization = organizationDbTester.insert();
     WebhookDto dto = webhookDbTester.insertWebhook(organization);
     userSession.logIn().addPermission(ADMINISTER, organization.getUuid());
 
     TestResponse response = wsActionTester.newRequest()
-      .setParam(KEY_PARAM, dto.getUuid())
-      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
-      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .setParam("webhook", dto.getUuid())
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", URL_WEBHOOK_EXAMPLE_001)
+      .setParam("secret", "a_new_secret")
       .execute();
 
     assertThat(response.getStatus()).isEqualTo(HTTP_NO_CONTENT);
@@ -136,27 +154,25 @@ public class UpdateActionTest {
     assertThat(reloaded.get().getUrl()).isEqualTo(URL_WEBHOOK_EXAMPLE_001);
     assertThat(reloaded.get().getOrganizationUuid()).isEqualTo(dto.getOrganizationUuid());
     assertThat(reloaded.get().getProjectUuid()).isNull();
-
+    assertThat(reloaded.get().getSecret()).isEqualTo("a_new_secret");
   }
 
   @Test
   public void fail_if_webhook_does_not_exist() {
-
     userSession.logIn().addPermission(ADMINISTER, defaultOrganizationProvider.get().getUuid());
 
     expectedException.expect(NotFoundException.class);
     expectedException.expectMessage("No webhook with key 'inexistent-webhook-uuid'");
 
     wsActionTester.newRequest()
-      .setParam(KEY_PARAM, "inexistent-webhook-uuid")
-      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
-      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .setParam("webhook", "inexistent-webhook-uuid")
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", URL_WEBHOOK_EXAMPLE_001)
       .execute();
   }
 
   @Test
-  public void fail_if_not_logged_in() throws Exception {
-
+  public void fail_if_not_logged_in() {
     OrganizationDto organization = organizationDbTester.insert();
     WebhookDto dto = webhookDbTester.insertWebhook(organization);
     userSession.anonymous();
@@ -164,16 +180,14 @@ public class UpdateActionTest {
     expectedException.expect(UnauthorizedException.class);
 
     wsActionTester.newRequest()
-      .setParam(KEY_PARAM, dto.getUuid())
-      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
-      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .setParam("webhook", dto.getUuid())
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", URL_WEBHOOK_EXAMPLE_001)
       .execute();
-
   }
 
   @Test
   public void fail_if_no_permission_on_webhook_scope_project() {
-
     ComponentDto project = componentDbTester.insertPrivateProject();
     WebhookDto dto = webhookDbTester.insertWebhook(project);
 
@@ -183,16 +197,14 @@ public class UpdateActionTest {
     expectedException.expectMessage("Insufficient privileges");
 
     wsActionTester.newRequest()
-      .setParam(KEY_PARAM, dto.getUuid())
-      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
-      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .setParam("webhook", dto.getUuid())
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", URL_WEBHOOK_EXAMPLE_001)
       .execute();
-
   }
 
   @Test
   public void fail_if_no_permission_on_webhook_scope_organization() {
-
     OrganizationDto organization = organizationDbTester.insert();
     WebhookDto dto = webhookDbTester.insertWebhook(organization);
 
@@ -202,16 +214,14 @@ public class UpdateActionTest {
     expectedException.expectMessage("Insufficient privileges");
 
     wsActionTester.newRequest()
-      .setParam(KEY_PARAM, dto.getUuid())
-      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
-      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .setParam("webhook", dto.getUuid())
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", URL_WEBHOOK_EXAMPLE_001)
       .execute();
-
   }
 
   @Test
-  public void fail_if_url_is_not_valid() throws Exception {
-
+  public void fail_if_url_is_not_valid() {
     ComponentDto project = componentDbTester.insertPrivateProject();
     WebhookDto dto = webhookDbTester.insertWebhook(project);
     userSession.logIn().addProjectPermission(ADMIN, project);
@@ -219,16 +229,14 @@ public class UpdateActionTest {
     expectedException.expect(IllegalArgumentException.class);
 
     wsActionTester.newRequest()
-      .setParam(KEY_PARAM, dto.getUuid())
-      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
-      .setParam(URL_PARAM, "htp://www.wrong-protocol.com/")
+      .setParam("webhook", dto.getUuid())
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", "htp://www.wrong-protocol.com/")
       .execute();
-
   }
 
   @Test
-  public void fail_if_credential_in_url_is_have_a_wrong_format() throws Exception {
-
+  public void fail_if_credential_in_url_is_have_a_wrong_format() {
     ComponentDto project = componentDbTester.insertPrivateProject();
     WebhookDto dto = webhookDbTester.insertWebhook(project);
     userSession.logIn().addProjectPermission(ADMIN, project);
@@ -236,11 +244,10 @@ public class UpdateActionTest {
     expectedException.expect(IllegalArgumentException.class);
 
     wsActionTester.newRequest()
-      .setParam(KEY_PARAM, dto.getUuid())
-      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
-      .setParam(URL_PARAM, "http://:www.wrong-protocol.com/")
+      .setParam("webhook", dto.getUuid())
+      .setParam("name", NAME_WEBHOOK_EXAMPLE_001)
+      .setParam("url", "http://:www.wrong-protocol.com/")
       .execute();
-
   }
 
 }
index e9e974e96bf537bdef7523f8bcacd2590f06a82b..737638bd9459c96bf95a3fb54ba82192b4eaf4fd 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonarqube.ws.client.webhooks;
 
-import java.util.List;
 import javax.annotation.Generated;
 
 /**
@@ -34,6 +33,7 @@ public class CreateRequest {
   private String name;
   private String organization;
   private String project;
+  private String secret;
   private String url;
 
   /**
@@ -74,6 +74,18 @@ public class CreateRequest {
     return project;
   }
 
+  /**
+   * Example value: "your_secret"
+   */
+  public CreateRequest setSecret(String secret) {
+    this.secret = secret;
+    return this;
+  }
+
+  public String getSecret() {
+    return secret;
+  }
+
   /**
    * This is a mandatory parameter.
    * Example value: "https://www.my-webhook-listener.com/sonar"
index 289062ce9d4c0d9d3d6d42a8d0b970d15780f6a1..11615e3e6b0ff8604d4f0c7f2b88668482c91822 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonarqube.ws.client.webhooks;
 
-import java.util.List;
 import javax.annotation.Generated;
 
 /**
@@ -32,6 +31,7 @@ import javax.annotation.Generated;
 public class UpdateRequest {
 
   private String name;
+  private String secret;
   private String url;
   private String webhook;
 
@@ -48,6 +48,18 @@ public class UpdateRequest {
     return name;
   }
 
+  /**
+   * Example value: "your_secret"
+   */
+  public UpdateRequest setSecret(String secret) {
+    this.secret = secret;
+    return this;
+  }
+
+  public String getSecret() {
+    return secret;
+  }
+
   /**
    * This is a mandatory parameter.
    * Example value: "https://www.my-webhook-listener.com/sonar"
index e55e891b0a332082051136fb2cedc4af9ba549c9..f61621a7516249d6ba03a346290f6f38ebd0738d 100644 (file)
@@ -53,6 +53,7 @@ public class WebhooksService extends BaseService {
         .setParam("name", request.getName())
         .setParam("organization", request.getOrganization())
         .setParam("project", request.getProject())
+        .setParam("secret", request.getSecret())
         .setParam("url", request.getUrl()),
       CreateWsResponse.parser());
   }
@@ -68,8 +69,7 @@ public class WebhooksService extends BaseService {
     call(
       new PostRequest(path("delete"))
         .setParam("webhook", request.getWebhook())
-        .setMediaType(MediaTypes.JSON)
-      ).content();
+        .setMediaType(MediaTypes.JSON)).content();
   }
 
   /**
@@ -130,9 +130,9 @@ public class WebhooksService extends BaseService {
     call(
       new PostRequest(path("update"))
         .setParam("name", request.getName())
+        .setParam("secret", request.getSecret())
         .setParam("url", request.getUrl())
         .setParam("webhook", request.getWebhook())
-        .setMediaType(MediaTypes.JSON)
-      ).content();
+        .setMediaType(MediaTypes.JSON)).content();
   }
 }
index 81e45aa24fd40e7e7022ade181f4da77634c12a4..f8642e7c5efb1c8e4cd6ddb57400972f89feac08 100644 (file)
@@ -54,6 +54,7 @@ message CreateWsResponse {
     optional string key = 1;
     optional string name = 2;
     optional string url = 3;
+    optional string secret = 4;
   }
 }