import org.sonar.core.technicaldebt.db.CharacteristicDao;
import org.sonar.core.template.LoadedTemplateDao;
import org.sonar.core.user.AuthorizationDao;
-import org.sonar.core.user.UserDao;
import org.sonar.server.activity.db.ActivityDao;
import org.sonar.server.component.db.ComponentDao;
import org.sonar.server.component.db.SnapshotDao;
import org.sonar.server.qualityprofile.db.ActiveRuleDao;
import org.sonar.server.rule.db.RuleDao;
import org.sonar.server.user.db.GroupDao;
+import org.sonar.server.user.db.UserDao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import com.google.common.base.Objects;
import org.apache.commons.lang.StringUtils;
+import java.util.Arrays;
+
public class Message {
private final String key;
return new Message(StringUtils.defaultString(l10nKey), l10nParams);
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Message message = (Message) o;
+
+ if (!key.equals(message.key)) {
+ return false;
+ }
+ // Probably incorrect - comparing Object[] arrays with Arrays.equals
+ if (!Arrays.equals(params, message.params)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = key.hashCode();
+ result = 31 * result + (params != null ? Arrays.hashCode(params) : 0);
+ return result;
+ }
+
@Override
public String toString() {
return Objects.toStringHelper(this)
import org.sonar.core.metric.DefaultMetricFinder;
import org.sonar.core.notification.DefaultNotificationManager;
import org.sonar.core.permission.PermissionFacade;
-import org.sonar.core.persistence.DaoUtils;
-import org.sonar.core.persistence.DatabaseVersion;
-import org.sonar.core.persistence.DefaultDatabase;
-import org.sonar.core.persistence.MyBatis;
-import org.sonar.core.persistence.PreviewDatabaseFactory;
-import org.sonar.core.persistence.SemaphoreUpdater;
-import org.sonar.core.persistence.SemaphoresImpl;
+import org.sonar.core.persistence.*;
import org.sonar.core.preview.PreviewCache;
import org.sonar.core.profiling.Profiling;
import org.sonar.core.purge.PurgeProfiler;
import org.sonar.server.activity.ws.ActivitiesWebService;
import org.sonar.server.activity.ws.ActivityMapping;
import org.sonar.server.authentication.ws.AuthenticationWs;
-import org.sonar.server.batch.BatchIndex;
-import org.sonar.server.batch.BatchWs;
-import org.sonar.server.batch.GlobalReferentialsAction;
-import org.sonar.server.batch.ProjectReferentialsAction;
-import org.sonar.server.batch.UploadReportAction;
+import org.sonar.server.batch.*;
import org.sonar.server.charts.ChartFactory;
import org.sonar.server.component.ComponentCleanerService;
import org.sonar.server.component.ComponentService;
import org.sonar.server.component.DefaultRubyComponentService;
import org.sonar.server.component.db.ComponentDao;
import org.sonar.server.component.db.SnapshotDao;
-import org.sonar.server.component.ws.ComponentAppAction;
-import org.sonar.server.component.ws.ComponentsWs;
-import org.sonar.server.component.ws.EventsWs;
-import org.sonar.server.component.ws.ProjectsWs;
-import org.sonar.server.component.ws.ResourcesWs;
-import org.sonar.server.computation.AnalysisReportQueue;
-import org.sonar.server.computation.AnalysisReportService;
-import org.sonar.server.computation.AnalysisReportTaskCleaner;
-import org.sonar.server.computation.AnalysisReportTaskLauncher;
-import org.sonar.server.computation.ComputationService;
+import org.sonar.server.component.ws.*;
+import org.sonar.server.computation.*;
import org.sonar.server.computation.db.AnalysisReportDao;
-import org.sonar.server.computation.step.ComponentIndexationInDatabaseStep;
-import org.sonar.server.computation.step.ComputationStepRegistry;
-import org.sonar.server.computation.step.DataCleanerStep;
-import org.sonar.server.computation.step.DigestReportStep;
-import org.sonar.server.computation.step.IndexProjectIssuesStep;
-import org.sonar.server.computation.step.InvalidatePreviewCacheStep;
-import org.sonar.server.computation.step.SwitchSnapshotStep;
-import org.sonar.server.computation.step.SynchronizeProjectPermissionsStep;
+import org.sonar.server.computation.step.*;
import org.sonar.server.computation.ws.ActiveAnalysisReportsAction;
import org.sonar.server.computation.ws.AnalysisReportHistorySearchAction;
import org.sonar.server.computation.ws.AnalysisReportWebService;
import org.sonar.server.db.EmbeddedDatabaseFactory;
import org.sonar.server.db.migrations.DatabaseMigrations;
import org.sonar.server.db.migrations.DatabaseMigrator;
-import org.sonar.server.debt.DebtCharacteristicsXMLImporter;
-import org.sonar.server.debt.DebtModelBackup;
-import org.sonar.server.debt.DebtModelLookup;
-import org.sonar.server.debt.DebtModelOperations;
-import org.sonar.server.debt.DebtModelPluginRepository;
-import org.sonar.server.debt.DebtModelService;
-import org.sonar.server.debt.DebtModelXMLExporter;
-import org.sonar.server.debt.DebtRulesXMLImporter;
+import org.sonar.server.debt.*;
import org.sonar.server.design.FileDesignWidget;
import org.sonar.server.duplication.ws.DuplicationsJsonWriter;
import org.sonar.server.duplication.ws.DuplicationsParser;
import org.sonar.server.es.EsClient;
import org.sonar.server.es.IndexCreator;
import org.sonar.server.es.IndexRegistry;
-import org.sonar.server.issue.ActionService;
-import org.sonar.server.issue.AddTagsAction;
-import org.sonar.server.issue.AssignAction;
-import org.sonar.server.issue.CommentAction;
-import org.sonar.server.issue.InternalRubyIssueService;
-import org.sonar.server.issue.IssueBulkChangeService;
-import org.sonar.server.issue.IssueChangelogFormatter;
-import org.sonar.server.issue.IssueChangelogService;
-import org.sonar.server.issue.IssueCommentService;
-import org.sonar.server.issue.IssueQueryService;
-import org.sonar.server.issue.IssueService;
-import org.sonar.server.issue.PlanAction;
-import org.sonar.server.issue.RemoveTagsAction;
-import org.sonar.server.issue.ServerIssueStorage;
-import org.sonar.server.issue.SetSeverityAction;
-import org.sonar.server.issue.TransitionAction;
+import org.sonar.server.issue.*;
import org.sonar.server.issue.actionplan.ActionPlanService;
import org.sonar.server.issue.actionplan.ActionPlanWs;
import org.sonar.server.issue.db.IssueDao;
import org.sonar.server.issue.filter.IssueFilterService;
import org.sonar.server.issue.filter.IssueFilterWriter;
import org.sonar.server.issue.filter.IssueFilterWs;
-import org.sonar.server.issue.index.IssueAuthorizationIndexer;
-import org.sonar.server.issue.index.IssueIndex;
-import org.sonar.server.issue.index.IssueIndexDefinition;
-import org.sonar.server.issue.index.IssueIndexer;
-import org.sonar.server.issue.index.IssueNormalizer;
+import org.sonar.server.issue.index.*;
import org.sonar.server.issue.ws.IssueActionsWriter;
import org.sonar.server.issue.ws.IssueShowAction;
import org.sonar.server.issue.ws.IssuesWs;
import org.sonar.server.platform.ws.RestartHandler;
import org.sonar.server.platform.ws.ServerWs;
import org.sonar.server.platform.ws.SystemWs;
-import org.sonar.server.plugins.InstalledPluginReferentialFactory;
-import org.sonar.server.plugins.PluginDownloader;
-import org.sonar.server.plugins.ServerExtensionInstaller;
-import org.sonar.server.plugins.ServerPluginJarInstaller;
-import org.sonar.server.plugins.ServerPluginJarsInstaller;
-import org.sonar.server.plugins.ServerPluginRepository;
-import org.sonar.server.plugins.UpdateCenterClient;
-import org.sonar.server.plugins.UpdateCenterMatrixFactory;
+import org.sonar.server.plugins.*;
import org.sonar.server.properties.ProjectSettingsFactory;
import org.sonar.server.qualitygate.QgateProjectFinder;
import org.sonar.server.qualitygate.QualityGates;
import org.sonar.server.qualitygate.RegisterQualityGates;
-import org.sonar.server.qualitygate.ws.QGatesAppAction;
-import org.sonar.server.qualitygate.ws.QGatesCopyAction;
-import org.sonar.server.qualitygate.ws.QGatesCreateAction;
-import org.sonar.server.qualitygate.ws.QGatesCreateConditionAction;
-import org.sonar.server.qualitygate.ws.QGatesDeleteConditionAction;
-import org.sonar.server.qualitygate.ws.QGatesDeselectAction;
-import org.sonar.server.qualitygate.ws.QGatesDestroyAction;
-import org.sonar.server.qualitygate.ws.QGatesListAction;
-import org.sonar.server.qualitygate.ws.QGatesRenameAction;
-import org.sonar.server.qualitygate.ws.QGatesSearchAction;
-import org.sonar.server.qualitygate.ws.QGatesSelectAction;
-import org.sonar.server.qualitygate.ws.QGatesSetAsDefaultAction;
-import org.sonar.server.qualitygate.ws.QGatesShowAction;
-import org.sonar.server.qualitygate.ws.QGatesUnsetDefaultAction;
-import org.sonar.server.qualitygate.ws.QGatesUpdateConditionAction;
-import org.sonar.server.qualitygate.ws.QGatesWs;
-import org.sonar.server.qualityprofile.BuiltInProfiles;
-import org.sonar.server.qualityprofile.QProfileBackuper;
-import org.sonar.server.qualityprofile.QProfileCopier;
-import org.sonar.server.qualityprofile.QProfileExporters;
-import org.sonar.server.qualityprofile.QProfileFactory;
-import org.sonar.server.qualityprofile.QProfileLoader;
-import org.sonar.server.qualityprofile.QProfileLookup;
-import org.sonar.server.qualityprofile.QProfileProjectLookup;
-import org.sonar.server.qualityprofile.QProfileProjectOperations;
-import org.sonar.server.qualityprofile.QProfileReset;
-import org.sonar.server.qualityprofile.QProfileService;
-import org.sonar.server.qualityprofile.QProfiles;
-import org.sonar.server.qualityprofile.RegisterQualityProfiles;
-import org.sonar.server.qualityprofile.RuleActivator;
-import org.sonar.server.qualityprofile.RuleActivatorContextFactory;
+import org.sonar.server.qualitygate.ws.*;
+import org.sonar.server.qualityprofile.*;
import org.sonar.server.qualityprofile.db.ActiveRuleDao;
import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer;
-import org.sonar.server.qualityprofile.ws.BulkRuleActivationActions;
-import org.sonar.server.qualityprofile.ws.ProfilesWs;
-import org.sonar.server.qualityprofile.ws.QProfileRestoreBuiltInAction;
-import org.sonar.server.qualityprofile.ws.QProfilesWs;
-import org.sonar.server.qualityprofile.ws.RuleActivationActions;
-import org.sonar.server.rule.DefaultRuleFinder;
-import org.sonar.server.rule.DeprecatedRulesDefinitionLoader;
-import org.sonar.server.rule.RegisterRules;
-import org.sonar.server.rule.RubyRuleService;
-import org.sonar.server.rule.RuleCreator;
-import org.sonar.server.rule.RuleDefinitionsLoader;
-import org.sonar.server.rule.RuleDeleter;
-import org.sonar.server.rule.RuleOperations;
-import org.sonar.server.rule.RuleRepositories;
-import org.sonar.server.rule.RuleService;
-import org.sonar.server.rule.RuleUpdater;
+import org.sonar.server.qualityprofile.ws.*;
+import org.sonar.server.rule.*;
import org.sonar.server.rule.db.RuleDao;
import org.sonar.server.rule.index.RuleIndex;
import org.sonar.server.rule.index.RuleNormalizer;
-import org.sonar.server.rule.ws.ActiveRuleCompleter;
-import org.sonar.server.rule.ws.AppAction;
-import org.sonar.server.rule.ws.DeleteAction;
-import org.sonar.server.rule.ws.RuleMapping;
-import org.sonar.server.rule.ws.RulesWebService;
-import org.sonar.server.rule.ws.SearchAction;
-import org.sonar.server.rule.ws.TagsAction;
-import org.sonar.server.rule.ws.UpdateAction;
-import org.sonar.server.search.IndexClient;
-import org.sonar.server.search.IndexQueue;
-import org.sonar.server.search.IndexSynchronizer;
-import org.sonar.server.search.SearchClient;
-import org.sonar.server.search.SearchHealth;
+import org.sonar.server.rule.ws.*;
+import org.sonar.server.search.*;
import org.sonar.server.source.HtmlSourceDecorator;
import org.sonar.server.source.IndexSourceLinesStep;
import org.sonar.server.source.SourceService;
import org.sonar.server.source.index.SourceLineIndex;
import org.sonar.server.source.index.SourceLineIndexDefinition;
import org.sonar.server.source.index.SourceLineIndexer;
-import org.sonar.server.source.ws.HashAction;
-import org.sonar.server.source.ws.IndexAction;
-import org.sonar.server.source.ws.LinesAction;
-import org.sonar.server.source.ws.RawAction;
-import org.sonar.server.source.ws.ScmAction;
-import org.sonar.server.source.ws.ScmWriter;
+import org.sonar.server.source.ws.*;
import org.sonar.server.source.ws.ShowAction;
-import org.sonar.server.source.ws.SourcesWs;
-import org.sonar.server.startup.CleanPreviewAnalysisCache;
-import org.sonar.server.startup.CopyRequirementsFromCharacteristicsToRules;
-import org.sonar.server.startup.GeneratePluginIndex;
-import org.sonar.server.startup.JdbcDriverDeployer;
-import org.sonar.server.startup.LogServerId;
-import org.sonar.server.startup.RegisterDashboards;
-import org.sonar.server.startup.RegisterDebtModel;
-import org.sonar.server.startup.RegisterMetrics;
-import org.sonar.server.startup.RegisterNewMeasureFilters;
-import org.sonar.server.startup.RegisterPermissionTemplates;
-import org.sonar.server.startup.RegisterServletFilters;
-import org.sonar.server.startup.RenameDeprecatedPropertyKeys;
-import org.sonar.server.startup.ServerMetadataPersister;
+import org.sonar.server.startup.*;
import org.sonar.server.test.CoverageService;
-import org.sonar.server.test.ws.CoverageShowAction;
-import org.sonar.server.test.ws.CoverageWs;
-import org.sonar.server.test.ws.TestsCoveredFilesAction;
-import org.sonar.server.test.ws.TestsShowAction;
-import org.sonar.server.test.ws.TestsTestCasesAction;
-import org.sonar.server.test.ws.TestsWs;
+import org.sonar.server.test.ws.*;
import org.sonar.server.text.MacroInterpreter;
import org.sonar.server.text.RubyTextService;
import org.sonar.server.ui.JRubyI18n;
import org.sonar.server.ui.PageDecorations;
import org.sonar.server.ui.Views;
import org.sonar.server.updatecenter.ws.UpdateCenterWs;
-import org.sonar.server.user.DefaultUserService;
-import org.sonar.server.user.DoPrivileged;
-import org.sonar.server.user.GroupMembershipFinder;
-import org.sonar.server.user.GroupMembershipService;
-import org.sonar.server.user.NewUserNotifier;
-import org.sonar.server.user.SecurityRealmFactory;
-import org.sonar.server.user.UserService;
+import org.sonar.server.user.*;
import org.sonar.server.user.db.GroupDao;
+import org.sonar.server.user.db.UserDao;
+import org.sonar.server.user.db.UserGroupDao;
import org.sonar.server.user.index.UserIndexDefinition;
import org.sonar.server.user.index.UserIndexer;
import org.sonar.server.user.ws.FavoritesWs;
import org.sonar.server.user.ws.UserPropertiesWs;
import org.sonar.server.user.ws.UsersWs;
-import org.sonar.server.util.BooleanTypeValidation;
-import org.sonar.server.util.FloatTypeValidation;
-import org.sonar.server.util.IntegerTypeValidation;
-import org.sonar.server.util.StringListTypeValidation;
-import org.sonar.server.util.StringTypeValidation;
-import org.sonar.server.util.TextTypeValidation;
-import org.sonar.server.util.TypeValidations;
+import org.sonar.server.util.*;
import org.sonar.server.ws.ListingWs;
import org.sonar.server.ws.WebServiceEngine;
// users
GroupDao.class,
+ UserDao.class,
+ UserGroupDao.class,
// dashboards
DashboardDao.class,
pico.addSingleton(UserIndexDefinition.class);
pico.addSingleton(UserIndexer.class);
pico.addSingleton(UserService.class);
+ pico.addSingleton(UserCreator.class);
// groups
pico.addSingleton(GroupMembershipService.class);
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.user;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.util.List;
+
+public class NewUser {
+
+ private String login;
+ private String name;
+ private String email;
+ private List<String> scmAccounts;
+
+ private String password;
+ private String passwordConfirmation;
+
+ private boolean preventReactivation = false;
+
+ private NewUser() {
+ // No direct call to this constructor
+ }
+
+ public NewUser setLogin(String login) {
+ this.login = login;
+ return this;
+ }
+
+ public String login() {
+ return login;
+ }
+
+ public String name() {
+ return name;
+ }
+
+ public NewUser setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ @CheckForNull
+ public String email() {
+ return email;
+ }
+
+ public NewUser setEmail(@Nullable String email) {
+ this.email = email;
+ return this;
+ }
+
+ public List<String> scmAccounts() {
+ return scmAccounts;
+ }
+
+ public NewUser setScmAccounts(List<String> scmAccounts) {
+ this.scmAccounts = scmAccounts;
+ return this;
+ }
+
+ public String password() {
+ return password;
+ }
+
+ public NewUser setPassword(String password) {
+ this.password = password;
+ return this;
+ }
+
+ public String passwordConfirmation() {
+ return passwordConfirmation;
+ }
+
+ public NewUser setPasswordConfirmation(String passwordConfirmation) {
+ this.passwordConfirmation = passwordConfirmation;
+ return this;
+ }
+
+ public boolean isPreventReactivation() {
+ return preventReactivation;
+ }
+
+ /**
+ * When true, if the user already exists in status disabled, an {@link org.sonar.server.user.ReactivationException} will be thrown
+ */
+ public NewUser setPreventReactivation(boolean preventReactivation) {
+ this.preventReactivation = preventReactivation;
+ return this;
+ }
+
+ public static NewUser create() {
+ return new NewUser();
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.user;
+
+public class ReactivationException extends RuntimeException {
+
+ private String login;
+
+ public ReactivationException(String message, String login) {
+ super(message);
+ this.login = login;
+ }
+
+ public String login() {
+ return login;
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.user;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.api.platform.NewUserHandler;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.text.CsvWriter;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.user.GroupDto;
+import org.sonar.core.user.UserDto;
+import org.sonar.core.user.UserGroupDto;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.Message;
+import org.sonar.server.user.db.UserGroupDao;
+import org.sonar.server.util.Validation;
+
+import javax.annotation.Nullable;
+
+import java.io.StringWriter;
+import java.security.SecureRandom;
+import java.util.List;
+import java.util.Random;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+public class UserCreator {
+
+ private static final String LOGIN = "Login";
+ private static final String PASSWORD_CONFIRMATION = "Password confirmation";
+ private static final String PASSWORD = "Password";
+ private static final String NAME = "Name";
+ private static final String EMAIL = "Email";
+
+ private final NewUserNotifier newUserNotifier;
+ private final Settings settings;
+ private final UserGroupDao userGroupDao;
+ private final DbClient dbClient;
+ private final System2 system2;
+
+ public UserCreator(NewUserNotifier newUserNotifier, Settings settings, UserGroupDao userGroupDao, DbClient dbClient, System2 system2) {
+ this.newUserNotifier = newUserNotifier;
+ this.settings = settings;
+ this.userGroupDao = userGroupDao;
+ this.dbClient = dbClient;
+ this.system2 = system2;
+ }
+
+ public void create(NewUser newUser) {
+ validate(newUser);
+
+ DbSession dbSession = dbClient.openSession(false);
+ try {
+ String login = newUser.login();
+ UserDto existingUser = dbClient.userDao().selectNullableByLogin(dbSession, login);
+ if (existingUser != null) {
+ updateExistingUser(dbSession, newUser);
+ } else {
+ createNewUser(dbSession, newUser);
+ }
+ dbSession.commit();
+ notifyNewUser(newUser);
+ } finally {
+ dbSession.close();
+ }
+ }
+
+ private static void validate(NewUser newUser) {
+ List<Message> messages = newArrayList();
+
+ validateLogin(newUser.login(), messages);
+ validateName(newUser.name(), messages);
+ if (newUser.email().length() >= 100) {
+ messages.add(Message.of(Validation.IS_TOO_LONG_MESSAGE, EMAIL, 100));
+ }
+ validatePassword(newUser, messages);
+
+ if (!messages.isEmpty()) {
+ throw new BadRequestException(messages);
+ }
+ }
+
+ private static void validateLogin(@Nullable String login, List<Message> messages) {
+ if (Strings.isNullOrEmpty(login)) {
+ messages.add(Message.of(Validation.CANT_BE_EMPTY_MESSAGE, LOGIN));
+ } else if (!login.matches("\\A\\w[\\w\\.\\-_@\\s]+\\z")) {
+ messages.add(Message.of("user.bad_login"));
+ } else if (login.length() <= 2) {
+ messages.add(Message.of(Validation.IS_TOO_SHORT_MESSAGE, LOGIN, 2));
+ } else if (login.length() >= 255) {
+ messages.add(Message.of(Validation.IS_TOO_LONG_MESSAGE, LOGIN, 255));
+ }
+ }
+
+ private static void validateName(@Nullable String name, List<Message> messages) {
+ if (Strings.isNullOrEmpty(name)) {
+ messages.add(Message.of(Validation.CANT_BE_EMPTY_MESSAGE, NAME));
+ } else if (name.length() >= 200) {
+ messages.add(Message.of(Validation.IS_TOO_LONG_MESSAGE, NAME, 200));
+ }
+ }
+
+ private static void validatePassword(NewUser newUser, List<Message> messages) {
+ if (Strings.isNullOrEmpty(newUser.password())) {
+ messages.add(Message.of(Validation.CANT_BE_EMPTY_MESSAGE, PASSWORD));
+ }
+ if (Strings.isNullOrEmpty(newUser.passwordConfirmation())) {
+ messages.add(Message.of(Validation.CANT_BE_EMPTY_MESSAGE, PASSWORD_CONFIRMATION));
+ }
+
+ if (!Strings.isNullOrEmpty(newUser.password()) && !Strings.isNullOrEmpty(newUser.passwordConfirmation())
+ && !StringUtils.equals(newUser.password(), newUser.passwordConfirmation())) {
+ messages.add(Message.of("user.password_doesnt_match_confirmation"));
+ }
+ }
+
+ private void createNewUser(DbSession dbSession, NewUser newUser) {
+ long now = system2.now();
+ UserDto userDto = new UserDto()
+ .setLogin(newUser.login())
+ .setName(newUser.name())
+ .setEmail(newUser.email())
+ .setActive(true)
+ .setScmAccounts(convertScmAccountsToCsv(newUser))
+ .setCreatedAt(now)
+ .setUpdatedAt(now);
+ setEncryptedPassWord(newUser, userDto);
+ dbClient.userDao().insert(dbSession, userDto);
+ addDefaultGroup(dbSession, userDto);
+ }
+
+ private void updateExistingUser(DbSession dbSession, NewUser newUser) {
+ String login = newUser.login();
+ UserDto existingUser = dbClient.userDao().selectNullableByLogin(dbSession, login);
+ if (existingUser != null) {
+ if (!existingUser.isActive()) {
+ if (newUser.isPreventReactivation()) {
+ throw new ReactivationException(String.format("A disabled user with the login '%s' already exists", login), login);
+ } else {
+ existingUser.setActive(true);
+ existingUser.setUpdatedAt(system2.now());
+ dbClient.userDao().update(dbSession, existingUser);
+ addDefaultGroup(dbSession, existingUser);
+ }
+ } else {
+ throw new IllegalArgumentException(String.format("A user with the login '%s' already exists", login));
+ }
+ }
+ }
+
+ private static void setEncryptedPassWord(NewUser newUser, UserDto userDto) {
+ Random random = new SecureRandom();
+ byte[] salt = new byte[32];
+ random.nextBytes(salt);
+ String saltHex = DigestUtils.sha1Hex(salt);
+ userDto.setSalt(saltHex);
+ userDto.setCryptedPassword(DigestUtils.sha1Hex("--" + saltHex + "--" + newUser.password() + "--"));
+ }
+
+ private static String convertScmAccountsToCsv(NewUser newUser) {
+ int size = newUser.scmAccounts().size();
+ StringWriter writer = new StringWriter(size);
+ CsvWriter csv = CsvWriter.of(writer);
+ csv.values(newUser.scmAccounts().toArray(new String[size]));
+ csv.close();
+ return writer.toString();
+ }
+
+ private void notifyNewUser(NewUser newUser) {
+ newUserNotifier.onNewUser(NewUserHandler.Context.builder()
+ .setLogin(newUser.login())
+ .setName(newUser.name())
+ .setEmail(newUser.email())
+ .build());
+ }
+
+ private void addDefaultGroup(DbSession dbSession, UserDto userDto) {
+ final String defaultGroup = settings.getString(CoreProperties.CORE_DEFAULT_GROUP);
+ if (defaultGroup == null) {
+ throw new IllegalStateException(String.format("The default group property '%s' is null", CoreProperties.CORE_DEFAULT_GROUP));
+ }
+ List<GroupDto> userGroups = dbClient.groupDao().findByUserLogin(dbSession, userDto.getLogin());
+ if (!Iterables.any(userGroups, new Predicate<GroupDto>() {
+ @Override
+ public boolean apply(@Nullable GroupDto input) {
+ return input != null && input.getKey().equals(defaultGroup);
+ }
+ })) {
+ GroupDto groupDto = dbClient.groupDao().getByKey(dbSession, defaultGroup);
+ userGroupDao.insert(dbSession, new UserGroupDto().setUserId(userDto.getId()).setGroupId(groupDto.getId()));
+ }
+ }
+
+}
package org.sonar.server.user;
import org.sonar.api.ServerComponent;
+import org.sonar.core.permission.GlobalPermissions;
import org.sonar.server.user.index.UserIndexer;
public class UserService implements ServerComponent {
private final UserIndexer userIndexer;
+ private final UserCreator userCreator;
- public UserService(UserIndexer userIndexer) {
+ public UserService(UserIndexer userIndexer, UserCreator userCreator) {
this.userIndexer = userIndexer;
+ this.userCreator = userCreator;
+ }
+
+ public void create(NewUser newUser) {
+ checkPermission();
+ userCreator.create(newUser);
+ userIndexer.index();
}
public void index() {
userIndexer.index();
}
+
+ private void checkPermission() {
+ UserSession userSession = UserSession.get();
+ userSession.checkLoggedIn();
+ userSession.checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN);
+ }
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.user.db;
+
+import org.sonar.api.utils.System2;
+import org.sonar.core.persistence.DaoComponent;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.MyBatis;
+import org.sonar.core.user.UserDto;
+import org.sonar.core.user.UserMapper;
+import org.sonar.server.exceptions.NotFoundException;
+
+import javax.annotation.CheckForNull;
+
+public class UserDao extends org.sonar.core.user.UserDao implements DaoComponent {
+
+ public UserDao(MyBatis mybatis, System2 system2) {
+ super(mybatis, system2);
+ }
+
+ @CheckForNull
+ public UserDto selectNullableByLogin(DbSession session, String login) {
+ return mapper(session).selectByLogin(login);
+ }
+
+ public UserDto selectByLogin(DbSession session, String login) {
+ UserDto user = selectNullableByLogin(session, login);
+ if (user == null) {
+ throw new NotFoundException(String.format("User with login '%s' has not been found", login));
+ }
+ return user;
+ }
+
+ protected UserMapper mapper(DbSession session) {
+ return session.getMapper(UserMapper.class);
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.user.db;
+
+import org.sonar.core.persistence.DaoComponent;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.user.UserGroupDto;
+import org.sonar.core.user.UserGroupMapper;
+
+public class UserGroupDao implements DaoComponent {
+
+ public UserGroupDto insert(DbSession session, UserGroupDto dto) {
+ mapper(session).insert(dto);
+ return dto;
+ }
+
+ protected UserGroupMapper mapper(DbSession session) {
+ return session.getMapper(UserGroupMapper.class);
+ }
+}
public class Validation {
public static final String CANT_BE_EMPTY_MESSAGE = "errors.cant_be_empty";
+ public static final String IS_TOO_SHORT_MESSAGE = "errors.is_too_short";
public static final String IS_TOO_LONG_MESSAGE = "errors.is_too_long";
public static final String IS_ALREADY_USED_MESSAGE = "errors.is_already_used";
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.user;
+
+import com.google.common.base.Strings;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.api.platform.NewUserHandler;
+import org.sonar.api.utils.System2;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.core.user.GroupDto;
+import org.sonar.core.user.GroupMembershipDao;
+import org.sonar.core.user.GroupMembershipQuery;
+import org.sonar.core.user.UserDto;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.Message;
+import org.sonar.server.user.db.GroupDao;
+import org.sonar.server.user.db.UserDao;
+import org.sonar.server.user.db.UserGroupDao;
+import org.sonar.server.util.Validation;
+import org.sonar.test.DbTests;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@Category(DbTests.class)
+@RunWith(MockitoJUnitRunner.class)
+public class UserCreatorTest {
+
+ @Rule
+ public DbTester db = new DbTester();
+
+ @Mock
+ System2 system2;
+
+ @Mock
+ NewUserNotifier newUserNotifier;
+
+ @Captor
+ ArgumentCaptor<NewUserHandler.Context> newUserHandler;
+
+ Settings settings;
+ UserDao userDao;
+ GroupDao groupDao;
+ GroupMembershipFinder groupMembershipFinder;
+ DbSession session;
+
+ UserCreator userCreator;
+
+ @Before
+ public void setUp() throws Exception {
+ settings = new Settings();
+ session = db.myBatis().openSession(false);
+ userDao = new UserDao(db.myBatis(), system2);
+ groupDao = new GroupDao(system2);
+ UserGroupDao userGroupDao = new UserGroupDao();
+ GroupMembershipDao groupMembershipDao = new GroupMembershipDao(db.myBatis());
+ groupMembershipFinder = new GroupMembershipFinder(userDao, groupMembershipDao);
+
+ userCreator = new UserCreator(newUserNotifier, settings, userGroupDao, new DbClient(db.database(), db.myBatis(), userDao, groupDao), system2);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ session.close();
+ }
+
+ @Test
+ public void create_user() throws Exception {
+ when(system2.now()).thenReturn(1418215735482L);
+ createDefaultGroup();
+
+ userCreator.create(NewUser.create()
+ .setLogin("user")
+ .setName("User")
+ .setEmail("user@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password")
+ .setScmAccounts(newArrayList("u1", "u_1")));
+
+ UserDto dto = userDao.selectNullableByLogin(session, "user");
+ assertThat(dto.getId()).isNotNull();
+ assertThat(dto.getLogin()).isEqualTo("user");
+ assertThat(dto.getName()).isEqualTo("User");
+ assertThat(dto.getEmail()).isEqualTo("user@mail.com");
+ assertThat(dto.getScmAccounts()).contains("u1,u_1");
+ assertThat(dto.isActive()).isTrue();
+
+ assertThat(dto.getSalt()).isNotNull();
+ assertThat(dto.getCryptedPassword()).isNotNull();
+ assertThat(dto.getCreatedAt()).isEqualTo(1418215735482L);
+ assertThat(dto.getUpdatedAt()).isEqualTo(1418215735482L);
+ }
+
+ @Test
+ public void fail_to_create_user_if_login_already_exists() throws Exception {
+ db.prepareDbUnit(getClass(), "fail_to_create_user_if_already_exists.xml");
+
+ try {
+ userCreator.create(NewUser.create()
+ .setLogin("marius")
+ .setName("Marius")
+ .setEmail("marius@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password"));
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("A user with the login 'marius' already exists");
+ }
+ }
+
+ @Test
+ public void fail_to_create_user_if_login_already_exists_but_inactive() throws Exception {
+ db.prepareDbUnit(getClass(), "fail_to_create_user_if_already_exists_but_inactive.xml");
+
+ try {
+ userCreator.create(NewUser.create()
+ .setLogin("marius")
+ .setName("Marius")
+ .setEmail("marius@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password")
+ .setPreventReactivation(true));
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(ReactivationException.class).hasMessage("A disabled user with the login 'marius' already exists");
+ }
+ }
+
+ @Test
+ public void fail_to_create_user_with_missing_login() throws Exception {
+ try {
+ userCreator.create(NewUser.create()
+ .setLogin(null)
+ .setName("Marius")
+ .setEmail("marius@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password"));
+ fail();
+ } catch (BadRequestException e) {
+ assertThat(e.errors().messages()).containsOnly(Message.of(Validation.CANT_BE_EMPTY_MESSAGE, "Login"));
+ }
+ }
+
+ @Test
+ public void fail_to_create_user_with_invalid_login() throws Exception {
+ try {
+ userCreator.create(NewUser.create()
+ .setLogin("/marius/")
+ .setName("Marius")
+ .setEmail("marius@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password"));
+ fail();
+ } catch (BadRequestException e) {
+ assertThat(e.errors().messages()).containsOnly(Message.of("user.bad_login"));
+ }
+ }
+
+ @Test
+ public void fail_to_create_user_with_too_short_login() throws Exception {
+ try {
+ userCreator.create(NewUser.create()
+ .setLogin("ma")
+ .setName("Marius")
+ .setEmail("marius@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password"));
+ fail();
+ } catch (BadRequestException e) {
+ assertThat(e.errors().messages()).containsOnly(Message.of(Validation.IS_TOO_SHORT_MESSAGE, "Login", 2));
+ }
+ }
+
+ @Test
+ public void fail_to_create_user_with_too_long_login() throws Exception {
+ try {
+ userCreator.create(NewUser.create()
+ .setLogin(Strings.repeat("m", 256))
+ .setName("Marius")
+ .setEmail("marius@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password"));
+ fail();
+ } catch (BadRequestException e) {
+ assertThat(e.errors().messages()).containsOnly(Message.of(Validation.IS_TOO_LONG_MESSAGE, "Login", 255));
+ }
+ }
+
+ @Test
+ public void fail_to_create_user_with_missing_name() throws Exception {
+ try {
+ userCreator.create(NewUser.create()
+ .setLogin("marius")
+ .setName(null)
+ .setEmail("marius@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password"));
+ fail();
+ } catch (BadRequestException e) {
+ assertThat(e.errors().messages()).containsOnly(Message.of(Validation.CANT_BE_EMPTY_MESSAGE, "Name"));
+ }
+ }
+
+ @Test
+ public void fail_to_create_user_with_too_long_name() throws Exception {
+ try {
+ userCreator.create(NewUser.create()
+ .setLogin("marius")
+ .setName(Strings.repeat("m", 201))
+ .setEmail("marius@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password"));
+ fail();
+ } catch (BadRequestException e) {
+ assertThat(e.errors().messages()).containsOnly(Message.of(Validation.IS_TOO_LONG_MESSAGE, "Name", 200));
+ }
+ }
+
+ @Test
+ public void fail_to_create_user_with_too_long_email() throws Exception {
+ try {
+ userCreator.create(NewUser.create()
+ .setLogin("marius")
+ .setName("Marius")
+ .setEmail(Strings.repeat("m", 101))
+ .setPassword("password")
+ .setPasswordConfirmation("password"));
+ fail();
+ } catch (BadRequestException e) {
+ assertThat(e.errors().messages()).containsOnly(Message.of(Validation.IS_TOO_LONG_MESSAGE, "Email", 100));
+ }
+ }
+
+ @Test
+ public void fail_to_create_user_with_missing_password() throws Exception {
+ try {
+ userCreator.create(NewUser.create()
+ .setLogin("marius")
+ .setName("Marius")
+ .setEmail("marius@mail.com")
+ .setPassword(null)
+ .setPasswordConfirmation("password"));
+ fail();
+ } catch (BadRequestException e) {
+ assertThat(e.errors().messages()).containsOnly(Message.of(Validation.CANT_BE_EMPTY_MESSAGE, "Password"));
+ }
+ }
+
+ @Test
+ public void fail_to_create_user_with_missing_password_confirmation() throws Exception {
+ try {
+ userCreator.create(NewUser.create()
+ .setLogin("marius")
+ .setName("Marius")
+ .setEmail("marius@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation(null));
+ fail();
+ } catch (BadRequestException e) {
+ assertThat(e.errors().messages()).containsOnly(Message.of(Validation.CANT_BE_EMPTY_MESSAGE, "Password confirmation"));
+ }
+ }
+
+ @Test
+ public void fail_to_create_user_with_password_not_matching_password_confirmation() throws Exception {
+ try {
+ userCreator.create(NewUser.create()
+ .setLogin("marius")
+ .setName("Marius")
+ .setEmail("marius@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password2"));
+ fail();
+ } catch (BadRequestException e) {
+ assertThat(e.errors().messages()).containsOnly(Message.of("user.password_doesnt_match_confirmation"));
+ }
+ }
+
+ @Test
+ public void fail_to_create_user_with_many_errors() throws Exception {
+ try {
+ userCreator.create(NewUser.create()
+ .setLogin("")
+ .setName("")
+ .setEmail("marius@mail.com")
+ .setPassword("")
+ .setPasswordConfirmation(""));
+ fail();
+ } catch (BadRequestException e) {
+ assertThat(e.errors().messages()).hasSize(4);
+ }
+ }
+
+ @Test
+ public void notify_new_user() throws Exception {
+ createDefaultGroup();
+
+ userCreator.create(NewUser.create()
+ .setLogin("user")
+ .setName("User")
+ .setEmail("user@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password")
+ .setScmAccounts(newArrayList("u1", "u_1")));
+
+ verify(newUserNotifier).onNewUser(newUserHandler.capture());
+ assertThat(newUserHandler.getValue().getLogin()).isEqualTo("user");
+ assertThat(newUserHandler.getValue().getName()).isEqualTo("User");
+ assertThat(newUserHandler.getValue().getEmail()).isEqualTo("user@mail.com");
+ }
+
+ @Test
+ public void associate_default_groups_when_creating_user() throws Exception {
+ createDefaultGroup();
+
+ userCreator.create(NewUser.create()
+ .setLogin("user")
+ .setName("User")
+ .setEmail("user@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password")
+ .setScmAccounts(newArrayList("u1", "u_1")));
+
+ GroupMembershipFinder.Membership membership = groupMembershipFinder.find(GroupMembershipQuery.builder().login("user").build());
+ assertThat(membership.groups()).hasSize(1);
+ assertThat(membership.groups().get(0).name()).isEqualTo("sonar-users");
+ assertThat(membership.groups().get(0).isMember()).isTrue();
+ }
+
+ @Test
+ public void fail_to_associate_default_groups_to_user_if_no_default_group() throws Exception {
+ settings.setProperty(CoreProperties.CORE_DEFAULT_GROUP, (String) null);
+
+ try {
+ userCreator.create(NewUser.create()
+ .setLogin("user")
+ .setName("User")
+ .setEmail("user@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password")
+ .setScmAccounts(newArrayList("u1", "u_1")));
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("The default group property 'sonar.defaultGroup' is null");
+ }
+ }
+
+ @Test
+ public void reactivate_user() throws Exception {
+ db.prepareDbUnit(getClass(), "reactivate_user.xml");
+ when(system2.now()).thenReturn(1418215735486L);
+ createDefaultGroup();
+
+ userCreator.create(NewUser.create()
+ .setLogin("marius")
+ .setName("Marius2")
+ .setEmail("marius2@mail.com")
+ .setPassword("password2")
+ .setPasswordConfirmation("password2")
+ .setPreventReactivation(false));
+
+ UserDto dto = userDao.selectNullableByLogin(session, "marius");
+ assertThat(dto.isActive()).isTrue();
+ assertThat(dto.getName()).isEqualTo("Marius");
+ assertThat(dto.getEmail()).isEqualTo("marius@lesbronzes.fr");
+ assertThat(dto.getScmAccounts()).contains("ma,marius33");
+
+ assertThat(dto.getSalt()).isEqualTo("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365");
+ assertThat(dto.getCryptedPassword()).isEqualTo("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg");
+ assertThat(dto.getCreatedAt()).isEqualTo(1418215735482L);
+ assertThat(dto.getUpdatedAt()).isEqualTo(1418215735486L);
+ }
+
+ @Test
+ public void associate_default_groups_when_reactivating_user() throws Exception {
+ db.prepareDbUnit(getClass(), "associate_default_groups_when_reactivating_user.xml");
+ createDefaultGroup();
+
+ userCreator.create(NewUser.create()
+ .setLogin("marius")
+ .setName("Marius2")
+ .setEmail("marius2@mail.com")
+ .setPassword("password2")
+ .setPasswordConfirmation("password2")
+ .setPreventReactivation(false));
+
+ GroupMembershipFinder.Membership membership = groupMembershipFinder.find(GroupMembershipQuery.builder().login("marius").groupSearch("sonar-users").build());
+ assertThat(membership.groups()).hasSize(1);
+ assertThat(membership.groups().get(0).name()).isEqualTo("sonar-users");
+ assertThat(membership.groups().get(0).isMember()).isTrue();
+ }
+
+ private void createDefaultGroup() {
+ settings.setProperty(CoreProperties.CORE_DEFAULT_GROUP, "sonar-users");
+ groupDao.insert(session, new GroupDto().setName("sonar-users").setDescription("Sonar Users"));
+ session.commit();
+ }
+}
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
+import org.sonar.api.CoreProperties;
+import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.DbSession;
+import org.sonar.core.user.GroupDto;
import org.sonar.core.user.UserDto;
import org.sonar.server.db.DbClient;
import org.sonar.server.es.EsClient;
+import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.tester.ServerTester;
+import org.sonar.server.user.db.UserDao;
import org.sonar.server.user.index.UserIndexDefinition;
-import org.sonar.server.user.index.UserIndexer;
+import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.Assertions.assertThat;
public class UserServiceMediumTest {
DbClient dbClient;
DbSession session;
- UserIndexer indexer;
+ UserService service;
@Before
public void setUp() throws Exception {
dbClient = tester.get(DbClient.class);
esClient = tester.get(EsClient.class);
session = dbClient.openSession(false);
- indexer = tester.get(UserIndexer.class);
+ service = tester.get(UserService.class);
}
@After
session.close();
}
+ @Test
+ public void create_user() throws Exception {
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+ GroupDto userGroup = new GroupDto().setName(CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE);
+ dbClient.groupDao().insert(session, userGroup);
+ session.commit();
+
+ service.create(NewUser.create()
+ .setLogin("user")
+ .setName("User")
+ .setEmail("user@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password")
+ .setScmAccounts(newArrayList("u1", "u_1")));
+
+ assertThat(tester.get(UserDao.class).selectNullableByLogin(session, "user")).isNotNull();
+ assertThat(esClient.prepareGet(UserIndexDefinition.INDEX, UserIndexDefinition.TYPE_USER, "user").get().isExists()).isTrue();
+ }
+
+ @Test(expected = ForbiddenException.class)
+ public void fail_to_create_user_without_sys_admin_permission() throws Exception {
+ MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.DASHBOARD_SHARING);
+
+ service.create(NewUser.create()
+ .setLogin("user")
+ .setName("User")
+ .setEmail("user@mail.com")
+ .setPassword("password")
+ .setPasswordConfirmation("password")
+ .setScmAccounts(newArrayList("u1", "u_1")));
+ }
+
@Test
public void index() throws Exception {
UserDto userDto = new UserDto().setLogin("user").setEmail("user@mail.com").setCreatedAt(System.currentTimeMillis()).setUpdatedAt(System.currentTimeMillis());
session.commit();
assertThat(esClient.prepareGet(UserIndexDefinition.INDEX, UserIndexDefinition.TYPE_USER, "user").get().isExists()).isFalse();
- indexer.index();
+ service.index();
assertThat(esClient.prepareGet(UserIndexDefinition.INDEX, UserIndexDefinition.TYPE_USER, "user").get().isExists()).isTrue();
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.user.db;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.core.user.UserDto;
+import org.sonar.server.exceptions.NotFoundException;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.fail;
+import static org.mockito.Mockito.mock;
+
+public class UserDaoTest {
+
+ @Rule
+ public DbTester db = new DbTester();
+
+ private UserDao dao;
+ private DbSession session;
+
+ @Before
+ public void before() throws Exception {
+ this.session = db.myBatis().openSession(false);
+ System2 system2 = mock(System2.class);
+ this.dao = new UserDao(db.myBatis(), system2);
+ }
+
+ @After
+ public void after() {
+ this.session.close();
+ }
+
+ @Test
+ public void select_by_login() {
+ db.prepareDbUnit(getClass(), "select_by_login.xml");
+
+ UserDto dto = dao.selectByLogin(session, "marius");
+ assertThat(dto.getId()).isEqualTo(101);
+ assertThat(dto.getLogin()).isEqualTo("marius");
+ assertThat(dto.getName()).isEqualTo("Marius");
+ assertThat(dto.getEmail()).isEqualTo("marius@lesbronzes.fr");
+ assertThat(dto.isActive()).isTrue();
+ assertThat(dto.getScmAccounts()).isEqualTo("ma,marius33");
+ assertThat(dto.getSalt()).isEqualTo("79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365");
+ assertThat(dto.getCryptedPassword()).isEqualTo("650d2261c98361e2f67f90ce5c65a95e7d8ea2fg");
+ assertThat(dto.getCreatedAt()).isEqualTo(1418215735482L);
+ assertThat(dto.getUpdatedAt()).isEqualTo(1418215735485L);
+ }
+
+ @Test
+ public void select_by_login_with_unknown_login() {
+ try {
+ dao.selectByLogin(session, "unknown");
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(NotFoundException.class).hasMessage("User with login 'unknown' has not been found");
+ }
+ }
+
+ @Test
+ public void select_nullable_by_login() {
+ db.prepareDbUnit(getClass(), "select_by_login.xml");
+
+ assertThat(dao.selectNullableByLogin(session, "marius")).isNotNull();
+
+ assertThat(dao.selectNullableByLogin(session, "unknown")).isNull();
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.user.db;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.core.user.UserGroupDto;
+
+public class UserGroupDaoTest {
+
+ @Rule
+ public DbTester db = new DbTester();
+
+ private UserGroupDao dao;
+ private DbSession session;
+
+ @Before
+ public void before() throws Exception {
+ this.session = db.myBatis().openSession(false);
+ this.dao = new UserGroupDao();
+ }
+
+ @After
+ public void after() {
+ this.session.close();
+ }
+
+ @Test
+ public void insert() {
+ UserGroupDto userGroupDto = new UserGroupDto().setUserId(1L).setGroupId(2L);
+ dao.insert(session, userGroupDto);
+ session.commit();
+
+ db.assertDbUnit(getClass(), "insert-result.xml", "groups_users");
+ }
+}
--- /dev/null
+<dataset>
+
+ <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[false]" scm_accounts="ma,marius33" created_at="1418215735482" updated_at="1418215735485"
+ salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
+
+ <groups id="1" name="sonar-devs" description="Sonar Devs" created_at="2014-09-08" updated_at="2014-09-08"/>
+
+ <groups_users user_id="1" group_id="1"/>
+
+</dataset>
--- /dev/null
+<dataset>
+
+ <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="ma,marius33" created_at="1418215735482" updated_at="1418215735485"
+ salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
+
+</dataset>
--- /dev/null
+<dataset>
+
+ <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[false]" scm_accounts="ma,marius33" created_at="1418215735482" updated_at="1418215735485"
+ salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
+
+</dataset>
--- /dev/null
+<dataset>
+
+ <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[false]" scm_accounts="ma,marius33" created_at="1418215735482" updated_at="1418215735485"
+ salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
+
+</dataset>
--- /dev/null
+<dataset>
+
+ <users id="101" login="marius" name="Marius" email="marius@lesbronzes.fr" active="[true]" scm_accounts="ma,marius33" created_at="1418215735482" updated_at="1418215735485"
+ salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8365" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fg"/>
+ <users id="102" login="sbrandhof" name="Simon Brandhof" email="marius@lesbronzes.fr" active="[true]" scm_accounts="[null]" created_at="1418215735482" updated_at="1418215735485"
+ salt="79bd6a8e79fb8c76ac8b121cc7e8e11ad1af8366" crypted_password="650d2261c98361e2f67f90ce5c65a95e7d8ea2fh"/>
+
+</dataset>
--- /dev/null
+<dataset>
+
+ <groups_users user_id="1" group_id="2"/>
+
+</dataset>
render :text => 'ok', :status => 200
else
# case user: don't exist, WITH ERRORS when create
- # case user: exist and ACTIVE, whith or without errors when create
@user = user
user.errors.full_messages.each { |msg| @errors<<msg }
render :partial => 'users/create_form', :status => 400
loadAlias(conf, "SchemaMigration", SchemaMigrationDto.class);
loadAlias(conf, "User", UserDto.class);
loadAlias(conf, "UserRole", UserRoleDto.class);
+ loadAlias(conf, "UserGroup", UserGroupDto.class);
loadAlias(conf, "Widget", WidgetDto.class);
loadAlias(conf, "WidgetProperty", WidgetPropertyDto.class);
loadAlias(conf, "MeasureModel", MeasureModel.class);
IssueMapper.class, IssueChangeMapper.class, IssueFilterMapper.class, IssueFilterFavouriteMapper.class,
LoadedTemplateMapper.class, MeasureFilterMapper.class, Migration44Mapper.class, PermissionTemplateMapper.class, PropertiesMapper.class, PurgeMapper.class,
ResourceKeyUpdaterMapper.class, ResourceIndexerMapper.class, ResourceSnapshotMapper.class, RoleMapper.class, RuleMapper.class,
- SchemaMigrationMapper.class, SemaphoreMapper.class, UserMapper.class, GroupMapper.class, WidgetMapper.class, WidgetPropertyMapper.class,
+ SchemaMigrationMapper.class, SemaphoreMapper.class, UserMapper.class, GroupMapper.class, UserGroupMapper.class, WidgetMapper.class, WidgetPropertyMapper.class,
org.sonar.api.database.model.MeasureMapper.class, SnapshotDataMapper.class, FileSourceMapper.class, ActionPlanMapper.class,
ActionPlanStatsMapper.class,
NotificationQueueMapper.class, CharacteristicMapper.class,
return dto;
}
+ public UserDto update(SqlSession session, UserDto dto) {
+ session.getMapper(UserMapper.class).update(dto);
+ return dto;
+ }
+
/**
* Deactivate a user and drops all his preferences.
* @return false if the user does not exist, true if the existing user has been deactivated
* Search for group by name.
*
* @return the group, null if group not found
+ *
+ * TODO should be moved to GroupDao
*/
@CheckForNull
public GroupDto selectGroupByName(String name, DbSession session) {
return mapper.selectGroupByName(name);
}
+ /**
+ * TODO should be moved to GroupDao
+ */
@CheckForNull
public GroupDto selectGroupByName(String name) {
DbSession session = mybatis.openSession(false);
*/
package org.sonar.core.user;
+import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
/**
private String login;
private String name;
private String email;
+ private boolean active = true;
+ private String scmAccounts;
+ private String cryptedPassword;
+ private String salt;
private Long createdAt;
private Long updatedAt;
- private boolean active = true;
public Long getId() {
return id;
return this;
}
+ @CheckForNull
public String getEmail() {
return email;
}
return this;
}
+ public boolean isActive() {
+ return active;
+ }
+
+ public UserDto setActive(boolean b) {
+ this.active = b;
+ return this;
+ }
+
+ public String getScmAccounts() {
+ return scmAccounts;
+ }
+
+ public UserDto setScmAccounts(String scmAccounts) {
+ this.scmAccounts = scmAccounts;
+ return this;
+ }
+
+ public String getCryptedPassword() {
+ return cryptedPassword;
+ }
+
+ public UserDto setCryptedPassword(String cryptedPassword) {
+ this.cryptedPassword = cryptedPassword;
+ return this;
+ }
+
+ public String getSalt() {
+ return salt;
+ }
+
+ public UserDto setSalt(String salt) {
+ this.salt = salt;
+ return this;
+ }
+
public Long getCreatedAt() {
return createdAt;
}
return this;
}
- public boolean isActive() {
- return active;
- }
-
- public UserDto setActive(boolean b) {
- this.active = b;
- return this;
- }
-
public DefaultUser toUser() {
return new DefaultUser()
.setLogin(login)
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.core.user;
+
+public class UserGroupDto {
+
+ private Long userId;
+ private Long groupId;
+
+ public Long getUserId() {
+ return userId;
+ }
+
+ public UserGroupDto setUserId(Long userId) {
+ this.userId = userId;
+ return this;
+ }
+
+ public Long getGroupId() {
+ return groupId;
+ }
+
+ public UserGroupDto setGroupId(Long groupId) {
+ this.groupId = groupId;
+ return this;
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.core.user;
+
+public interface UserGroupMapper {
+
+ void insert(UserGroupDto userGroupDto);
+
+}
public interface UserMapper {
+ @CheckForNull
+ UserDto selectByLogin(String login);
+
@CheckForNull
UserDto selectUser(long userId);
void insert(UserDto userDto);
+ void update(UserDto userDto);
+
void removeUserFromGroups(long userId);
void deleteUserActiveDashboards(long userId);
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.core.user.UserGroupMapper">
+
+ <insert id="insert" parameterType="UserGroup" useGeneratedKeys="false">
+ INSERT INTO groups_users (user_id, group_id)
+ VALUES (#{userId}, #{groupId})
+ </insert>
+
+</mapper>
u.login as login,
u.name as name,
u.email as email,
+ u.active as "active",
+ u.scm_accounts as "scmAccounts",
+ u.salt as "salt",
+ u.crypted_password as "cryptedPassword",
u.created_at as "createdAt",
- u.updated_at as "updatedAt",
- u.active as "active"
+ u.updated_at as "updatedAt"
</sql>
- <select id="selectUser" parameterType="long" resultType="User">
+ <select id="selectByLogin" parameterType="String" resultType="User">
+ SELECT
+ <include refid="userColumns"/>
+ FROM users u WHERE u.login=#{login}
+ </select>
+
+ <select id="selectUser" parameterType="long" resultType="User">
SELECT
<include refid="userColumns"/>
FROM users u WHERE u.id=#{id}
<include refid="userColumns"/>
FROM users u WHERE
(<foreach item="login" index="index" collection="logins" open="(" separator=" or " close=")">
- u.login=#{login}
- </foreach>)
+ u.login=#{login}
+ </foreach>)
</select>
<select id="selectUsers" parameterType="map" resultType="User">
ORDER BY u.name
</select>
-
<select id="selectGroupByName" parameterType="string" resultType="Group">
SELECT id, name, description, created_at AS "createdAt", updated_at AS "updatedAt"
FROM groups WHERE name=#{id}
</update>
<insert id="insert" parameterType="User" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
- INSERT INTO users (login, name, email, active, created_at, updated_at)
- VALUES (#{login}, #{name}, #{email}, ${_true}, #{createdAt}, #{updatedAt})
+ INSERT INTO users (login, name, email, active, scm_accounts, salt, crypted_password, created_at, updated_at)
+ VALUES (#{login}, #{name}, #{email}, #{active}, #{scmAccounts}, #{salt}, #{cryptedPassword}, #{createdAt}, #{updatedAt})
+ </insert>
+
+ <insert id="update" parameterType="User">
+ UPDATE users set name=#{name}, email=#{email}, active=#{active}, scm_accounts=#{scmAccounts}, salt=#{salt}, crypted_password=#{cryptedPassword}, updated_at=#{updatedAt}
+ WHERE login = #{login}
</insert>
</mapper>
events.add_an_event=Add an event
events.name_required=Name (required)
+#------------------------------------------------------------------------------
+#
+# USER
+#
+#------------------------------------------------------------------------------
+user.bad_login=Use only letters, numbers, and .-_@ please.
+user.password_doesnt_match_confirmation=Password doesn't match confirmation.
+
#------------------------------------------------------------------------------
#
@Test
public void selectUsersByLogins_empty_logins() throws Exception {
// no need to access db
- Collection<UserDto> users = dao.selectUsersByLogins(Collections.<String> emptyList());
+ Collection<UserDto> users = dao.selectUsersByLogins(Collections.<String>emptyList());
assertThat(users).isEmpty();
}
public void insert_user() {
Long date = DateUtils.parseDate("2014-06-20").getTime();
- UserDto userDto = new UserDto().setId(1L).setLogin("john").setName("John").setEmail("jo@hn.com").setCreatedAt(date).setUpdatedAt(date);
+ UserDto userDto = new UserDto()
+ .setId(1L)
+ .setLogin("john")
+ .setName("John")
+ .setEmail("jo@hn.com")
+ .setScmAccounts("jo.hn,john2")
+ .setActive(true)
+ .setSalt("1234")
+ .setCryptedPassword("abcd")
+ .setCreatedAt(date)
+ .setUpdatedAt(date);
dao.insert(session, userDto);
session.commit();
assertThat(user.getLogin()).isEqualTo("john");
assertThat(user.getName()).isEqualTo("John");
assertThat(user.getEmail()).isEqualTo("jo@hn.com");
+ assertThat(user.isActive()).isTrue();
+ assertThat(user.getScmAccounts()).isEqualTo("jo.hn,john2");
+ assertThat(user.getSalt()).isEqualTo("1234");
+ assertThat(user.getCryptedPassword()).isEqualTo("abcd");
assertThat(user.getCreatedAt()).isEqualTo(date);
assertThat(user.getUpdatedAt()).isEqualTo(date);
+ }
+
+ @Test
+ public void update_user() {
+ setupData("update_user");
+
+ Long date = DateUtils.parseDate("2014-06-21").getTime();
+
+ UserDto userDto = new UserDto()
+ .setId(1L)
+ .setLogin("john")
+ .setName("John Doo")
+ .setEmail("jodoo@hn.com")
+ .setScmAccounts("jo.hn,john2,johndoo")
+ .setActive(false)
+ .setSalt("12345")
+ .setCryptedPassword("abcde")
+ .setUpdatedAt(date);
+ dao.update(session, userDto);
+ session.commit();
- // checkTables("insert", new String[] {"id"}, "users");
+ UserDto user = dao.getUser(1);
+ assertThat(user).isNotNull();
+ assertThat(user.getId()).isEqualTo(1L);
+ assertThat(user.getLogin()).isEqualTo("john");
+ assertThat(user.getName()).isEqualTo("John Doo");
+ assertThat(user.getEmail()).isEqualTo("jodoo@hn.com");
+ assertThat(user.isActive()).isFalse();
+ assertThat(user.getScmAccounts()).isEqualTo("jo.hn,john2,johndoo");
+ assertThat(user.getSalt()).isEqualTo("12345");
+ assertThat(user.getCryptedPassword()).isEqualTo("abcde");
+ assertThat(user.getCreatedAt()).isEqualTo(1418215735482L);
+ assertThat(user.getUpdatedAt()).isEqualTo(date);
}
@Test
--- /dev/null
+<dataset>
+ <users id="1" login="john" name="John" email="jo@hn.com" created_at="1418215735482" updated_at="1418215735482" active="[true]"/>
+</dataset>