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;
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;
.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)
}
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);
}
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) {
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) {
--- /dev/null
+/*
+ * 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();
+ }
+}
import java.io.IOException;
import java.io.InputStream;
+import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
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;
}
@Test
- public void return_json_example() {
+ public void json_example() {
String response = ws.newRequest()
.setMediaType(MediaTypes.JSON)
.setParam(PARAM_LOGIN, GRACE_HOPPER)
assertJson(response).isSimilarTo(getClass().getResource("generate-example.json"));
}
+ @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");
}
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 {
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();
}
*/
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;
}