aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2016-01-18 09:29:39 +0100
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2016-01-18 18:35:42 +0100
commit0742be3f903c7b3a2381dfd283cd3855af1ce625 (patch)
tree725625eb0e3b2fa3d3aeaa7342532eed8f844f24
parentddee50ce60a9cf14288e4d593adaa217d6372894 (diff)
downloadsonarqube-0742be3f903c7b3a2381dfd283cd3855af1ce625.tar.gz
sonarqube-0742be3f903c7b3a2381dfd283cd3855af1ce625.zip
SONAR-7208 WS user_tokens/generate a user can generate its own tokens
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/GenerateAction.java28
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/TokenPermissionsValidator.java40
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/usertoken/ws/GenerateActionTest.java39
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/usertoken/ws/UserTokensWsTest.java2
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/usertoken/GenerateWsRequest.java6
5 files changed, 94 insertions, 21 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/GenerateAction.java b/server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/GenerateAction.java
index 6affa614a70..fe693a26ccc 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/GenerateAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/GenerateAction.java
@@ -24,9 +24,9 @@ import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
-import org.sonar.core.permission.GlobalPermissions;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
+import org.sonar.db.user.UserDto;
import org.sonar.db.user.UserTokenDto;
import org.sonar.server.exceptions.ServerException;
import org.sonar.server.user.UserSession;
@@ -36,7 +36,7 @@ import org.sonarqube.ws.WsUserTokens.GenerateWsResponse;
import org.sonarqube.ws.client.usertoken.GenerateWsRequest;
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
-import static org.sonar.server.ws.WsUtils.checkFound;
+import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
import static org.sonar.server.ws.WsUtils.checkRequest;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.client.usertoken.UserTokensWsParameters.ACTION_GENERATE;
@@ -63,13 +63,12 @@ public class GenerateAction implements UserTokensWsAction {
.setPost(true)
.setDescription("Generate a user access token. <br />" +
"Please keep your tokens secret. They enable to authenticate and analyze projects.<br />" +
- "It requires administration permissions.")
+ "If the login is set, it requires administration permissions. Otherwise, a token is generated for the authenticated user.")
.setResponseExample(getClass().getResource("generate-example.json"))
.setHandler(this);
action.createParam(PARAM_LOGIN)
- .setRequired(true)
- .setDescription("User login")
+ .setDescription("User login. If not set, the token is generated for the authenticated user.")
.setExampleValue("g.hopper");
action.createParam(PARAM_NAME)
@@ -85,11 +84,10 @@ public class GenerateAction implements UserTokensWsAction {
}
private WsUserTokens.GenerateWsResponse doHandle(GenerateWsRequest request) {
- userSession.checkPermission(GlobalPermissions.SYSTEM_ADMIN);
-
DbSession dbSession = dbClient.openSession(false);
try {
checkWsRequest(dbSession, request);
+ TokenPermissionsValidator.validate(userSession, request.getLogin());
String token = tokenGenerator.generate();
String tokenHash = hashToken(dbSession, token);
@@ -120,7 +118,10 @@ public class GenerateAction implements UserTokensWsAction {
}
private void checkLoginExists(DbSession dbSession, GenerateWsRequest request) {
- checkFound(dbClient.userDao().selectByLogin(dbSession, request.getLogin()), "User with login '%s' not found", request.getLogin());
+ UserDto user = dbClient.userDao().selectByLogin(dbSession, request.getLogin());
+ if (user == null) {
+ throw insufficientPrivilegesException();
+ }
}
private UserTokenDto insertTokenInDb(DbSession dbSession, GenerateWsRequest request, String tokenHash) {
@@ -135,10 +136,15 @@ public class GenerateAction implements UserTokensWsAction {
return userTokenDto;
}
- private static GenerateWsRequest toCreateWsRequest(Request request) {
- return new GenerateWsRequest()
- .setLogin(request.mandatoryParam(PARAM_LOGIN))
+ private GenerateWsRequest toCreateWsRequest(Request request) {
+ GenerateWsRequest generateWsRequest = new GenerateWsRequest()
+ .setLogin(request.param(PARAM_LOGIN))
.setName(request.mandatoryParam(PARAM_NAME));
+ if (generateWsRequest.getLogin() == null) {
+ generateWsRequest.setLogin(userSession.getLogin());
+ }
+
+ return generateWsRequest;
}
private static GenerateWsResponse buildResponse(UserTokenDto userTokenDto, String token) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/TokenPermissionsValidator.java b/server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/TokenPermissionsValidator.java
new file mode 100644
index 00000000000..8be1c2a32e3
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/usertoken/ws/TokenPermissionsValidator.java
@@ -0,0 +1,40 @@
+/*
+ * 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.usertoken.ws;
+
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.server.user.UserSession;
+
+import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
+
+class TokenPermissionsValidator {
+ private TokenPermissionsValidator() {
+ // prevent instantiation
+ }
+
+ static void validate(UserSession userSession, String requestLogin) {
+ if (userSession.hasPermission(GlobalPermissions.SYSTEM_ADMIN)
+ || requestLogin.equals(userSession.getLogin())) {
+ return;
+ }
+
+ throw insufficientPrivilegesException();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/usertoken/ws/GenerateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/usertoken/ws/GenerateActionTest.java
index e9bf3c1b5fd..4bccd4fc0f6 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/usertoken/ws/GenerateActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/usertoken/ws/GenerateActionTest.java
@@ -21,6 +21,7 @@ package org.sonar.server.usertoken.ws;
import java.io.IOException;
import java.io.InputStream;
+import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -31,16 +32,18 @@ import org.sonar.core.permission.GlobalPermissions;
import org.sonar.db.DbTester;
import org.sonar.db.user.UserDbTester;
import org.sonar.server.exceptions.BadRequestException;
-import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.ServerException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.usertoken.TokenGenerator;
+import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.WsActionTester;
import org.sonar.test.DbTests;
import org.sonarqube.ws.MediaTypes;
import org.sonarqube.ws.WsUserTokens.GenerateWsResponse;
import static com.google.common.base.Throwables.propagate;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -82,7 +85,7 @@ public class GenerateActionTest {
}
@Test
- public void return_json_example() {
+ public void json_example() {
String response = ws.newRequest()
.setMediaType(MediaTypes.JSON)
.setParam(PARAM_LOGIN, GRACE_HOPPER)
@@ -93,9 +96,17 @@ public class GenerateActionTest {
}
@Test
+ public void a_user_can_generate_token_for_himself() {
+ userSession.login(GRACE_HOPPER).setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
+
+ GenerateWsResponse response = newRequest(null, TOKEN_NAME);
+
+ assertThat(response.getLogin()).isEqualTo(GRACE_HOPPER);
+ }
+
+ @Test
public void fail_if_login_does_not_exist() {
- expectedException.expect(NotFoundException.class);
- expectedException.expectMessage("User with login 'unknown-login' not found");
+ expectedException.expect(ForbiddenException.class);
newRequest("unknown-login", "any-name");
}
@@ -120,11 +131,23 @@ public class GenerateActionTest {
newRequest(GRACE_HOPPER, TOKEN_NAME);
}
- private GenerateWsResponse newRequest(String login, String name) {
- InputStream responseStream = ws.newRequest()
+ @Test
+ public void fail_if_insufficient_privileges() {
+ userSession.login(ADA_LOVELACE).setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION);
+ expectedException.expect(ForbiddenException.class);
+
+ newRequest(GRACE_HOPPER, TOKEN_NAME);
+ }
+
+ private GenerateWsResponse newRequest(@Nullable String login, String name) {
+ TestRequest testRequest = ws.newRequest()
.setMediaType(MediaTypes.PROTOBUF)
- .setParam(PARAM_LOGIN, login)
- .setParam(PARAM_NAME, name)
+ .setParam(PARAM_NAME, name);
+ if (login != null) {
+ testRequest.setParam(PARAM_LOGIN, login);
+ }
+
+ InputStream responseStream = testRequest
.execute().getInputStream();
try {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/usertoken/ws/UserTokensWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/usertoken/ws/UserTokensWsTest.java
index b72c3113c7f..86d40f98edb 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/usertoken/ws/UserTokensWsTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/usertoken/ws/UserTokensWsTest.java
@@ -57,7 +57,7 @@ public class UserTokensWsTest {
assertThat(action.since()).isEqualTo("5.3");
assertThat(action.responseExampleAsString()).isNotEmpty();
assertThat(action.isPost()).isTrue();
- assertThat(action.param("login").isRequired()).isTrue();
+ assertThat(action.param("login").isRequired()).isFalse();
assertThat(action.param("name").isRequired()).isTrue();
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/usertoken/GenerateWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/usertoken/GenerateWsRequest.java
index dddc0dd296e..d331da4852d 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/usertoken/GenerateWsRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/usertoken/GenerateWsRequest.java
@@ -19,15 +19,19 @@
*/
package org.sonarqube.ws.client.usertoken;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
public class GenerateWsRequest {
private String login;
private String name;
+ @CheckForNull
public String getLogin() {
return login;
}
- public GenerateWsRequest setLogin(String login) {
+ public GenerateWsRequest setLogin(@Nullable String login) {
this.login = login;
return this;
}