--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.organization;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbSession;
+import org.sonar.db.organization.OrganizationDto;
+
+import static java.util.Objects.requireNonNull;
+
+public interface OrganizationCreation {
+ String OWNERS_GROUP_NAME = "Owners";
+ String OWNERS_GROUP_DESCRIPTION_PATTERN = "Owners of organization %s";
+ String PERM_TEMPLATE_DESCRIPTION_PATTERN = "Default permission template of organization %s";
+
+ /**
+ * Create a new Organization with the specified properties and of which the specified user will assign Administer
+ * Organization permission.
+ * <p>
+ * This method does several operations at once:
+ * <ol>
+ * <li>create an ungarded organization with the specified details</li>
+ * <li>create a group called {@link #OWNERS_GROUP_NAME Owners} with Administer Organization permission</li>
+ * <li>make the specified user a member of this group</li>
+ * <li>create a default template for the organization (which name and description will follow patterns
+ * {@link #OWNERS_GROUP_NAME} and {@link #OWNERS_GROUP_DESCRIPTION_PATTERN} based on the organization name)</li>
+ * <li>this group defines the specified permissions (which effectively makes projects public):
+ * <ul>
+ * <li>group {@link #OWNERS_GROUP_NAME Owners} : {@link UserRole#ADMIN ADMIN}</li>
+ * <li>group {@link #OWNERS_GROUP_NAME Owners} : {@link UserRole#ISSUE_ADMIN ISSUE_ADMIN}</li>
+ * <li>any one : {@link UserRole#USER USER}</li>
+ * <li>any one : {@link UserRole#CODEVIEWER CODEVIEWER}</li>
+ * </ul>
+ * </li>
+ * </ol>
+ * </p>
+ *
+ * @return the created organization
+ *
+ * @throws KeyConflictException if an organization with the specified key already exists
+ * @throws IllegalArgumentException if any field of {@code newOrganization} is invalid according to {@link OrganizationValidation}
+ */
+ OrganizationDto create(DbSession dbSession, long createUserId, NewOrganization newOrganization) throws KeyConflictException;
+
+ final class KeyConflictException extends Exception {
+ public KeyConflictException(String message) {
+ super(message);
+ }
+ }
+
+ final class NewOrganization {
+ private final String key;
+ private final String name;
+ @CheckForNull
+ private final String description;
+ @CheckForNull
+ private final String url;
+ @CheckForNull
+ private final String avatar;
+
+ private NewOrganization(Builder builder) {
+ this.key = builder.key;
+ this.name = builder.name;
+ this.description = builder.description;
+ this.url = builder.url;
+ this.avatar = builder.avatarUrl;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @CheckForNull
+ public String getDescription() {
+ return description;
+ }
+
+ @CheckForNull
+ public String getUrl() {
+ return url;
+ }
+
+ @CheckForNull
+ public String getAvatar() {
+ return avatar;
+ }
+
+ public static NewOrganization.Builder newOrganizationBuilder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String key;
+ private String name;
+ private String description;
+ private String url;
+ private String avatarUrl;
+
+ private Builder() {
+ // use factory method
+ }
+
+ public Builder setKey(String key) {
+ this.key = requireNonNull(key, "key can't be null");
+ return this;
+ }
+
+ public Builder setName(String name) {
+ this.name = requireNonNull(name, "name can't be null");
+ return this;
+ }
+
+ public Builder setDescription(@Nullable String description) {
+ this.description = description;
+ return this;
+ }
+
+ public Builder setUrl(@Nullable String url) {
+ this.url = url;
+ return this;
+ }
+
+ public Builder setAvatarUrl(@Nullable String avatarUrl) {
+ this.avatarUrl = avatarUrl;
+ return this;
+ }
+
+ public NewOrganization build() {
+ requireNonNull(key, "key can't be null");
+ requireNonNull(name, "name can't be null");
+ return new NewOrganization(this);
+ }
+ }
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.organization;
+
+import java.util.Date;
+import javax.annotation.Nullable;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.organization.DefaultTemplates;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.permission.GroupPermissionDto;
+import org.sonar.db.permission.template.PermissionTemplateDto;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.UserGroupDto;
+
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+
+public class OrganizationCreationImpl implements OrganizationCreation {
+ private final DbClient dbClient;
+ private final System2 system2;
+ private final UuidFactory uuidFactory;
+ private final OrganizationValidation organizationValidation;
+
+ public OrganizationCreationImpl(DbClient dbClient, System2 system2, UuidFactory uuidFactory,
+ OrganizationValidation organizationValidation) {
+ this.dbClient = dbClient;
+ this.system2 = system2;
+ this.uuidFactory = uuidFactory;
+ this.organizationValidation = organizationValidation;
+ }
+
+ @Override
+ public OrganizationDto create(DbSession dbSession, long creatorUserId, NewOrganization newOrganization) throws KeyConflictException {
+ validate(newOrganization);
+ String key = newOrganization.getKey();
+ if (organizationKeyIsUsed(dbSession, key)) {
+ throw new KeyConflictException(format("Organization key '%s' is already used", key));
+ }
+
+ OrganizationDto organization = insertOrganization(dbSession, newOrganization);
+ GroupDto group = insertOwnersGroup(dbSession, organization);
+ insertDefaultTemplate(dbSession, organization, group);
+ addCurrentUserToGroup(dbSession, group, creatorUserId);
+
+ dbSession.commit();
+
+ return organization;
+ }
+
+ private void validate(NewOrganization newOrganization) {
+ requireNonNull(newOrganization, "newOrganization can't be null");
+ organizationValidation.checkName(newOrganization.getName());
+ organizationValidation.checkKey(newOrganization.getKey());
+ organizationValidation.checkDescription(newOrganization.getDescription());
+ organizationValidation.checkUrl(newOrganization.getUrl());
+ organizationValidation.checkAvatar(newOrganization.getAvatar());
+ }
+
+ private OrganizationDto insertOrganization(DbSession dbSession, NewOrganization newOrganization) {
+ OrganizationDto res = new OrganizationDto()
+ .setUuid(uuidFactory.create())
+ .setName(newOrganization.getName())
+ .setKey(newOrganization.getKey())
+ .setDescription(newOrganization.getDescription())
+ .setUrl(newOrganization.getUrl())
+ .setAvatarUrl(newOrganization.getAvatar());
+ dbClient.organizationDao().insert(dbSession, res);
+ return res;
+ }
+
+ private boolean organizationKeyIsUsed(DbSession dbSession, String key) {
+ return dbClient.organizationDao().selectByKey(dbSession, key).isPresent();
+ }
+
+ private void insertDefaultTemplate(DbSession dbSession, OrganizationDto organizationDto, GroupDto group) {
+ Date now = new Date(system2.now());
+ PermissionTemplateDto permissionTemplateDto = dbClient.permissionTemplateDao().insert(
+ dbSession,
+ new PermissionTemplateDto()
+ .setOrganizationUuid(organizationDto.getUuid())
+ .setUuid(uuidFactory.create())
+ .setName("Default template")
+ .setDescription(format(PERM_TEMPLATE_DESCRIPTION_PATTERN, organizationDto.getName()))
+ .setCreatedAt(now)
+ .setUpdatedAt(now));
+
+ insertGroupPermission(dbSession, permissionTemplateDto, UserRole.ADMIN, group);
+ insertGroupPermission(dbSession, permissionTemplateDto, UserRole.ISSUE_ADMIN, group);
+ insertGroupPermission(dbSession, permissionTemplateDto, UserRole.USER, null);
+ insertGroupPermission(dbSession, permissionTemplateDto, UserRole.CODEVIEWER, null);
+
+ dbClient.organizationDao().setDefaultTemplates(
+ dbSession,
+ organizationDto.getUuid(),
+ new DefaultTemplates().setProjectUuid(permissionTemplateDto.getUuid()));
+ }
+
+ private void insertGroupPermission(DbSession dbSession, PermissionTemplateDto template, String permission, @Nullable GroupDto group) {
+ dbClient.permissionTemplateDao().insertGroupPermission(dbSession, template.getId(), group == null ? null : group.getId(), permission);
+ }
+
+ /**
+ * Owners group has an hard coded name, a description based on the organization's name and has all global permissions.
+ */
+ private GroupDto insertOwnersGroup(DbSession dbSession, OrganizationDto organization) {
+ GroupDto group = dbClient.groupDao().insert(dbSession, new GroupDto()
+ .setOrganizationUuid(organization.getUuid())
+ .setName(OWNERS_GROUP_NAME)
+ .setDescription(format(OWNERS_GROUP_DESCRIPTION_PATTERN, organization.getName())));
+ GlobalPermissions.ALL.forEach(permission -> addPermissionToGroup(dbSession, group, permission));
+ return group;
+ }
+
+ private void addPermissionToGroup(DbSession dbSession, GroupDto group, String permission) {
+ dbClient.groupPermissionDao().insert(
+ dbSession,
+ new GroupPermissionDto()
+ .setOrganizationUuid(group.getOrganizationUuid())
+ .setGroupId(group.getId())
+ .setRole(permission));
+ }
+
+ private void addCurrentUserToGroup(DbSession dbSession, GroupDto group, long createUserId) {
+ dbClient.userGroupDao().insert(
+ dbSession,
+ new UserGroupDto().setGroupId(group.getId()).setUserId(createUserId));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.organization;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+public interface OrganizationValidation {
+ int KEY_MIN_LENGTH = 2;
+ int KEY_MAX_LENGTH = 32;
+ int NAME_MIN_LENGTH = 2;
+ int NAME_MAX_LENGTH = 64;
+ int DESCRIPTION_MAX_LENGTH = 256;
+ int URL_MAX_LENGTH = 256;
+
+ /**
+ * Ensures the specified argument is a valid key by failing with an exception if it is not so.
+ * <p>
+ * A valid key is non null and its length is between {@link #KEY_MIN_LENGTH 2} and {@link #KEY_MAX_LENGTH 32}.
+ * </p>
+ *
+ * @return the argument
+ *
+ * @throws NullPointerException if argument is {@code null}.
+ * @throws IllegalArgumentException if argument is not a valid key.
+ */
+ String checkKey(String keyCandidate);
+
+ /**
+ * Ensures the specified argument is a valid name by failing with an exception if it is not so.
+ * <p>
+ * A valid name is non null and its length is between {@link #NAME_MIN_LENGTH 2} and {@link #NAME_MAX_LENGTH 64}.
+ * </p>
+ *
+ * @return the argument
+ *
+ * @throws NullPointerException if argument is {@code null}.
+ * @throws IllegalArgumentException if argument is not a valid name.
+ */
+ String checkName(String nameCandidate);
+
+ /**
+ * Ensures the specified argument is either {@code null}, empty or a valid description by failing with an exception
+ * if it is not so.
+ * <p>
+ * The length of a valid url can't be more than {@link #DESCRIPTION_MAX_LENGTH 256}.
+ * </p>
+ *
+ * @return the argument
+ *
+ * @throws IllegalArgumentException if argument is not a valid description.
+ */
+ @CheckForNull
+ String checkDescription(@Nullable String descriptionCandidate);
+
+ /**
+ * Ensures the specified argument is either {@code null}, empty or a valid URL by failing with an exception if it is
+ * not so.
+ * <p>
+ * The length of a valid URL can't be more than {@link #URL_MAX_LENGTH 256}.
+ * </p>
+ *
+ * @return the argument
+ *
+ * @throws IllegalArgumentException if argument is not a valid url.
+ */
+ @CheckForNull
+ String checkUrl(@Nullable String urlCandidate);
+
+ /**
+ * Ensures the specified argument is either {@code null}, empty or a valid avatar URL by failing with an exception if
+ * it is not so.
+ * <p>
+ * The length of a valid avatar URL can't be more than {@link #URL_MAX_LENGTH 256}.
+ * </p>
+ *
+ * @return the argument
+ *
+ * @throws IllegalArgumentException if argument is not a valid avatar url.
+ */
+ @CheckForNull
+ String checkAvatar(@Nullable String avatarCandidate);
+
+ /**
+ * Transforms the specified string into a valid key.
+ *
+ * @see #checkKey(String)
+ */
+ String generateKeyFrom(String source);
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.organization;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.Math.min;
+import static java.util.Objects.requireNonNull;
+import static org.sonar.core.util.Slug.slugify;
+
+public class OrganizationValidationImpl implements OrganizationValidation {
+
+ @Override
+ public String checkKey(String keyCandidate) {
+ requireNonNull(keyCandidate, "key can't be null");
+ checkArgument(keyCandidate.length() >= KEY_MIN_LENGTH, "Key '%s' must be at least %s chars long", keyCandidate, KEY_MIN_LENGTH);
+ checkArgument(keyCandidate.length() <= KEY_MAX_LENGTH, "Key '%s' must be at most %s chars long", keyCandidate, KEY_MAX_LENGTH);
+ checkArgument(slugify(keyCandidate).equals(keyCandidate), "Key '%s' contains at least one invalid char", keyCandidate);
+
+ return keyCandidate;
+ }
+
+ @Override
+ public String checkName(String nameCandidate) {
+ requireNonNull(nameCandidate, "name can't be null");
+
+ checkArgument(nameCandidate.length() >= NAME_MIN_LENGTH, "Name '%s' must be at least %s chars long", nameCandidate, NAME_MIN_LENGTH);
+ checkArgument(nameCandidate.length() <= NAME_MAX_LENGTH, "Name '%s' must be at most %s chars long", nameCandidate, NAME_MAX_LENGTH);
+
+ return nameCandidate;
+ }
+
+ @Override
+ public String checkDescription(@Nullable String descriptionCandidate) {
+ checkParamMaxLength(descriptionCandidate, "Description", DESCRIPTION_MAX_LENGTH);
+
+ return descriptionCandidate;
+ }
+
+ @Override
+ public String checkUrl(@Nullable String urlCandidate) {
+ checkParamMaxLength(urlCandidate, "Url", URL_MAX_LENGTH);
+
+ return urlCandidate;
+ }
+
+ @Override
+ public String checkAvatar(@Nullable String avatarCandidate) {
+ checkParamMaxLength(avatarCandidate, "Avatar", URL_MAX_LENGTH);
+
+ return avatarCandidate;
+ }
+
+ @CheckForNull
+ private static void checkParamMaxLength(@Nullable String value, String label, int maxLength) {
+ if (value != null) {
+ checkArgument(value.length() <= maxLength, "%s '%s' must be at most %s chars long", label, value, maxLength);
+ }
+ }
+
+ @Override
+ public String generateKeyFrom(String source) {
+ return slugify(source.substring(0, min(source.length(), KEY_MAX_LENGTH)));
+ }
+}
*/
package org.sonar.server.organization.ws;
-import java.util.Date;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.config.Settings;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
-import org.sonar.api.utils.System2;
-import org.sonar.api.web.UserRole;
import org.sonar.core.config.CorePropertyDefinitions;
-import org.sonar.core.permission.GlobalPermissions;
-import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
-import org.sonar.db.organization.DefaultTemplates;
import org.sonar.db.organization.OrganizationDto;
-import org.sonar.db.permission.GroupPermissionDto;
-import org.sonar.db.permission.template.PermissionTemplateDto;
-import org.sonar.db.user.GroupDto;
-import org.sonar.db.user.UserGroupDto;
+import org.sonar.server.organization.OrganizationCreation;
+import org.sonar.server.organization.OrganizationValidation;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Organizations.CreateWsResponse;
import static com.google.common.base.Preconditions.checkArgument;
-import static java.lang.Math.min;
-import static java.lang.String.format;
-import static org.sonar.core.util.Slug.slugify;
-import static org.sonar.server.organization.ws.OrganizationsWsSupport.KEY_MAX_LENGTH;
-import static org.sonar.server.organization.ws.OrganizationsWsSupport.KEY_MIN_LENGTH;
-import static org.sonar.server.organization.ws.OrganizationsWsSupport.PARAM_AVATAR_URL;
-import static org.sonar.server.organization.ws.OrganizationsWsSupport.PARAM_DESCRIPTION;
+import static org.sonar.server.organization.OrganizationCreation.NewOrganization.newOrganizationBuilder;
import static org.sonar.server.organization.ws.OrganizationsWsSupport.PARAM_KEY;
-import static org.sonar.server.organization.ws.OrganizationsWsSupport.PARAM_URL;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
public class CreateAction implements OrganizationsAction {
private static final String ACTION = "create";
- private static final String OWNERS_GROUP_NAME = "Owners";
- private static final String OWNERS_GROUP_DESCRIPTION_PATTERN = "Owners of organization %s";
- private static final String PERM_TEMPLATE_DESCRIPTION_PATTERN = "Default permission template of organization %s";
private final Settings settings;
private final UserSession userSession;
private final DbClient dbClient;
- private final UuidFactory uuidFactory;
private final OrganizationsWsSupport wsSupport;
- private final System2 system2;
+ private final OrganizationValidation organizationValidation;
+ private final OrganizationCreation organizationCreation;
- public CreateAction(Settings settings, UserSession userSession, DbClient dbClient, UuidFactory uuidFactory,
- OrganizationsWsSupport wsSupport, System2 system2) {
+ public CreateAction(Settings settings, UserSession userSession, DbClient dbClient, OrganizationsWsSupport wsSupport,
+ OrganizationValidation organizationValidation, OrganizationCreation organizationCreation) {
this.settings = settings;
this.userSession = userSession;
this.dbClient = dbClient;
- this.uuidFactory = uuidFactory;
this.wsSupport = wsSupport;
- this.system2 = system2;
+ this.organizationValidation = organizationValidation;
+ this.organizationCreation = organizationCreation;
}
@Override
String name = wsSupport.getAndCheckMandatoryName(request);
String requestKey = getAndCheckKey(request);
String key = useOrGenerateKey(requestKey, name);
- wsSupport.getAndCheckDescription(request);
- wsSupport.getAndCheckUrl(request);
- wsSupport.getAndCheckAvatar(request);
+ String description = wsSupport.getAndCheckDescription(request);
+ String url = wsSupport.getAndCheckUrl(request);
+ String avatar = wsSupport.getAndCheckAvatar(request);
try (DbSession dbSession = dbClient.openSession(false)) {
- checkKeyIsNotUsed(dbSession, key, requestKey, name);
-
- OrganizationDto organization = createOrganizationDto(dbSession, request, name, key);
- GroupDto group = createOwnersGroup(dbSession, organization);
- createDefaultTemplate(dbSession, organization, group);
- addCurrentUserToGroup(dbSession, group);
-
- dbSession.commit();
+ OrganizationDto organization = organizationCreation.create(
+ dbSession,
+ userSession.getUserId().longValue(),
+ newOrganizationBuilder()
+ .setName(name)
+ .setKey(key)
+ .setDescription(description)
+ .setUrl(url)
+ .setAvatarUrl(avatar)
+ .build());
writeResponse(request, response, organization);
+ } catch (OrganizationCreation.KeyConflictException e) {
+ checkArgument(requestKey == null, "Key '%s' is already used. Specify another one.", key);
+ checkArgument(requestKey != null, "Key '%s' generated from name '%s' is already used. Specify one.", key, name);
}
}
- private OrganizationDto createOrganizationDto(DbSession dbSession, Request request, String name, String key) {
- OrganizationDto res = new OrganizationDto()
- .setUuid(uuidFactory.create())
- .setName(name)
- .setKey(key)
- .setDescription(request.param(PARAM_DESCRIPTION))
- .setUrl(request.param(PARAM_URL))
- .setAvatarUrl(request.param(PARAM_AVATAR_URL));
- dbClient.organizationDao().insert(dbSession, res);
- return res;
- }
-
- private void createDefaultTemplate(DbSession dbSession, OrganizationDto organizationDto, GroupDto group) {
- Date now = new Date(system2.now());
- PermissionTemplateDto permissionTemplateDto = dbClient.permissionTemplateDao().insert(
- dbSession,
- new PermissionTemplateDto()
- .setOrganizationUuid(organizationDto.getUuid())
- .setUuid(uuidFactory.create())
- .setName("Default template")
- .setDescription(format(PERM_TEMPLATE_DESCRIPTION_PATTERN, organizationDto.getName()))
- .setCreatedAt(now)
- .setUpdatedAt(now));
-
- insertGroupPermission(dbSession, permissionTemplateDto, UserRole.ADMIN, group);
- insertGroupPermission(dbSession, permissionTemplateDto, UserRole.ISSUE_ADMIN, group);
- insertGroupPermission(dbSession, permissionTemplateDto, UserRole.USER, null);
- insertGroupPermission(dbSession, permissionTemplateDto, UserRole.CODEVIEWER, null);
-
- dbClient.organizationDao().setDefaultTemplates(
- dbSession,
- organizationDto.getUuid(),
- new DefaultTemplates().setProjectUuid(permissionTemplateDto.getUuid()));
- }
-
- private void insertGroupPermission(DbSession dbSession, PermissionTemplateDto template, String permission, @Nullable GroupDto group) {
- dbClient.permissionTemplateDao().insertGroupPermission(dbSession, template.getId(), group == null ? null : group.getId(), permission);
- }
-
- /**
- * Owners group has an hard coded name, a description based on the organization's name and has all global permissions.
- */
- private GroupDto createOwnersGroup(DbSession dbSession, OrganizationDto organization) {
- GroupDto group = dbClient.groupDao().insert(dbSession, new GroupDto()
- .setOrganizationUuid(organization.getUuid())
- .setName(OWNERS_GROUP_NAME)
- .setDescription(format(OWNERS_GROUP_DESCRIPTION_PATTERN, organization.getName())));
- GlobalPermissions.ALL.forEach(permission -> addPermissionToGroup(dbSession, group, permission));
- return group;
- }
-
- private void addPermissionToGroup(DbSession dbSession, GroupDto group, String permission) {
- dbClient.groupPermissionDao().insert(
- dbSession,
- new GroupPermissionDto()
- .setOrganizationUuid(group.getOrganizationUuid())
- .setGroupId(group.getId())
- .setRole(permission));
- }
-
- private void addCurrentUserToGroup(DbSession dbSession, GroupDto group) {
- dbClient.userGroupDao().insert(
- dbSession,
- new UserGroupDto().setGroupId(group.getId()).setUserId(userSession.getUserId().longValue()));
- }
-
@CheckForNull
- private static String getAndCheckKey(Request request) {
+ private String getAndCheckKey(Request request) {
String rqstKey = request.param(PARAM_KEY);
if (rqstKey != null) {
- checkArgument(rqstKey.length() >= KEY_MIN_LENGTH, "Key '%s' must be at least %s chars long", rqstKey, KEY_MIN_LENGTH);
- checkArgument(rqstKey.length() <= KEY_MAX_LENGTH, "Key '%s' must be at most %s chars long", rqstKey, KEY_MAX_LENGTH);
- checkArgument(slugify(rqstKey).equals(rqstKey), "Key '%s' contains at least one invalid char", rqstKey);
+ return organizationValidation.checkKey(rqstKey);
}
return rqstKey;
}
- private static String useOrGenerateKey(@Nullable String key, String name) {
+ private String useOrGenerateKey(@Nullable String key, String name) {
if (key == null) {
- return slugify(name.substring(0, min(name.length(), KEY_MAX_LENGTH)));
+ return organizationValidation.generateKeyFrom(name);
}
return key;
}
- private void checkKeyIsNotUsed(DbSession dbSession, String key, @Nullable String requestKey, String name) {
- boolean isUsed = checkKeyIsUsed(dbSession, key);
- checkArgument(requestKey == null || !isUsed, "Key '%s' is already used. Specify another one.", key);
- checkArgument(requestKey != null || !isUsed, "Key '%s' generated from name '%s' is already used. Specify one.", key, name);
- }
-
- private boolean checkKeyIsUsed(DbSession dbSession, String key) {
- return dbClient.organizationDao().selectByKey(dbSession, key).isPresent();
- }
-
private void writeResponse(Request request, Response response, OrganizationDto dto) {
writeProtobuf(
CreateWsResponse.newBuilder().setOrganization(wsSupport.toOrganization(dto)).build(),
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.WebService;
import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.organization.OrganizationValidation;
import org.sonarqube.ws.Organizations;
-import static com.google.common.base.Preconditions.checkArgument;
import static org.sonar.core.util.Protobuf.setNullable;
/**
static final String PARAM_DESCRIPTION = "description";
static final String PARAM_URL = "url";
static final String PARAM_AVATAR_URL = "avatar";
- static final int KEY_MIN_LENGTH = 2;
- static final int KEY_MAX_LENGTH = 32;
- static final int NAME_MIN_LENGTH = 2;
- static final int NAME_MAX_LENGTH = 64;
- static final int DESCRIPTION_MAX_LENGTH = 256;
- static final int URL_MAX_LENGTH = 256;
+
+ private final OrganizationValidation organizationValidation;
+
+ public OrganizationsWsSupport(OrganizationValidation organizationValidation) {
+ this.organizationValidation = organizationValidation;
+ }
String getAndCheckMandatoryName(Request request) {
String name = request.mandatoryParam(PARAM_NAME);
- checkName(name);
+ organizationValidation.checkName(name);
return name;
}
String getAndCheckName(Request request) {
String name = request.param(PARAM_NAME);
if (name != null) {
- checkName(name);
+ organizationValidation.checkName(name);
}
return name;
}
- private static void checkName(String name) {
- checkArgument(name.length() >= NAME_MIN_LENGTH, "Name '%s' must be at least %s chars long", name, NAME_MIN_LENGTH);
- checkArgument(name.length() <= NAME_MAX_LENGTH, "Name '%s' must be at most %s chars long", name, NAME_MAX_LENGTH);
- }
-
@CheckForNull
String getAndCheckAvatar(Request request) {
- return getAndCheckParamMaxLength(request, PARAM_AVATAR_URL, URL_MAX_LENGTH);
+ return organizationValidation.checkAvatar(request.param(PARAM_AVATAR_URL));
}
@CheckForNull
String getAndCheckUrl(Request request) {
- return getAndCheckParamMaxLength(request, PARAM_URL, URL_MAX_LENGTH);
+ return organizationValidation.checkUrl(request.param(PARAM_URL));
}
@CheckForNull
String getAndCheckDescription(Request request) {
- return getAndCheckParamMaxLength(request, PARAM_DESCRIPTION, DESCRIPTION_MAX_LENGTH);
- }
-
- @CheckForNull
- private static String getAndCheckParamMaxLength(Request request, String key, int maxLength) {
- String value = request.param(key);
- if (value != null) {
- checkArgument(value.length() <= maxLength, "%s '%s' must be at most %s chars long", key, value, maxLength);
- }
- return value;
+ return organizationValidation.checkDescription(request.param(PARAM_DESCRIPTION));
}
void addOrganizationDetailsParams(WebService.NewAction action, boolean isNameRequired) {
import org.sonar.server.metric.DefaultMetricFinder;
import org.sonar.server.metric.ws.MetricsWsModule;
import org.sonar.server.notification.NotificationModule;
+import org.sonar.server.organization.OrganizationCreationImpl;
+import org.sonar.server.organization.OrganizationValidationImpl;
import org.sonar.server.organization.ws.OrganizationsWsModule;
import org.sonar.server.permission.GroupPermissionChanger;
import org.sonar.server.permission.PermissionTemplateService;
UpdateCenterModule.class,
// organizations
+ OrganizationValidationImpl.class,
+ OrganizationCreationImpl.class,
OrganizationsWsModule.class,
// quality profile
@ServerSide
public class UserUpdater {
- public static final String SQ_AUTHORITY = "sonarqube";
+ private static final String SQ_AUTHORITY = "sonarqube";
private static final String LOGIN_PARAM = "Login";
private static final String PASSWORD_PARAM = "Password";
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.organization;
+
+import java.util.List;
+import java.util.Optional;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.organization.DefaultTemplates;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.permission.template.PermissionTemplateDto;
+import org.sonar.db.permission.template.PermissionTemplateGroupDto;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.db.user.UserMembershipDto;
+import org.sonar.db.user.UserMembershipQuery;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.fail;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.organization.OrganizationCreation.NewOrganization.newOrganizationBuilder;
+
+public class OrganizationCreationImplTest {
+ private static final long SOME_USER_ID = 1L;
+ private static final String SOME_UUID = "org-uuid";
+ private static final long SOME_DATE = 12893434L;
+ private OrganizationCreation.NewOrganization FULL_POPULATED_NEW_ORGANIZATION = newOrganizationBuilder()
+ .setName("a-name")
+ .setKey("a-key")
+ .setDescription("a-description")
+ .setUrl("a-url")
+ .setAvatarUrl("a-avatar")
+ .build();
+
+ private System2 system2 = mock(System2.class);
+
+ @Rule
+ public DbTester dbTester = DbTester.create(system2);
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private DbSession dbSession = dbTester.getSession();
+
+ private IllegalArgumentException exceptionThrownByOrganizationValidation = new IllegalArgumentException("simulate IAE thrown by OrganizationValidation");
+ private DbClient dbClient = dbTester.getDbClient();
+ private UuidFactory uuidFactory = mock(UuidFactory.class);
+ private OrganizationValidation organizationValidation = mock(OrganizationValidation.class);
+
+ private OrganizationCreationImpl underTest = new OrganizationCreationImpl(dbClient, system2, uuidFactory, organizationValidation);
+
+ @Test
+ public void create_throws_NPE_if_NewOrganization_arg_is_null() throws OrganizationCreation.KeyConflictException {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("newOrganization can't be null");
+
+ underTest.create(dbSession, SOME_USER_ID, null);
+ }
+
+ @Test
+ public void create_throws_exception_thrown_by_checkValidKey() throws OrganizationCreation.KeyConflictException {
+ when(organizationValidation.checkKey(FULL_POPULATED_NEW_ORGANIZATION.getKey()))
+ .thenThrow(exceptionThrownByOrganizationValidation);
+
+ createThrowsExceptionThrownByOrganizationValidation();
+ }
+
+ private void createThrowsExceptionThrownByOrganizationValidation() throws OrganizationCreation.KeyConflictException {
+ try {
+ underTest.create(dbSession, SOME_USER_ID, FULL_POPULATED_NEW_ORGANIZATION);
+ fail(exceptionThrownByOrganizationValidation + " should have been thrown");
+ } catch (IllegalArgumentException e) {
+ assertThat(e).isSameAs(exceptionThrownByOrganizationValidation);
+ }
+ }
+
+ @Test
+ public void create_throws_exception_thrown_by_checkValidDescription() throws OrganizationCreation.KeyConflictException {
+ when(organizationValidation.checkDescription(FULL_POPULATED_NEW_ORGANIZATION.getDescription())).thenThrow(exceptionThrownByOrganizationValidation);
+
+ createThrowsExceptionThrownByOrganizationValidation();
+ }
+
+ @Test
+ public void create_throws_exception_thrown_by_checkValidUrl() throws OrganizationCreation.KeyConflictException {
+ when(organizationValidation.checkUrl(FULL_POPULATED_NEW_ORGANIZATION.getUrl())).thenThrow(exceptionThrownByOrganizationValidation);
+
+ createThrowsExceptionThrownByOrganizationValidation();
+ }
+
+ @Test
+ public void create_throws_exception_thrown_by_checkValidAvatar() throws OrganizationCreation.KeyConflictException {
+ when(organizationValidation.checkAvatar(FULL_POPULATED_NEW_ORGANIZATION.getAvatar())).thenThrow(exceptionThrownByOrganizationValidation);
+
+ createThrowsExceptionThrownByOrganizationValidation();
+ }
+
+ @Test
+ public void create_fails_with_KeyConflictException_if_org_with_key_in_NewOrganization_arg_already_exists_in_db() throws OrganizationCreation.KeyConflictException {
+ dbTester.organizations().insertForKey(FULL_POPULATED_NEW_ORGANIZATION.getKey());
+
+ expectedException.expect(OrganizationCreation.KeyConflictException.class);
+ expectedException.expectMessage("Organization key '" + FULL_POPULATED_NEW_ORGANIZATION.getKey() + "' is already used");
+
+ underTest.create(dbSession, SOME_USER_ID, FULL_POPULATED_NEW_ORGANIZATION);
+ }
+
+ @Test
+ public void create_creates_unguarded_organization_with_properties_from_NewOrganization_arg() throws OrganizationCreation.KeyConflictException {
+ mockForSuccessfulInsert(SOME_UUID, SOME_DATE);
+
+ underTest.create(dbSession, SOME_USER_ID, FULL_POPULATED_NEW_ORGANIZATION);
+
+ OrganizationDto organization = dbClient.organizationDao().selectByKey(dbSession, FULL_POPULATED_NEW_ORGANIZATION.getKey()).get();
+ assertThat(organization.getUuid()).isEqualTo(SOME_UUID);
+ assertThat(organization.getKey()).isEqualTo(FULL_POPULATED_NEW_ORGANIZATION.getKey());
+ assertThat(organization.getName()).isEqualTo(FULL_POPULATED_NEW_ORGANIZATION.getName());
+ assertThat(organization.getDescription()).isEqualTo(FULL_POPULATED_NEW_ORGANIZATION.getDescription());
+ assertThat(organization.getUrl()).isEqualTo(FULL_POPULATED_NEW_ORGANIZATION.getUrl());
+ assertThat(organization.getAvatarUrl()).isEqualTo(FULL_POPULATED_NEW_ORGANIZATION.getAvatar());
+ assertThat(organization.getCreatedAt()).isEqualTo(SOME_DATE);
+ assertThat(organization.getUpdatedAt()).isEqualTo(SOME_DATE);
+ }
+
+ @Test
+ public void create_creates_owners_group_with_all_permissions_for_new_organization_and_add_current_user_to_it() throws OrganizationCreation.KeyConflictException {
+ UserDto user = dbTester.users().insertUser();
+
+ mockForSuccessfulInsert(SOME_UUID, SOME_DATE);
+
+ underTest.create(dbSession, user.getId(), FULL_POPULATED_NEW_ORGANIZATION);
+
+ OrganizationDto organization = dbClient.organizationDao().selectByKey(dbSession, FULL_POPULATED_NEW_ORGANIZATION.getKey()).get();
+ Optional<GroupDto> groupDtoOptional = dbClient.groupDao().selectByName(dbSession, organization.getUuid(), "Owners");
+ assertThat(groupDtoOptional).isNotEmpty();
+ GroupDto groupDto = groupDtoOptional.get();
+ assertThat(groupDto.getDescription()).isEqualTo("Owners of organization " + FULL_POPULATED_NEW_ORGANIZATION.getName());
+ assertThat(dbClient.groupPermissionDao().selectGlobalPermissionsOfGroup(dbSession, groupDto.getOrganizationUuid(), groupDto.getId()))
+ .containsOnly(GlobalPermissions.ALL.toArray(new String[GlobalPermissions.ALL.size()]));
+ List<UserMembershipDto> members = dbClient.groupMembershipDao().selectMembers(
+ dbSession,
+ UserMembershipQuery.builder().groupId(groupDto.getId()).membership(UserMembershipQuery.IN).build(), 0, Integer.MAX_VALUE);
+ assertThat(members)
+ .extracting(UserMembershipDto::getLogin)
+ .containsOnly(user.getLogin());
+ }
+
+ @Test
+ public void create_does_not_require_description_url_and_avatar_to_be_non_null() throws OrganizationCreation.KeyConflictException {
+ mockForSuccessfulInsert(SOME_UUID, SOME_DATE);
+
+ underTest.create(dbSession, SOME_USER_ID, newOrganizationBuilder()
+ .setKey("key")
+ .setName("name")
+ .build());
+
+ OrganizationDto organization = dbClient.organizationDao().selectByKey(dbSession, "key").get();
+ assertThat(organization.getKey()).isEqualTo("key");
+ assertThat(organization.getName()).isEqualTo("name");
+ assertThat(organization.getDescription()).isNull();
+ assertThat(organization.getUrl()).isNull();
+ assertThat(organization.getAvatarUrl()).isNull();
+ }
+
+ @Test
+ public void create_creates_default_template_for_new_organization() throws OrganizationCreation.KeyConflictException {
+ mockForSuccessfulInsert(SOME_UUID, SOME_DATE);
+
+ underTest.create(dbSession, SOME_USER_ID, FULL_POPULATED_NEW_ORGANIZATION);
+
+ OrganizationDto organization = dbClient.organizationDao().selectByKey(dbSession, FULL_POPULATED_NEW_ORGANIZATION.getKey()).get();
+ GroupDto ownersGroup = dbClient.groupDao().selectByName(dbSession, organization.getUuid(), "Owners").get();
+ PermissionTemplateDto defaultTemplate = dbClient.permissionTemplateDao().selectByName(dbSession, organization.getUuid(), "default template");
+ assertThat(defaultTemplate.getName()).isEqualTo("Default template");
+ assertThat(defaultTemplate.getDescription()).isEqualTo("Default permission template of organization " + FULL_POPULATED_NEW_ORGANIZATION.getName());
+ DefaultTemplates defaultTemplates = dbClient.organizationDao().getDefaultTemplates(dbSession, organization.getUuid()).get();
+ assertThat(defaultTemplates.getProjectUuid()).isEqualTo(defaultTemplate.getUuid());
+ assertThat(defaultTemplates.getViewUuid()).isNull();
+ assertThat(dbClient.permissionTemplateDao().selectGroupPermissionsByTemplateId(dbSession, defaultTemplate.getId()))
+ .extracting(PermissionTemplateGroupDto::getGroupId, PermissionTemplateGroupDto::getPermission)
+ .containsOnly(
+ tuple(ownersGroup.getId(), UserRole.ADMIN), tuple(ownersGroup.getId(), UserRole.ISSUE_ADMIN),
+ tuple(0L, UserRole.USER), tuple(0L, UserRole.CODEVIEWER));
+ }
+
+ private void mockForSuccessfulInsert(String orgUuid, long orgDate) {
+ when(uuidFactory.create()).thenReturn(orgUuid);
+ when(system2.now()).thenReturn(orgDate);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.organization;
+
+import java.util.Random;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+public class OrganizationValidationImplTest {
+ private static final String STRING_32_CHARS = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+ private static final String STRING_64_CHARS = STRING_32_CHARS + STRING_32_CHARS;
+ private static final String STRING_256_CHARS = STRING_64_CHARS + STRING_64_CHARS + STRING_64_CHARS + STRING_64_CHARS;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private OrganizationValidationImpl underTest = new OrganizationValidationImpl();
+
+ @Test
+ public void checkValidKey_throws_NPE_if_arg_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("key can't be null");
+
+ underTest.checkKey(null);
+ }
+
+ @Test
+ public void checkValidKey_throws_IAE_if_arg_is_empty() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Key '' must be at least 2 chars long");
+
+ underTest.checkKey("");
+ }
+
+ @Test
+ public void checkValidKey_throws_IAE_if_arg_is_1_char_long() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Key 'a' must be at least 2 chars long");
+
+ underTest.checkKey("a");
+ }
+
+ @Test
+ public void checkValidKey_does_not_fail_if_arg_is_2_to_32_chars_long() {
+ String str = "aa";
+ for (int i = 0; i < 31; i++) {
+ underTest.checkKey(str);
+ str += "a";
+ }
+ }
+
+ @Test
+ public void checkValidKey_throws_IAE_if_arg_is_33_or_more_chars_long() {
+ String str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+ underTest.checkKey(str);
+ for (int i = 0; i < 5 + Math.abs(new Random().nextInt(10)); i++) {
+ str += "c";
+ try {
+ underTest.checkKey(str);
+ fail("A IllegalArgumentException should have been thrown");
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Key '" + str + "' must be at most 32 chars long");
+ }
+ }
+ }
+
+ @Test
+ public void checkValidKey_throws_IAE_if_arg_contains_invalid_chars() {
+ char[] invalidChars = {'é', '<', '@'};
+
+ for (char invalidChar : invalidChars) {
+ String str = "aa" + invalidChar;
+ try {
+ underTest.checkKey(str);
+ fail("A IllegalArgumentException should have been thrown");
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Key '" + str + "' contains at least one invalid char");
+ }
+ }
+ }
+
+ @Test
+ public void checkValidName_throws_NPE_if_arg_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("name can't be null");
+
+ underTest.checkName(null);
+ }
+
+ @Test
+ public void checkValidName_throws_IAE_if_arg_is_empty() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Name '' must be at least 2 chars long");
+
+ underTest.checkName("");
+ }
+
+ @Test
+ public void checkValidName_throws_IAE_if_arg_is_1_char_long() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Name 'a' must be at least 2 chars long");
+
+ underTest.checkName("a");
+ }
+
+ @Test
+ public void checkValidName_does_not_fail_if_arg_is_2_to_32_chars_long() {
+ String str = "aa";
+ for (int i = 0; i < 63; i++) {
+ underTest.checkName(str);
+ str += "a";
+ }
+ }
+
+ @Test
+ public void checkValidName_throws_IAE_if_arg_is_65_or_more_chars_long() {
+ String str = STRING_64_CHARS;
+ underTest.checkName(str);
+ for (int i = 0; i < 5 + Math.abs(new Random().nextInt(10)); i++) {
+ str += "c";
+ try {
+ underTest.checkName(str);
+ fail("A IllegalArgumentException should have been thrown");
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Name '" + str + "' must be at most 64 chars long");
+ }
+ }
+ }
+
+ @Test
+ public void checkValidDescription_does_not_fail_if_arg_is_null() {
+ underTest.checkDescription(null);
+ }
+
+ @Test
+ public void checkValidDescription_does_not_fail_if_arg_is_empty() {
+ underTest.checkDescription("");
+ }
+
+ @Test
+ public void checkValidDescription_does_not_fail_if_arg_is_1_to_256_chars_long() {
+ String str = "1";
+ for (int i = 0; i < 256; i++) {
+ underTest.checkDescription(str);
+ str += "a";
+ }
+ }
+
+ @Test
+ public void checkValidDescription_throws_IAE_if_arg_is_more_than_256_chars_long() {
+ String str = STRING_256_CHARS;
+ underTest.checkDescription(str);
+ for (int i = 0; i < 5 + Math.abs(new Random().nextInt(10)); i++) {
+ str += "c";
+ try {
+ underTest.checkDescription(str);
+ fail("A IllegalArgumentException should have been thrown");
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Description '" + str + "' must be at most 256 chars long");
+ }
+ }
+ }
+
+ @Test
+ public void checkValidUrl_does_not_fail_if_arg_is_null() {
+ underTest.checkUrl(null);
+ }
+
+ @Test
+ public void checkValidUrl_does_not_fail_if_arg_is_1_to_256_chars_long() {
+ String str = "1";
+ for (int i = 0; i < 256; i++) {
+ underTest.checkUrl(str);
+ str += "a";
+ }
+ }
+
+ @Test
+ public void checkValidUrl_throws_IAE_if_arg_is_more_than_256_chars_long() {
+ String str = STRING_256_CHARS;
+ underTest.checkUrl(str);
+ for (int i = 0; i < 5 + Math.abs(new Random().nextInt(10)); i++) {
+ str += "c";
+ try {
+ underTest.checkUrl(str);
+ fail("A IllegalArgumentException should have been thrown");
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Url '" + str + "' must be at most 256 chars long");
+ }
+ }
+ }
+
+ @Test
+ public void checkValidAvatar_does_not_fail_if_arg_is_null() {
+ underTest.checkAvatar(null);
+ }
+
+ @Test
+ public void checkValidAvatar_does_not_fail_if_arg_is_1_to_256_chars_long() {
+ String str = "1";
+ for (int i = 0; i < 256; i++) {
+ underTest.checkAvatar(str);
+ str += "a";
+ }
+ }
+
+ @Test
+ public void checkValidAvatar_throws_IAE_if_arg_is_more_than_256_chars_long() {
+ String str = STRING_256_CHARS;
+ underTest.checkAvatar(str);
+ for (int i = 0; i < 5 + Math.abs(new Random().nextInt(10)); i++) {
+ str += "c";
+ try {
+ underTest.checkAvatar(str);
+ fail("A IllegalArgumentException should have been thrown");
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Avatar '" + str + "' must be at most 256 chars long");
+ }
+ }
+ }
+
+ @Test
+ public void generateKeyFrom_returns_slug_of_arg() {
+ assertThat(underTest.generateKeyFrom("foo")).isEqualTo("foo");
+ assertThat(underTest.generateKeyFrom(" FOO ")).isEqualTo("foo");
+ assertThat(underTest.generateKeyFrom("he's here")).isEqualTo("he-s-here");
+ assertThat(underTest.generateKeyFrom("foo-bar")).isEqualTo("foo-bar");
+ assertThat(underTest.generateKeyFrom("foo_bar")).isEqualTo("foo_bar");
+ assertThat(underTest.generateKeyFrom("accents éà")).isEqualTo("accents-ea");
+ assertThat(underTest.generateKeyFrom("<foo>")).isEqualTo("foo");
+ assertThat(underTest.generateKeyFrom("<\"foo:\">")).isEqualTo("foo");
+ }
+
+ @Test
+ public void generateKeyFrom_truncate_arg_to_32_chars() {
+ assertThat(underTest.generateKeyFrom(STRING_64_CHARS))
+ .isEqualTo(underTest.generateKeyFrom(STRING_256_CHARS))
+ .isEqualTo(underTest.generateKeyFrom(STRING_32_CHARS));
+ }
+}
import org.sonar.api.config.Settings;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
-import org.sonar.api.utils.internal.AlwaysIncreasingSystem2;
import org.sonar.api.web.UserRole;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.util.UuidFactory;
import org.sonar.db.user.UserMembershipQuery;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.organization.OrganizationCreation;
+import org.sonar.server.organization.OrganizationCreationImpl;
+import org.sonar.server.organization.OrganizationValidation;
+import org.sonar.server.organization.OrganizationValidationImpl;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.WsActionTester;
private Settings settings = new MapSettings()
.setProperty(ORGANIZATIONS_ANYONE_CAN_CREATE, false);
private UuidFactory uuidFactory = mock(UuidFactory.class);
- private CreateAction underTest = new CreateAction(settings, userSession, dbClient, uuidFactory, new OrganizationsWsSupport(), new AlwaysIncreasingSystem2());
+ private OrganizationValidation organizationValidation = new OrganizationValidationImpl();
+ private OrganizationCreation organizationCreation = new OrganizationCreationImpl(dbClient, system2, uuidFactory, organizationValidation);
+ private CreateAction underTest = new CreateAction(settings, userSession, dbClient, new OrganizationsWsSupport(organizationValidation), organizationValidation, organizationCreation);
private WsActionTester wsTester = new WsActionTester(underTest);
@Test
makeUserRoot();
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("description '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");
+ expectedException.expectMessage("Description '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");
executeRequest("foo", "bar", STRING_257_CHARS_LONG, null, null);
}
makeUserRoot();
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("url '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");
+ expectedException.expectMessage("Url '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");
executeRequest("foo", "bar", null, STRING_257_CHARS_LONG, null);
}
makeUserRoot();
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("avatar '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");
+ expectedException.expectMessage("Avatar '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");
executeRequest("foo", "bar", null, null, STRING_257_CHARS_LONG);
}
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.organization.OrganizationValidationImpl;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.MediaTypes;
@Rule
public ExpectedException expectedException = ExpectedException.none();
- private SearchAction underTest = new SearchAction(dbTester.getDbClient(), new OrganizationsWsSupport());
+ private SearchAction underTest = new SearchAction(dbTester.getDbClient(), new OrganizationsWsSupport(new OrganizationValidationImpl()));
private WsActionTester wsTester = new WsActionTester(underTest);
@Test
import org.sonar.db.organization.OrganizationDto;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.organization.OrganizationValidationImpl;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.WsActionTester;
@Rule
public ExpectedException expectedException = ExpectedException.none();
- private UpdateAction underTest = new UpdateAction(userSession, new OrganizationsWsSupport(), dbTester.getDbClient());
+ private UpdateAction underTest = new UpdateAction(userSession, new OrganizationsWsSupport(new OrganizationValidationImpl()), dbTester.getDbClient());
private WsActionTester wsTester = new WsActionTester(underTest);
@Test
userSession.logIn();
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("description '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");
+ expectedException.expectMessage("Description '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");
executeKeyRequest(SOME_KEY, "bar", STRING_257_CHARS_LONG, null, null);
}
userSession.logIn();
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("url '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");
+ expectedException.expectMessage("Url '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");
executeKeyRequest(SOME_KEY, "bar", null, STRING_257_CHARS_LONG, null);
}
userSession.logIn();
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("avatar '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");
+ expectedException.expectMessage("Avatar '" + STRING_257_CHARS_LONG + "' must be at most 256 chars long");
executeKeyRequest(SOME_KEY, "bar", null, null, STRING_257_CHARS_LONG);
}