import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.utils.DateUtils;
+import org.sonar.db.user.TokenType;
import org.sonar.db.user.UserDto;
import org.sonar.db.user.UserTokenDto;
@Nullable
private String tokenUuid;
+ @Nullable
private String userUuid;
@Nullable
this.tokenName = tokenName;
}
+ public UserTokenNewValue(String projectKey) {
+ this.projectKey = projectKey;
+ this.type = TokenType.PROJECT_ANALYSIS_TOKEN.name();
+ }
+
@CheckForNull
public String getTokenUuid() {
return this.tokenUuid;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info 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.db.user;
+
+public enum TokenType {
+
+ USER_TOKEN("u"),
+ GLOBAL_ANALYSIS_TOKEN("a"),
+ PROJECT_ANALYSIS_TOKEN("p");
+
+ private final String identifier;
+
+ TokenType(String identifier) {
+ this.identifier = identifier;
+ }
+
+ public String getIdentifier() {
+ return identifier;
+ }
+}
}
}
+ public void deleteByProjectKey(DbSession dbSession, String projectKey) {
+ int deletedRows = mapper(dbSession).deleteByProjectKey(projectKey);
+
+ if (deletedRows > 0) {
+ auditPersister.deleteUserToken(dbSession, new UserTokenNewValue(projectKey));
+ }
+ }
+
private static UserTokenMapper mapper(DbSession dbSession) {
return dbSession.getMapper(UserTokenMapper.class);
}
int deleteByUserUuidAndName(@Param("userUuid") String userUuid, @Param("name") String name);
+ int deleteByProjectKey(@Param("projectKey") String projectKey);
+
List<UserTokenCount> countTokensByUserUuids(@Param("userUuids") List<String> userUuids);
}
DELETE FROM user_tokens WHERE user_uuid=#{userUuid, jdbcType=VARCHAR} and name=#{name, jdbcType=VARCHAR}
</delete>
+ <delete id="deleteByProjectKey">
+ DELETE FROM user_tokens WHERE project_key=#{projectKey, jdbcType=VARCHAR}
+ </delete>
+
</mapper>
assertThat(underTest.selectByUserAndName(dbSession, user2, "name")).isNotNull();
}
+ @Test
+ public void delete_tokens_by_projectKey() {
+ UserDto user1 = db.users().insertUser();
+ UserDto user2 = db.users().insertUser();
+ db.users().insertToken(user1, t -> t.setProjectKey("projectKey1"));
+ db.users().insertToken(user1, t -> t.setProjectKey("projectKey2"));
+ db.users().insertToken(user2, t -> t.setProjectKey("projectKey1"));
+
+ underTest.deleteByProjectKey(dbSession, "projectKey1");
+ db.commit();
+
+ assertThat(underTest.selectByUser(dbSession, user1)).hasSize(1);
+ assertThat(underTest.selectByUser(dbSession, user2)).isEmpty();
+ }
+
@Test
public void count_tokens_by_user() {
UserDto user = db.users().insertUser();
}
@Test
- public void delete_token_by_user_and_name_without_affected_rows_is_persisted() {
+ public void delete_token_by_user_and_name_without_affected_rows_is_not_persisted() {
UserDto user1 = db.users().insertUser();
underTest.deleteByUserAndName(dbSession, user1, "name");
verify(auditPersister).addUser(any(), any());
verifyNoMoreInteractions(auditPersister);
}
+
+
+ @Test
+ public void delete_token_by_projectKey_is_persisted() {
+ UserDto user1 = db.users().insertUser();
+ UserDto user2 = db.users().insertUser();
+ db.users().insertToken(user1, t -> t.setProjectKey("projectToDelete"));
+ db.users().insertToken(user1, t -> t.setProjectKey("projectToKeep"));
+ db.users().insertToken(user2, t -> t.setProjectKey("projectToDelete"));
+
+ underTest.deleteByProjectKey(dbSession, "projectToDelete");
+
+ assertThat(underTest.selectByUser(dbSession, user1)).hasSize(1);
+ assertThat(underTest.selectByUser(dbSession, user2)).isEmpty();
+ verify(auditPersister).deleteUserToken(eq(db.getSession()), newValueCaptor.capture());
+ assertThat(newValueCaptor.getValue())
+ .extracting(UserTokenNewValue::getProjectKey, UserTokenNewValue::getType)
+ .containsExactly("projectToDelete", "PROJECT_ANALYSIS_TOKEN");
+ }
+
+ @Test
+ public void delete_token_by_projectKey_without_affected_rows_is_not_persisted() {
+ UserDto user1 = db.users().insertUser();
+
+ db.users().insertToken(user1, t -> t.setProjectKey("projectToKeep"));
+
+ underTest.deleteByProjectKey(dbSession, "projectToDelete");
+
+ assertThat(underTest.selectByUser(dbSession, user1)).hasSize(1);
+
+ verify(auditPersister).addUser(any(), any());
+ verify(auditPersister).addUserToken(any(), any());
+ verifyNoMoreInteractions(auditPersister);
+ }
}
*/
package org.sonar.server.usertoken;
+import org.sonar.db.user.TokenType;
+
public interface TokenGenerator {
/**
* Generate a token. It must be unique and non deterministic.<br />
import java.security.SecureRandom;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
+import org.sonar.db.user.TokenType;
public class TokenGeneratorImpl implements TokenGenerator {
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2022 SonarSource SA
- * mailto:info 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;
-
-public enum TokenType {
-
- USER_TOKEN("u"),
- GLOBAL_ANALYSIS_TOKEN("a"),
- PROJECT_ANALYSIS_TOKEN("p");
-
- private final String identifier;
-
- TokenType(String identifier) {
- this.identifier = identifier;
- }
-
- public String getIdentifier() {
- return identifier;
- }
-}
import javax.annotation.Nullable;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
+import org.sonar.db.user.TokenType;
import org.sonar.db.user.UserTokenDto;
import org.sonar.server.authentication.UserLastConnectionDatesUpdater;
package org.sonar.server.usertoken;
import org.junit.Test;
+import org.sonar.db.user.TokenType;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.sonar.api.utils.System2;
import org.sonar.db.DbTester;
+import org.sonar.db.user.TokenType;
import org.sonar.db.user.UserDto;
import org.sonar.db.user.UserTokenDto;
import org.sonar.server.authentication.UserLastConnectionDatesUpdater;
import org.sonar.db.project.ProjectBadgeTokenDto;
import org.sonar.server.user.UserSession;
import org.sonar.server.usertoken.TokenGenerator;
-import org.sonar.server.usertoken.TokenType;
+import org.sonar.db.user.TokenType;
import org.sonarqube.ws.ProjectBadgeToken.TokenWsResponse;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import org.sonar.db.project.ProjectDto;
import org.sonar.server.user.UserSession;
import org.sonar.server.usertoken.TokenGenerator;
-import org.sonar.server.usertoken.TokenType;
+import org.sonar.db.user.TokenType;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
public void delete(DbSession dbSession, ProjectDto project) {
dbClient.purgeDao().deleteProject(dbSession, project.getUuid(), project.getQualifier(), project.getName(), project.getKey());
dbClient.userDao().cleanHomepage(dbSession, project);
+ dbClient.userTokenDao().deleteByProjectKey(dbSession, project.getKey());
projectIndexers.commitAndIndexProjects(dbSession, singletonList(project), PROJECT_DELETION);
}
}
public void delete(DbSession dbSession, ComponentDto project) {
- checkArgument(!hasNotProjectScope(project) && !isNotDeletable(project) && project.getMainBranchProjectUuid() == null, "Only projects can be deleted");
+ checkArgument(hasProjectScope(project) && isDeletable(project) && project.getMainBranchProjectUuid() == null, "Only projects can be deleted");
dbClient.purgeDao().deleteProject(dbSession, project.uuid(), project.qualifier(), project.name(), project.getKey());
dbClient.userDao().cleanHomepage(dbSession, project);
+ dbClient.userTokenDao().deleteByProjectKey(dbSession, project.getKey());
projectIndexers.commitAndIndexComponents(dbSession, singletonList(project), PROJECT_DELETION);
}
- private static boolean hasNotProjectScope(ComponentDto project) {
- return !Scopes.PROJECT.equals(project.scope());
+ private static boolean hasProjectScope(ComponentDto project) {
+ return Scopes.PROJECT.equals(project.scope());
}
- private boolean isNotDeletable(ComponentDto project) {
+ private boolean isDeletable(ComponentDto project) {
ResourceType resourceType = resourceTypes.get(project.qualifier());
// this essentially means PROJECTS, VIEWS and APPS (not SUBVIEWS)
- return resourceType == null || !resourceType.getBooleanProperty("deletable");
+ return resourceType != null && resourceType.getBooleanProperty("deletable");
}
}
import org.sonar.db.user.UserTokenDto;
import org.sonar.server.exceptions.ServerException;
import org.sonar.server.usertoken.TokenGenerator;
-import org.sonar.server.usertoken.TokenType;
+import org.sonar.db.user.TokenType;
import org.sonarqube.ws.UserTokens;
import org.sonarqube.ws.UserTokens.GenerateWsResponse;
import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
import static org.sonar.api.utils.DateUtils.formatDateTime;
import static org.sonar.server.exceptions.BadRequestException.checkRequest;
-import static org.sonar.server.usertoken.TokenType.GLOBAL_ANALYSIS_TOKEN;
-import static org.sonar.server.usertoken.TokenType.PROJECT_ANALYSIS_TOKEN;
-import static org.sonar.server.usertoken.TokenType.USER_TOKEN;
+import static org.sonar.db.user.TokenType.GLOBAL_ANALYSIS_TOKEN;
+import static org.sonar.db.user.TokenType.PROJECT_ANALYSIS_TOKEN;
+import static org.sonar.db.user.TokenType.USER_TOKEN;
import static org.sonar.server.usertoken.ws.UserTokenSupport.ACTION_GENERATE;
import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_LOGIN;
import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_NAME;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.usertoken.TokenGenerator;
-import org.sonar.server.usertoken.TokenType;
+import org.sonar.db.user.TokenType;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.usertoken.TokenGenerator;
-import org.sonar.server.usertoken.TokenType;
+import org.sonar.db.user.TokenType;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;
*/
package org.sonar.server.component;
+import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.resources.ResourceType;
assertExists(data3);
}
+ @Test
+ public void delete_list_of_components_from_db() {
+ ComponentDto componentDto1 = db.components().insertPublicProject();
+ ComponentDto componentDto2 = db.components().insertPublicProject();
+ ComponentDto componentDto3 = db.components().insertPublicProject();
+
+ mockResourceTypeAsValidProject();
+
+ underTest.deleteComponents(dbSession, asList(componentDto1, componentDto2));
+ dbSession.commit();
+
+ assertNotExists(componentDto1);
+ assertNotExists(componentDto2);
+ assertExists(componentDto3);
+ }
+
+ @Test
+ public void fail_with_IAE_if_project_non_deletable() {
+ ComponentDto componentDto1 = db.components().insertPublicProject();
+ ComponentDto componentDto2 = db.components().insertPublicProject();
+
+ mockResourceTypeAsNonDeletable();
+
+ dbSession.commit();
+
+ List<ComponentDto> componentDtos = asList(componentDto1, componentDto2);
+
+ assertThatThrownBy(() -> underTest.deleteComponents(dbSession, componentDtos))
+ .withFailMessage("Only projects can be deleted")
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
@Test
public void delete_application_from_db_and_index() {
DbData data1 = insertProjectData();
when(mockResourceTypes.get(anyString())).thenReturn(resourceType);
}
+ private void mockResourceTypeAsNonDeletable() {
+ ResourceType resourceType = mock(ResourceType.class);
+ when(resourceType.getBooleanProperty("deletable")).thenReturn(false);
+ when(mockResourceTypes.get(anyString())).thenReturn(resourceType);
+ }
+
private void assertNotExists(DbData data) {
assertDataInDb(data, false);
assertThat(projectIndexers.hasBeenCalled(data.branch.getUuid(), PROJECT_DELETION)).isTrue();
assertThat(dbClient.branchDao().selectByUuid(dbSession, appOrProject.getUuid()).isPresent()).isEqualTo(exists);
}
+ private void assertNotExists(ComponentDto componentDto) {
+ assertComponentExists(componentDto, false);
+ }
+
+ private void assertExists(ComponentDto componentDto) {
+ assertComponentExists(componentDto, true);
+ }
+
+ private void assertComponentExists(ComponentDto componentDto, boolean exists) {
+ assertThat(dbClient.componentDao().selectByUuid(dbSession, componentDto.uuid()).isPresent()).isEqualTo(exists);
+ }
+
private static class DbData {
final ProjectDto project;
final BranchDto branch;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.usertoken.TokenGenerator;
-import org.sonar.server.usertoken.TokenType;
+import org.sonar.db.user.TokenType;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.MediaTypes;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.db.permission.GlobalPermission.SCAN;
-import static org.sonar.server.usertoken.TokenType.GLOBAL_ANALYSIS_TOKEN;
-import static org.sonar.server.usertoken.TokenType.PROJECT_ANALYSIS_TOKEN;
-import static org.sonar.server.usertoken.TokenType.USER_TOKEN;
+import static org.sonar.db.user.TokenType.GLOBAL_ANALYSIS_TOKEN;
+import static org.sonar.db.user.TokenType.PROJECT_ANALYSIS_TOKEN;
+import static org.sonar.db.user.TokenType.USER_TOKEN;
import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_LOGIN;
import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_NAME;
import static org.sonar.server.usertoken.ws.UserTokenSupport.PARAM_PROJECT_KEY;