diff options
author | Belen Pruvost <belen.pruvost@sonarsource.com> | 2021-07-14 17:15:32 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2021-07-27 20:03:02 +0000 |
commit | 09d8569209aade0e3b4cbd21c6dee9da025b245b (patch) | |
tree | 938e184ebc8296a3ae821d7225ba3fff82375e75 /server/sonar-db-dao/src/main | |
parent | 1627d31954c6fcbcbad8bd4045285df8e97cc96b (diff) | |
download | sonarqube-09d8569209aade0e3b4cbd21c6dee9da025b245b.tar.gz sonarqube-09d8569209aade0e3b4cbd21c6dee9da025b245b.zip |
SONAR-15142 Persisting audits for User operations
Diffstat (limited to 'server/sonar-db-dao/src/main')
19 files changed, 923 insertions, 11 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java index 529d9778530..bf6bda78ddb 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java @@ -26,6 +26,7 @@ import org.sonar.core.platform.Module; import org.sonar.db.alm.pat.AlmPatDao; import org.sonar.db.alm.setting.AlmSettingDao; import org.sonar.db.alm.setting.ProjectAlmSettingDao; +import org.sonar.db.audit.AuditDao; import org.sonar.db.ce.CeActivityDao; import org.sonar.db.ce.CeQueueDao; import org.sonar.db.ce.CeScannerContextDao; @@ -98,6 +99,7 @@ public class DaoModule extends Module { AnalysisPropertiesDao.class, AuthorizationDao.class, ApplicationProjectsDao.class, + AuditDao.class, BranchDao.class, CeActivityDao.class, CeQueueDao.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java index 8b1235d78af..ea6a0516646 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java @@ -24,6 +24,7 @@ import java.util.Map; import org.sonar.db.alm.pat.AlmPatDao; import org.sonar.db.alm.setting.AlmSettingDao; import org.sonar.db.alm.setting.ProjectAlmSettingDao; +import org.sonar.db.audit.AuditDao; import org.sonar.db.ce.CeActivityDao; import org.sonar.db.ce.CeQueueDao; import org.sonar.db.ce.CeScannerContextDao; @@ -100,6 +101,7 @@ public class DbClient { private final PropertiesDao propertiesDao; private final AlmSettingDao almSettingDao; private final AlmPatDao almPatDao; + private final AuditDao auditDao; private final ProjectAlmSettingDao projectAlmSettingDao; private final InternalComponentPropertiesDao internalComponentPropertiesDao; private final InternalPropertiesDao internalPropertiesDao; @@ -169,6 +171,7 @@ public class DbClient { map.put(dao.getClass(), dao); } almSettingDao = getDao(map, AlmSettingDao.class); + auditDao = getDao(map, AuditDao.class); almPatDao = getDao(map, AlmPatDao.class); projectAlmSettingDao = getDao(map, ProjectAlmSettingDao.class); schemaMigrationDao = getDao(map, SchemaMigrationDao.class); @@ -255,6 +258,10 @@ public class DbClient { return applicationProjectsDao; } + public AuditDao auditDao() { + return auditDao; + } + public ProjectAlmSettingDao projectAlmSettingDao() { return projectAlmSettingDao; } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java index e19514ae10a..2f070acc78b 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java @@ -37,6 +37,7 @@ import org.sonar.api.Startable; import org.sonar.db.alm.pat.AlmPatMapper; import org.sonar.db.alm.setting.AlmSettingMapper; import org.sonar.db.alm.setting.ProjectAlmSettingMapper; +import org.sonar.db.audit.AuditMapper; import org.sonar.db.ce.CeActivityMapper; import org.sonar.db.ce.CeQueueMapper; import org.sonar.db.ce.CeScannerContextMapper; @@ -223,6 +224,7 @@ public class MyBatis implements Startable { AlmSettingMapper.class, AnalysisPropertiesMapper.class, ApplicationProjectsMapper.class, + AuditMapper.class, AuthorizationMapper.class, BranchMapper.class, CeActivityMapper.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDao.java new file mode 100644 index 00000000000..5279b63575b --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDao.java @@ -0,0 +1,73 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.audit; + +import org.sonar.api.utils.System2; +import org.sonar.core.util.UuidFactory; +import org.sonar.db.Dao; +import org.sonar.db.DbSession; + +import java.util.List; + +import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.MAX_SIZE; + +public class AuditDao implements Dao { + + public static final int DEFAULT_PAGE_SIZE = 10000; + public static final String EXCEEDED_LENGTH = "{ \"valueLengthExceeded\": true }"; + + private final UuidFactory uuidFactory; + private final System2 system2; + + public AuditDao(System2 system2, UuidFactory uuidFactory) { + this.system2 = system2; + this.uuidFactory = uuidFactory; + } + + private static AuditMapper getMapper(DbSession dbSession) { + return dbSession.getMapper(AuditMapper.class); + } + + public List<AuditDto> selectAll(DbSession dbSession) { + return getMapper(dbSession).selectAll(); + } + + public List<AuditDto> selectByPeriod(DbSession dbSession, long beginning, long end) { + return getMapper(dbSession).selectByPeriod(beginning, end); + } + + public List<AuditDto> selectIfBeforeSelectedDate(DbSession dbSession, long end) { + return getMapper(dbSession).selectIfBeforeSelectedDate(end); + } + + public void insert(DbSession dbSession, AuditDto auditDto) { + long now = system2.now(); + auditDto.setUuid(uuidFactory.create()); + auditDto.setCreatedAt(now); + if (auditDto.getNewValue().length() > MAX_SIZE) { + auditDto.setNewValue(EXCEEDED_LENGTH); + } + getMapper(dbSession).insert(auditDto); + } + + public void deleteIfBeforeSelectedDate(DbSession dbSession, long timestamp) { + getMapper(dbSession).deleteIfBeforeSelectedDate(timestamp); + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDto.java new file mode 100644 index 00000000000..45460da3603 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditDto.java @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.audit; + +import javax.annotation.Nullable; + +public class AuditDto { + + private String uuid; + private String userUuid; + private String userLogin; + private String category; + private String operation; + private String newValue; + private long createdAt; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getUserUuid() { + return userUuid; + } + + public void setUserUuid(@Nullable String userUuid) { + this.userUuid = userUuid; + } + + public String getUserLogin() { + return userLogin; + } + + public void setUserLogin(@Nullable String userLogin) { + this.userLogin = userLogin; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public String getOperation() { + return operation; + } + + public void setOperation(String operation) { + this.operation = operation; + } + + public String getNewValue() { + return newValue; + } + + public void setNewValue(String newValue) { + this.newValue = newValue; + } + + public long getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(long createdAt) { + this.createdAt = createdAt; + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditMapper.java new file mode 100644 index 00000000000..22c7ea2ef53 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditMapper.java @@ -0,0 +1,40 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.audit; + +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface AuditMapper { + + List<AuditDto> selectAll(); + + List<AuditDto> selectByPeriod(@Param("start") long start, @Param("end") long end); + + List<AuditDto> selectIfBeforeSelectedDate(@Param("end") long end); + + void insert(@Param("dto") AuditDto auditDto); + + void delete(@Param("uuids") List<String> uuids); + + void deleteIfBeforeSelectedDate(@Param("timestamp") long timestamp); + +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java new file mode 100644 index 00000000000..535f286014c --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/AuditPersister.java @@ -0,0 +1,58 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.audit; + +import org.sonar.core.extension.PlatformLevel; +import org.sonar.db.DbSession; +import org.sonar.db.audit.model.NewValue; + +@PlatformLevel(1) +public interface AuditPersister { + + void addUserGroup(DbSession dbSession, NewValue newValue); + + void updateUserGroup(DbSession dbSession, NewValue newValue); + + void deleteUserGroup(DbSession dbSession, NewValue newValue); + + void addUser(DbSession dbSession, NewValue newValue); + + void updateUser(DbSession dbSession, NewValue newValue); + + void deactivateUser(DbSession dbSession, NewValue newValue); + + void addUserToGroup(DbSession dbSession, NewValue newValue); + + void deleteUserFromGroup(DbSession dbSession, NewValue newValue); + + void addUserProperty(DbSession dbSession, NewValue newValue); + + void updateUserProperty(DbSession dbSession, NewValue newValue); + + void deleteUserProperty(DbSession dbSession, NewValue newValue); + + void addUserToken(DbSession dbSession, NewValue newValue); + + void updateUserToken(DbSession dbSession, NewValue newValue); + + void deleteUserToken(DbSession dbSession, NewValue newValue); + + boolean isTrackedProperty(String propertyKey); +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/NewValue.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/NewValue.java new file mode 100644 index 00000000000..b46946cfc47 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/NewValue.java @@ -0,0 +1,41 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.audit.model; + +import static com.google.common.base.Strings.isNullOrEmpty; + +public interface NewValue { + + default void addField(StringBuilder sb, String field, String value, boolean isString) { + if (!isNullOrEmpty(value)) { + sb.append(field); + addQuote(sb, isString); + sb.append(value); + addQuote(sb, isString); + sb.append(","); + } + } + + private static void addQuote(StringBuilder sb, boolean isString) { + if(isString) { + sb.append("'"); + } + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/PropertyNewValue.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/PropertyNewValue.java new file mode 100644 index 00000000000..31d19b37e54 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/PropertyNewValue.java @@ -0,0 +1,67 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.audit.model; + +import org.sonar.db.user.UserPropertyDto; + +public class PropertyNewValue implements NewValue { + private String propertyKey; + private String propertyValue; + private String userUuid; + private String userLogin; + + public PropertyNewValue(UserPropertyDto userPropertyDto, String login) { + this.propertyKey = userPropertyDto.getKey(); + this.userUuid = userPropertyDto.getUserUuid(); + this.userLogin = login; + + if(!propertyKey.contains(".secured")) { + this.propertyValue = userPropertyDto.getValue(); + } + } + + public String getPropertyKey() { + return this.propertyKey; + } + + public String getPropertyValue() { + return this.propertyValue; + } + + public String getUserUuid() { + return this.userUuid; + } + + public String getUserLogin() { + return this.userLogin; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("{"); + addField(sb, "'propertyKey':", this.propertyKey, true); + addField(sb, "'propertyValue':", this.propertyValue, true); + addField(sb, "'userUuid':", this.userUuid, true); + addField(sb, "'userLogin':", this.userLogin, true); + sb.append("}"); + return sb.toString(); + } + +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/UserGroupNewValue.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/UserGroupNewValue.java new file mode 100644 index 00000000000..f22892acbdf --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/UserGroupNewValue.java @@ -0,0 +1,95 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.audit.model; + +import org.sonar.db.user.GroupDto; +import org.sonar.db.user.UserDto; +import org.sonar.db.user.UserGroupDto; + +public class UserGroupNewValue implements NewValue { + private String groupUuid; + private String name; + private String description; + private String userUuid; + private String userLogin; + + public UserGroupNewValue(String groupUuid, String name) { + this.groupUuid = groupUuid; + this.name = name; + } + + public UserGroupNewValue(GroupDto groupDto) { + this.groupUuid = groupDto.getUuid(); + this.name = groupDto.getName(); + this.description = groupDto.getDescription(); + } + + public UserGroupNewValue(GroupDto groupDto, UserDto userDto) { + this.groupUuid = groupDto.getUuid(); + this.name = groupDto.getName(); + this.userUuid = userDto.getUuid(); + this.userLogin = userDto.getLogin(); + } + + public UserGroupNewValue(UserDto userDto) { + this.userUuid = userDto.getUuid(); + this.userLogin = userDto.getLogin(); + } + + public UserGroupNewValue(UserGroupDto userGroupDto, String groupName, String userLogin) { + this.groupUuid = userGroupDto.getGroupUuid(); + this.userUuid = userGroupDto.getUserUuid(); + this.name = groupName; + this.userLogin = userLogin; + } + + public String getGroupUuid() { + return this.groupUuid; + } + + public String getName() { + return this.name; + } + + public String getDescription() { + return this.description; + } + + public String getUserUuid() { + return this.userUuid; + } + + public String getUserLogin() { + return this.userLogin; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("{"); + addField(sb, "'groupUuid':", this.groupUuid, true); + addField(sb, "'name':", this.name, true); + addField(sb, "'description':", this.description, true); + addField(sb, "'userUuid':", this.userUuid, true); + addField(sb, "'userLogin':", this.userLogin, true); + sb.append("}"); + return sb.toString(); + } + +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/UserNewValue.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/UserNewValue.java new file mode 100644 index 00000000000..5d072fd8bb1 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/UserNewValue.java @@ -0,0 +1,133 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.audit.model; + +import org.apache.commons.lang.ObjectUtils; +import org.sonar.db.user.UserDto; + +public class UserNewValue implements NewValue { + private String userUuid; + private String login; + private String name; + private String email; + private Boolean isActive; + private String scmAccounts; + private String externalId; + private String externalLogin; + private String externalIdentityProvider; + private Boolean local; + private Boolean onboarded; + private Boolean root; + private Long lastConnectionDate; + + public UserNewValue(String userUuid, String userLogin) { + this.userUuid = userUuid; + this.login = userLogin; + } + + public UserNewValue(UserDto userDto) { + this.userUuid = userDto.getUuid(); + this.login = userDto.getLogin(); + this.name = userDto.getName(); + this.email = userDto.getEmail(); + this.isActive = userDto.isActive(); + this.scmAccounts = userDto.getScmAccounts(); + this.externalId = userDto.getExternalId(); + this.externalLogin = userDto.getExternalLogin(); + this.externalIdentityProvider = userDto.getExternalIdentityProvider(); + this.local = userDto.isLocal(); + this.onboarded = userDto.isOnboarded(); + this.root = userDto.isRoot(); + this.lastConnectionDate = userDto.getLastConnectionDate(); + } + + public String getUserUuid() { + return this.userUuid; + } + + public String getLogin() { + return this.login; + } + + public String getName() { + return this.name; + } + + public String getEmail() { + return this.email; + } + + public boolean isActive() { + return this.isActive; + } + + public String getScmAccounts() { + return this.scmAccounts; + } + + public String getExternalId() { + return this.externalId; + } + + public String getExternalLogin() { + return this.externalLogin; + } + + public String getExternalIdentityProvider() { + return this.externalIdentityProvider; + } + + public boolean isLocal() { + return this.local; + } + + public boolean isOnboarded() { + return this.onboarded; + } + + public boolean isRoot() { + return this.root; + } + + public Long getLastConnectionDate() { + return this.lastConnectionDate; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("{"); + addField(sb, "'userUuid':", this.userUuid, true); + addField(sb, "'login':", this.login, true); + addField(sb, "'name':", this.name, true); + addField(sb, "'email':", this.email, true); + addField(sb, "'isActive':", ObjectUtils.toString(this.isActive), false); + addField(sb, "'scmAccounts':", this.scmAccounts, true); + addField(sb, "'externalId':", this.externalId, true); + addField(sb, "'externalLogin':", this.externalLogin, true); + addField(sb, "'externalIdentityProvider':", this.externalIdentityProvider, true); + addField(sb, "'local':", ObjectUtils.toString(this.local), false); + addField(sb, "'onboarded':", ObjectUtils.toString(this.onboarded), false); + addField(sb, "'root':", ObjectUtils.toString(this.root), false); + addField(sb, "'lastConnectionDate':", ObjectUtils.toString(this.lastConnectionDate), false); + sb.append("}"); + return sb.toString(); + } + +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/UserTokenNewValue.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/UserTokenNewValue.java new file mode 100644 index 00000000000..fd12379bdf3 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/model/UserTokenNewValue.java @@ -0,0 +1,83 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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.audit.model; + +import org.apache.commons.lang.ObjectUtils; +import org.sonar.db.user.UserDto; +import org.sonar.db.user.UserTokenDto; + +public class UserTokenNewValue implements NewValue { + private String tokenUuid; + private String userUuid; + private String userLogin; + private String tokenName; + private Long lastConnectionDate; + + public UserTokenNewValue(UserTokenDto userTokenDto, @Nullable String userLogin) { + this.tokenUuid = userTokenDto.getUuid(); + this.tokenName = userTokenDto.getName(); + this.userUuid = userTokenDto.getUserUuid(); + this.lastConnectionDate = userTokenDto.getLastConnectionDate(); + this.userLogin = userLogin; + } + + public UserTokenNewValue(UserDto userDto) { + this.userUuid = userDto.getUuid(); + this.userLogin = userDto.getLogin(); + } + + public UserTokenNewValue(UserDto userDto, String tokenName) { + this(userDto); + this.tokenName = tokenName; + } + + public String getTokenUuid() { + return this.tokenUuid; + } + + public String getUserUuid() { + return this.userUuid; + } + + public String getUserLogin() { + return this.userLogin; + } + + public String getTokenName() { + return this.tokenName; + } + + public Long getLastConnectionDate() { + return this.lastConnectionDate; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("{"); + addField(sb, "'tokenUuid':", this.tokenUuid, true); + addField(sb, "'userUuid':", this.userUuid, true); + addField(sb, "'userLogin':", this.userLogin, true); + addField(sb, "'tokenName':", this.tokenName, true); + addField(sb, "'lastConnectionDate':", ObjectUtils.toString(this.lastConnectionDate), false); + sb.append("}"); + return sb.toString(); + } + +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/audit/package-info.java b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/package-info.java new file mode 100644 index 00000000000..1c616daf42d --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/audit/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 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. + */ +@ParametersAreNonnullByDefault +package org.sonar.db.audit; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/GroupDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/GroupDao.java index a056fba97d6..80b1c7dad4e 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/GroupDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/GroupDao.java @@ -33,20 +33,28 @@ import org.sonar.db.Dao; import org.sonar.db.DaoUtils; import org.sonar.db.DbSession; import org.sonar.db.WildcardPosition; +import org.sonar.db.audit.AuditPersister; +import org.sonar.db.audit.model.UserGroupNewValue; import static org.sonar.db.DatabaseUtils.executeLargeInputs; public class GroupDao implements Dao { private final System2 system; + private AuditPersister auditPersister; public GroupDao(System2 system) { this.system = system; } + public GroupDao(System2 system, AuditPersister auditPersister) { + this(system); + this.auditPersister = auditPersister; + } + /** * @param dbSession - * @param name non-null group name + * @param name non-null group name * @return the group with the given name */ public Optional<GroupDto> selectByName(DbSession dbSession, String name) { @@ -66,8 +74,12 @@ public class GroupDao implements Dao { return executeLargeInputs(uuids, mapper(dbSession)::selectByUuids); } - public void deleteByUuid(DbSession dbSession, String groupUuid) { + public void deleteByUuid(DbSession dbSession, String groupUuid, String groupName) { mapper(dbSession).deleteByUuid(groupUuid); + + if (auditPersister != null) { + auditPersister.deleteUserGroup(dbSession, new UserGroupNewValue(groupUuid, groupName)); + } } public int countByQuery(DbSession session, @Nullable String query) { @@ -83,12 +95,22 @@ public class GroupDao implements Dao { item.setCreatedAt(createdAt) .setUpdatedAt(createdAt); mapper(session).insert(item); + + if (auditPersister != null) { + auditPersister.addUserGroup(session, new UserGroupNewValue(item.getUuid(), item.getName())); + } + return item; } public GroupDto update(DbSession session, GroupDto item) { item.setUpdatedAt(new Date(system.now())); mapper(session).update(item); + + if (auditPersister != null) { + auditPersister.updateUserGroup(session, new UserGroupNewValue(item)); + } + return item; } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java index 87e77341c71..fda3d03005d 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java @@ -34,6 +34,8 @@ import org.sonar.api.utils.System2; import org.sonar.core.util.UuidFactory; import org.sonar.db.Dao; import org.sonar.db.DbSession; +import org.sonar.db.audit.AuditPersister; +import org.sonar.db.audit.model.UserNewValue; import org.sonar.db.component.ComponentDto; import org.sonar.db.project.ProjectDto; @@ -48,11 +50,18 @@ public class UserDao implements Dao { private final System2 system2; private final UuidFactory uuidFactory; + private AuditPersister auditPersister; + public UserDao(System2 system2, UuidFactory uuidFactory) { this.system2 = system2; this.uuidFactory = uuidFactory; } + public UserDao(System2 system2, UuidFactory uuidFactory, AuditPersister auditPersister) { + this(system2, uuidFactory); + this.auditPersister = auditPersister; + } + @CheckForNull public UserDto selectByUuid(DbSession session, String uuid) { return mapper(session).selectByUuid(uuid); @@ -105,11 +114,23 @@ public class UserDao implements Dao { public UserDto insert(DbSession session, UserDto dto) { long now = system2.now(); mapper(session).insert(dto.setUuid(uuidFactory.create()).setCreatedAt(now).setUpdatedAt(now)); + + if (auditPersister != null) { + auditPersister.addUser(session, new UserNewValue(dto.getUuid(), dto.getLogin())); + } + return dto; } public UserDto update(DbSession session, UserDto dto) { + return update(session, dto, true); + } + + public UserDto update(DbSession session, UserDto dto, boolean track) { mapper(session).update(dto.setUpdatedAt(system2.now())); + if (track && auditPersister != null) { + auditPersister.updateUser(session, new UserNewValue(dto)); + } return dto; } @@ -123,6 +144,10 @@ public class UserDao implements Dao { public void deactivateUser(DbSession dbSession, UserDto user) { mapper(dbSession).deactivateUser(user.getLogin(), system2.now()); + + if (auditPersister != null) { + auditPersister.deactivateUser(dbSession, new UserNewValue(user.getUuid(), user.getLogin())); + } } public void cleanHomepage(DbSession dbSession, ProjectDto project) { diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserGroupDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserGroupDao.java index d2b589cb4ef..1c1a3cf2a90 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserGroupDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserGroupDao.java @@ -22,11 +22,27 @@ package org.sonar.db.user; import java.util.Set; import org.sonar.db.Dao; import org.sonar.db.DbSession; +import org.sonar.db.audit.AuditPersister; +import org.sonar.db.audit.model.UserGroupNewValue; public class UserGroupDao implements Dao { - public UserGroupDto insert(DbSession session, UserGroupDto dto) { + private AuditPersister auditPersister; + + public UserGroupDao() { + } + + public UserGroupDao(AuditPersister auditPersister) { + this.auditPersister = auditPersister; + } + + public UserGroupDto insert(DbSession session, UserGroupDto dto, String groupName, String login) { mapper(session).insert(dto); + + if (auditPersister != null) { + auditPersister.addUserToGroup(session, new UserGroupNewValue(dto, groupName, login)); + } + return dto; } @@ -34,16 +50,28 @@ public class UserGroupDao implements Dao { return mapper(session).selectUserUuidsInGroup(groupUuid); } - public void delete(DbSession session, String groupUuid, String userUuid) { - mapper(session).delete(groupUuid, userUuid); + public void delete(DbSession session, GroupDto group, UserDto user) { + mapper(session).delete(group.getUuid(), user.getUuid()); + + if (auditPersister != null) { + auditPersister.deleteUserFromGroup(session, new UserGroupNewValue(group, user)); + } } - public void deleteByGroupUuid(DbSession session, String groupUuid) { + public void deleteByGroupUuid(DbSession session, String groupUuid, String groupName) { mapper(session).deleteByGroupUuid(groupUuid); + + if (auditPersister != null) { + auditPersister.deleteUserFromGroup(session, new UserGroupNewValue(groupUuid, groupName)); + } } - public void deleteByUserUuid(DbSession dbSession, String userUuid) { - mapper(dbSession).deleteByUserUuid(userUuid); + public void deleteByUserUuid(DbSession dbSession, UserDto userDto) { + mapper(dbSession).deleteByUserUuid(userDto.getUuid()); + + if (auditPersister != null) { + auditPersister.deleteUserFromGroup(dbSession, new UserGroupNewValue(userDto)); + } } private static UserGroupMapper mapper(DbSession session) { diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserPropertiesDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserPropertiesDao.java index 753f0f50320..5f1bc0a022d 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserPropertiesDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserPropertiesDao.java @@ -20,35 +20,63 @@ package org.sonar.db.user; import java.util.List; +import javax.annotation.Nullable; import org.sonar.api.utils.System2; import org.sonar.core.util.UuidFactory; import org.sonar.db.Dao; import org.sonar.db.DbSession; +import org.sonar.db.audit.AuditPersister; +import org.sonar.db.audit.model.PropertyNewValue; public class UserPropertiesDao implements Dao { private final System2 system2; private final UuidFactory uuidFactory; + private AuditPersister auditPersister; + public UserPropertiesDao(System2 system2, UuidFactory uuidFactory) { this.system2 = system2; this.uuidFactory = uuidFactory; } + public UserPropertiesDao(System2 system2, UuidFactory uuidFactory, AuditPersister auditPersister) { + this(system2, uuidFactory); + this.auditPersister = auditPersister; + } + public List<UserPropertyDto> selectByUser(DbSession session, UserDto user) { return mapper(session).selectByUserUuid(user.getUuid()); } - public UserPropertyDto insertOrUpdate(DbSession session, UserPropertyDto dto) { + public UserPropertyDto insertOrUpdate(DbSession session, UserPropertyDto dto, @Nullable String login) { long now = system2.now(); + boolean isUpdate = true; if (mapper(session).update(dto, now) == 0) { mapper(session).insert(dto.setUuid(uuidFactory.create()), now); + isUpdate = false; + } + + if (auditPersister != null && auditPersister.isTrackedProperty(dto.getKey())) { + if (isUpdate) { + auditPersister.updateUserProperty(session, new PropertyNewValue(dto, login)); + } else { + auditPersister.addUserProperty(session, new PropertyNewValue(dto, login)); + } } + return dto; } public void deleteByUser(DbSession session, UserDto user) { + List<UserPropertyDto> userProperties = selectByUser(session, user); mapper(session).deleteByUserUuid(user.getUuid()); + + if (auditPersister != null) { + userProperties.stream() + .filter(p -> auditPersister.isTrackedProperty(p.getKey())) + .forEach(p -> auditPersister.deleteUserProperty(session, new PropertyNewValue(p, user.getLogin()))); + } } private static UserPropertiesMapper mapper(DbSession session) { diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserTokenDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserTokenDao.java index d5cac8104a8..ed4053f92a6 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserTokenDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserTokenDao.java @@ -24,9 +24,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.sonar.core.util.UuidFactory; import org.sonar.db.Dao; import org.sonar.db.DbSession; +import org.sonar.db.audit.AuditPersister; +import org.sonar.db.audit.model.UserTokenNewValue; import static org.sonar.core.util.stream.MoreCollectors.toList; import static org.sonar.db.DatabaseUtils.executeLargeInputs; @@ -34,18 +37,36 @@ import static org.sonar.db.DatabaseUtils.executeLargeInputs; public class UserTokenDao implements Dao { private UuidFactory uuidFactory; + private AuditPersister auditPersister; public UserTokenDao(UuidFactory uuidFactory) { this.uuidFactory = uuidFactory; } - public void insert(DbSession dbSession, UserTokenDto userTokenDto) { + public UserTokenDao(UuidFactory uuidFactory, AuditPersister auditPersister) { + this(uuidFactory); + this.auditPersister = auditPersister; + } + + public void insert(DbSession dbSession, UserTokenDto userTokenDto, String userLogin) { userTokenDto.setUuid(uuidFactory.create()); mapper(dbSession).insert(userTokenDto); + + if (auditPersister != null) { + auditPersister.addUserToken(dbSession, new UserTokenNewValue(userTokenDto, userLogin)); + } + } + + public void update(DbSession session, UserTokenDto userTokenDto, @Nullable String userLogin) { + update(session, userTokenDto, true, userLogin); } - public void update(DbSession dbSession, UserTokenDto userTokenDto) { + public void update(DbSession dbSession, UserTokenDto userTokenDto, boolean track, @Nullable String userLogin) { mapper(dbSession).update(userTokenDto); + + if (track && auditPersister != null) { + auditPersister.updateUserToken(dbSession, new UserTokenNewValue(userTokenDto, userLogin)); + } } @CheckForNull @@ -79,10 +100,18 @@ public class UserTokenDao implements Dao { public void deleteByUser(DbSession dbSession, UserDto user) { mapper(dbSession).deleteByUserUuid(user.getUuid()); + + if (auditPersister != null) { + auditPersister.deleteUserToken(dbSession, new UserTokenNewValue(user)); + } } public void deleteByUserAndName(DbSession dbSession, UserDto user, String name) { mapper(dbSession).deleteByUserUuidAndName(user.getUuid(), name); + + if (auditPersister != null) { + auditPersister.deleteUserToken(dbSession, new UserTokenNewValue(user, name)); + } } private static UserTokenMapper mapper(DbSession dbSession) { diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/audit/AuditMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/audit/AuditMapper.xml new file mode 100644 index 00000000000..31ab08d8a71 --- /dev/null +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/audit/AuditMapper.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd"> + +<mapper namespace="org.sonar.db.audit.AuditMapper"> + + <sql id="sqlColumns"> + a.uuid as "uuid", + a.user_uuid as "userUuid", + a.user_login as "userLogin", + a.category as "category", + a.operation as "operation", + a.new_value as "newValue", + a.created_at as "createdAt" + </sql> + + <select id="selectAll" resultType="org.sonar.db.audit.AuditDto"> + select <include refid="sqlColumns"/> + from + audits a + </select> + + <select id="selectByPeriod" parameterType="map" resultType="org.sonar.db.audit.AuditDto"> + select <include refid="sqlColumns"/> + from + audits a + where + a.created_at > #{start, jdbcType=BIGINT} AND + a.created_at < #{end, jdbcType=BIGINT} + </select> + + <select id="selectIfBeforeSelectedDate" parameterType="map" resultType="org.sonar.db.audit.AuditDto"> + select <include refid="sqlColumns"/> + from + audits a + where + a.created_at > #{end, jdbcType=BIGINT} + </select> + + <insert id="insert" parameterType="Map" useGeneratedKeys="false"> + INSERT INTO audits + ( + uuid, + user_uuid, + user_login, + category, + operation, + new_value, + created_at + ) + VALUES ( + #{dto.uuid, jdbcType=VARCHAR}, + #{dto.userUuid, jdbcType=VARCHAR}, + #{dto.userLogin, jdbcType=VARCHAR}, + #{dto.category, jdbcType=VARCHAR}, + #{dto.operation, jdbcType=VARCHAR}, + #{dto.newValue, jdbcType=VARCHAR}, + #{dto.createdAt, jdbcType=BIGINT} + ) + </insert> + + <delete id="deleteIfBeforeSelectedDate"> + delete from audits + where + created_at <= #{timestamp,jdbcType=BIGINT} + </delete> + +</mapper> |