]> source.dussan.org Git - sonarqube.git/blob
1546a24be1866124711be32d9e194d2735035bfd
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2022 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.almsettings.ws;
21
22 import com.tngtech.java.junit.dataprovider.DataProvider;
23 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
24 import com.tngtech.java.junit.dataprovider.UseDataProvider;
25 import org.assertj.core.groups.Tuple;
26 import org.junit.Before;
27 import org.junit.Rule;
28 import org.junit.Test;
29 import org.junit.runner.RunWith;
30 import org.mockito.ArgumentCaptor;
31 import org.sonar.api.config.internal.Encryption;
32 import org.sonar.api.resources.ResourceTypes;
33 import org.sonar.api.server.ws.Change;
34 import org.sonar.api.server.ws.WebService;
35 import org.sonar.db.DbSession;
36 import org.sonar.db.DbTester;
37 import org.sonar.db.alm.setting.AlmSettingDto;
38 import org.sonar.db.audit.AuditPersister;
39 import org.sonar.db.audit.model.DevOpsPlatformSettingNewValue;
40 import org.sonar.db.audit.model.SecretNewValue;
41 import org.sonar.db.user.UserDto;
42 import org.sonar.server.almsettings.MultipleAlmFeatureProvider;
43 import org.sonar.server.component.ComponentFinder;
44 import org.sonar.server.exceptions.ForbiddenException;
45 import org.sonar.server.exceptions.NotFoundException;
46 import org.sonar.server.tester.UserSessionRule;
47 import org.sonar.server.ws.TestRequest;
48 import org.sonar.server.ws.WsActionTester;
49
50 import static java.lang.String.format;
51 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
52 import static org.assertj.core.api.Assertions.assertThat;
53 import static org.assertj.core.api.Assertions.assertThatThrownBy;
54 import static org.assertj.core.groups.Tuple.tuple;
55 import static org.mockito.ArgumentMatchers.any;
56 import static org.mockito.Mockito.mock;
57 import static org.mockito.Mockito.verify;
58 import static org.sonar.db.alm.setting.ALM.GITHUB;
59
60 @RunWith(DataProviderRunner.class)
61 public class UpdateGithubActionTest {
62
63   private final AuditPersister auditPersister = mock(AuditPersister.class);
64
65   @Rule
66   public UserSessionRule userSession = UserSessionRule.standalone();
67   @Rule
68   public DbTester db = DbTester.create(auditPersister);
69
70   private final Encryption encryption = mock(Encryption.class);
71
72   private final WsActionTester ws = new WsActionTester(new UpdateGithubAction(db.getDbClient(), userSession,
73     new AlmSettingsSupport(db.getDbClient(), userSession, new ComponentFinder(db.getDbClient(), mock(ResourceTypes.class)),
74       mock(MultipleAlmFeatureProvider.class))));
75
76   private AlmSettingDto almSettingDto;
77
78   @Before
79   public void setUp() {
80     almSettingDto = db.almSettings().insertGitHubAlmSetting();
81     UserDto user = db.users().insertUser();
82     userSession.logIn(user).setSystemAdministrator();
83   }
84
85   @Test
86   public void update() {
87     buildTestRequest().execute();
88
89     assertThat(db.getDbClient().almSettingDao().selectAll(db.getSession()))
90       .extracting(AlmSettingDto::getKey, AlmSettingDto::getUrl, AlmSettingDto::getAppId,
91         s -> s.getDecryptedPrivateKey(encryption), AlmSettingDto::getClientId, s -> s.getDecryptedClientSecret(encryption))
92       .containsOnly(tuple(almSettingDto.getKey(), "https://github.enterprise-unicorn.com", "54321", "10987654321", "client_1234", "client_so_secret"));
93   }
94
95   private TestRequest buildTestRequest() {
96     return ws.newRequest()
97       .setParam("key", almSettingDto.getKey())
98       .setParam("url", "https://github.enterprise-unicorn.com")
99       .setParam("appId", "54321")
100       .setParam("privateKey", "10987654321")
101       .setParam("clientId", "client_1234")
102       .setParam("clientSecret", "client_so_secret");
103   }
104
105   @Test
106   public void update_url_with_trailing_slash() {
107     buildTestRequest().setParam("url", "https://github.enterprise-unicorn.com/").execute();
108
109     assertThat(db.getDbClient().almSettingDao().selectAll(db.getSession()))
110       .extracting(AlmSettingDto::getKey, AlmSettingDto::getUrl, AlmSettingDto::getAppId,
111         s -> s.getDecryptedPrivateKey(encryption), AlmSettingDto::getClientId, s -> s.getDecryptedClientSecret(encryption))
112       .containsOnly(tuple(almSettingDto.getKey(), "https://github.enterprise-unicorn.com", "54321", "10987654321", "client_1234", "client_so_secret"));
113   }
114
115   @Test
116   public void update_with_new_key() {
117     buildTestRequest().setParam("newKey", "GitHub Server - Infra Team").execute();
118
119     assertThat(db.getDbClient().almSettingDao().selectAll(db.getSession()))
120       .extracting(AlmSettingDto::getKey, AlmSettingDto::getUrl, AlmSettingDto::getAppId,
121         s -> s.getDecryptedPrivateKey(encryption), AlmSettingDto::getClientId, s -> s.getDecryptedClientSecret(encryption))
122       .containsOnly(tuple("GitHub Server - Infra Team", "https://github.enterprise-unicorn.com", "54321", "10987654321", "client_1234", "client_so_secret"));
123   }
124
125   @Test
126   public void update_without_private_key_nor_client_secret() {
127     buildTestRequestWithoutSecrets().execute();
128
129     assertThat(db.getDbClient().almSettingDao().selectAll(db.getSession()))
130       .extracting(AlmSettingDto::getKey, AlmSettingDto::getUrl, AlmSettingDto::getAppId,
131         s -> s.getDecryptedPrivateKey(encryption), AlmSettingDto::getClientId, s -> s.getDecryptedClientSecret(encryption))
132       .containsOnly(tuple(almSettingDto.getKey(), "https://github.enterprise-unicorn.com", "54321",
133         almSettingDto.getDecryptedPrivateKey(encryption), "client_1234", almSettingDto.getDecryptedClientSecret(encryption)));
134   }
135
136
137   private TestRequest buildTestRequestWithoutSecrets() {
138     return ws.newRequest()
139       .setParam("key", almSettingDto.getKey())
140       .setParam("url", "https://github.enterprise-unicorn.com/")
141       .setParam("appId", "54321")
142       .setParam("clientId", "client_1234");
143   }
144
145   @Test
146   public void fail_when_key_does_not_match_existing_alm_setting() {
147     TestRequest request = buildTestRequest()
148       .setParam("key", "unknown")
149       .setParam("newKey", "GitHub Server - Infra Team");
150
151     assertThatThrownBy(request::execute)
152       .isInstanceOf(NotFoundException.class)
153       .hasMessageContaining("ALM setting with key 'unknown' cannot be found");
154   }
155
156   @Test
157   public void fail_when_new_key_matches_existing_alm_setting() {
158     AlmSettingDto almSetting2 = db.almSettings().insertGitHubAlmSetting();
159
160     TestRequest request = buildTestRequest()
161       .setParam("key", almSettingDto.getKey())
162       .setParam("newKey", almSetting2.getKey());
163
164     assertThatThrownBy(request::execute)
165       .isInstanceOf(IllegalArgumentException.class)
166       .hasMessageContaining(format("An ALM setting with key '%s' already exists", almSetting2.getKey()));
167   }
168
169   @Test
170   public void fail_when_missing_administer_system_permission() {
171     UserDto user = db.users().insertUser();
172     userSession.logIn(user);
173
174     TestRequest request = buildTestRequest();
175
176     assertThatThrownBy(request::execute).isInstanceOf(ForbiddenException.class);
177   }
178
179   @Test
180   public void definition() {
181     WebService.Action def = ws.getDef();
182
183     assertThat(def.since()).isEqualTo("8.1");
184     assertThat(def.isPost()).isTrue();
185     assertThat(def.params())
186       .extracting(WebService.Param::key, WebService.Param::isRequired)
187       .containsExactlyInAnyOrder(
188         tuple("key", true),
189         tuple("newKey", false),
190         tuple("url", true),
191         tuple("appId", true),
192         tuple("privateKey", false),
193         tuple("clientId", true),
194         tuple("clientSecret", false),
195         tuple("webhookSecret", false));
196   }
197
198   @Test
199   public void update_withWebhookSecret() {
200     buildTestRequest().setParam("webhookSecret", "webhook_secret").execute();
201
202     assertThat(db.getDbClient().almSettingDao().selectAll(db.getSession()))
203       .extracting(almSettings -> almSettings.getDecryptedWebhookSecret(encryption))
204       .containsOnly("webhook_secret");
205   }
206
207   @Test
208   public void update_withoutWebhookSecret_shouldNotOverrideExistingValue() {
209     buildTestRequest().setParam("webhookSecret", "webhook_secret").execute();
210
211     buildTestRequest().execute();
212
213     assertThat(db.getDbClient().almSettingDao().selectAll(db.getSession()))
214       .extracting(almSettings -> almSettings.getDecryptedWebhookSecret(encryption))
215       .containsOnly("webhook_secret");
216   }
217
218   @Test
219   public void update_withEmptyValue_shouldResetWebhookSecret() {
220     buildTestRequest().setParam("webhookSecret", "webhook_secret").execute();
221
222     buildTestRequest().setParam("webhookSecret", "").execute();
223
224     assertThat(db.getDbClient().almSettingDao().selectAll(db.getSession()))
225       .extracting(almSettings -> almSettings.getDecryptedWebhookSecret(encryption))
226       .containsOnly((String) null);
227   }
228
229   @Test
230   public void definition_shouldHaveChangeLog() {
231     assertThat(ws.getDef().changelog()).extracting(Change::getVersion, Change::getDescription).containsExactly(
232       new Tuple("9.7", "Optional parameter 'webhookSecret' was added"),
233       new Tuple("8.7", "Parameter 'privateKey' is no longer required"),
234       new Tuple("8.7", "Parameter 'clientSecret' is no longer required")
235     );
236   }
237
238   @Test
239   @UseDataProvider("secretParams")
240   public void update_withSecretChange_shouldAuditDevOpsPlatformSecret(String secretParam) {
241     buildTestRequestWithoutSecrets().setParam(secretParam, randomAlphanumeric(10)).execute();
242     SecretNewValue expected = new SecretNewValue("DevOpsPlatform", GITHUB.getId());
243     ArgumentCaptor<SecretNewValue> captor = ArgumentCaptor.forClass(SecretNewValue.class);
244
245     verify(auditPersister).updateDevOpsPlatformSecret(any(DbSession.class), captor.capture());
246     assertThat(captor.getValue()).usingRecursiveComparison().isEqualTo(expected);
247   }
248
249   @DataProvider
250   public static Object[][] secretParams() {
251     return new Object[][] {
252       {"webhookSecret"},
253       {"clientSecret"},
254       {"privateKey"}
255     };
256   }
257
258   @Test
259   public void update_withNoSecretChanges_shouldAuditDevOpsPlatformSettings() {
260     buildTestRequestWithoutSecrets().execute();
261     DevOpsPlatformSettingNewValue expected = new DevOpsPlatformSettingNewValue(almSettingDto.getUuid(), almSettingDto.getKey());
262     ArgumentCaptor<DevOpsPlatformSettingNewValue> captor = ArgumentCaptor.forClass(DevOpsPlatformSettingNewValue.class);
263
264     verify(auditPersister).updateDevOpsPlatformSetting(any(DbSession.class), captor.capture());
265     assertThat(captor.getValue()).usingRecursiveComparison().comparingOnlyFields("devOpsPlatformSettingUuid", "key").isEqualTo(expected);
266   }
267
268 }