]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7982 Create WS api/settings/encrypt to encrypt a setting value
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Fri, 2 Sep 2016 14:40:35 +0000 (16:40 +0200)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Mon, 5 Sep 2016 12:37:54 +0000 (14:37 +0200)
server/sonar-server/src/main/java/org/sonar/server/setting/ws/EncryptAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsModule.java
server/sonar-server/src/main/resources/org/sonar/server/setting/ws/encrypt-example.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/setting/ws/EncryptActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/setting/ws/SettingsWsModuleTest.java
sonar-ws/src/main/protobuf/ws-settings.proto

diff --git a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/EncryptAction.java b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/EncryptAction.java
new file mode 100644 (file)
index 0000000..b815073
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.setting.ws;
+
+import org.sonar.api.config.Settings;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.server.user.UserSession;
+import org.sonarqube.ws.Settings.EncryptWsResponse;
+
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+import static org.sonar.server.ws.WsUtils.checkRequest;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+import static org.sonarqube.ws.client.setting.SettingsWsParameters.PARAM_VALUE;
+
+public class EncryptAction implements SettingsWsAction {
+  private final UserSession userSession;
+  private final Settings settings;
+
+  public EncryptAction(UserSession userSession, Settings settings) {
+    this.userSession = userSession;
+    this.settings = settings;
+  }
+
+  @Override
+  public void define(WebService.NewController context) {
+    WebService.NewAction action = context.createAction("encrypt")
+      .setDescription("Encrypt a setting value.<br>" +
+        "Requires 'Administer System' permission.")
+      .setSince("6.1")
+      .setHandler(this)
+      .setInternal(true)
+      .setResponseExample(getClass().getResource("encrypt-example.json"));
+
+    action.createParam(PARAM_VALUE)
+      .setRequired(true)
+      .setDescription("Setting value to encrypt")
+      .setExampleValue("my value");
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    userSession.checkPermission(SYSTEM_ADMIN);
+
+    String value = request.mandatoryParam(PARAM_VALUE);
+    checkRequest(!value.isEmpty(), "Parameter '%s' must not be empty", PARAM_VALUE);
+
+    String encryptedValue = settings.getEncryption().encrypt(value);
+
+    writeProtobuf(toEncryptWsResponse(encryptedValue), request, response);
+  }
+
+  private static EncryptWsResponse toEncryptWsResponse(String encryptedValue) {
+    return EncryptWsResponse.newBuilder().setEncryptedValue(encryptedValue).build();
+  }
+}
index 0b1a2ae9806b0a0ff3131b27b490f5d39adb6d74..4a0a9755dec4c019d14ddae45f6515b3befbcb22 100644 (file)
@@ -32,6 +32,7 @@ public class SettingsWsModule extends Module {
       ValuesAction.class,
       SettingsFinder.class,
       ResetAction.class,
+      EncryptAction.class,
       SettingsUpdater.class);
   }
 }
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/setting/ws/encrypt-example.json b/server/sonar-server/src/main/resources/org/sonar/server/setting/ws/encrypt-example.json
new file mode 100644 (file)
index 0000000..ae4fd4c
--- /dev/null
@@ -0,0 +1,3 @@
+{
+  "encryptedValue": "{aes}q2ANI9ikR9R8P2CMCCTWeA=="
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/setting/ws/EncryptActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/setting/ws/EncryptActionTest.java
new file mode 100644 (file)
index 0000000..b5c7a09
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.setting.ws;
+
+import com.google.common.base.Throwables;
+import java.io.File;
+import java.io.IOException;
+import javax.annotation.Nullable;
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.config.Encryption;
+import org.sonar.api.config.Settings;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.WsActionTester;
+import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.Settings.EncryptWsResponse;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.core.permission.GlobalPermissions.QUALITY_PROFILE_ADMIN;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+import static org.sonar.test.JsonAssert.assertJson;
+import static org.sonarqube.ws.client.setting.SettingsWsParameters.PARAM_VALUE;
+
+public class EncryptActionTest {
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone().setGlobalPermissions(SYSTEM_ADMIN);
+  @Rule
+  public TemporaryFolder folder = new TemporaryFolder();
+
+  Settings settings = mock(Settings.class);
+  Encryption encryption;
+
+  EncryptAction underTest = new EncryptAction(userSession, settings);
+
+  WsActionTester ws = new WsActionTester(underTest);
+
+  @Before
+  public void setUp_secret_key() {
+    try {
+      File secretKeyFile = folder.newFile();
+      FileUtils.writeStringToFile(secretKeyFile, "fCVFf/JHRi8Qwu5KLNva7g==");
+
+      encryption = new Encryption(secretKeyFile.getAbsolutePath());
+
+      when(settings.getEncryption()).thenReturn(encryption);
+    } catch (IOException e) {
+      Throwables.propagate(e);
+    }
+  }
+
+  @Test
+  public void json_example() {
+    String result = ws.newRequest().setParam("value", "my value").execute().getInput();
+
+    assertJson(result).isSimilarTo(ws.getDef().responseExampleAsString());
+  }
+
+  @Test
+  public void encrypt() {
+    EncryptWsResponse result = call("my value!");
+
+    assertThat(result.getEncryptedValue()).isEqualTo("{aes}NoofntibpMBdhkMfXQxYcA==");
+  }
+
+  @Test
+  public void definition() {
+    WebService.Action definition = ws.getDef();
+
+    assertThat(definition.key()).isEqualTo("encrypt");
+    assertThat(definition.isPost()).isFalse();
+    assertThat(definition.isInternal()).isTrue();
+    assertThat(definition.responseExampleAsString()).isNotEmpty();
+    assertThat(definition.params()).hasSize(1);
+
+  }
+
+  @Test
+  public void fail_if_insufficient_permissions() {
+    expectedException.expect(ForbiddenException.class);
+
+    userSession.anonymous().setGlobalPermissions(QUALITY_PROFILE_ADMIN);
+
+    call("my value");
+  }
+
+  @Test
+  public void fail_if_value_is_not_provided() {
+    expectedException.expect(IllegalArgumentException.class);
+
+    call(null);
+  }
+
+  @Test
+  public void fail_if_value_is_empty() {
+    expectedException.expect(BadRequestException.class);
+    expectedException.expectMessage("Parameter 'value' must not be empty");
+
+    call("  ");
+  }
+
+  private EncryptWsResponse call(@Nullable String value) {
+    TestRequest request = ws.newRequest()
+      .setMediaType(MediaTypes.PROTOBUF)
+      .setMethod("POST");
+
+    if (value != null) {
+      request.setParam(PARAM_VALUE, value);
+    }
+
+    try {
+      return EncryptWsResponse.parseFrom(request.execute().getInputStream());
+    } catch (IOException e) {
+      throw Throwables.propagate(e);
+    }
+  }
+}
index 54d0a4b2abba630ebbc0a2bc7a3d139756a163e2..386ba30f9cc756bc332377261a282c5f39bd3260 100644 (file)
@@ -29,6 +29,6 @@ public class SettingsWsModuleTest {
   public void verify_count_of_added_components() {
     ComponentContainer container = new ComponentContainer();
     new SettingsWsModule().configure(container);
-    assertThat(container.size()).isEqualTo(8 + 2);
+    assertThat(container.size()).isEqualTo(9 + 2);
   }
 }
index be09b3d1b69f4027204f02153fcbc8475771858a..0d04c2c4f22fc5bc1066d46e97b3a44de18eb9cf 100644 (file)
@@ -29,6 +29,11 @@ message ListDefinitionsWsResponse {
   repeated Definition definitions = 1;
 }
 
+// Response of GET api/settings/encrypt
+message EncryptWsResponse {
+  optional string encryptedValue = 1;
+}
+
 message Definition {
   optional string key = 1;
   optional string name = 2;