mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-06-18 20:41:03 +02:00
Add cli commands to modify repository permissions (#2090)
Co-authored-by: Konstantin Schaper <konstantin.schaper@cloudogu.com>
This commit is contained in:
2
gradle/changelog/cli_repository_permission_commands.yaml
Normal file
2
gradle/changelog/cli_repository_permission_commands.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
- type: added
|
||||
description: Cli commands to modify repository permissions ([#2090](https://github.com/scm-manager/scm-manager/pull/2090))
|
||||
@@ -22,7 +22,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
package sonia.scm.group.cli;
|
||||
|
||||
import picocli.CommandLine;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
@@ -41,6 +41,17 @@ public class NamespaceAndName implements Comparable<NamespaceAndName> {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.38.0
|
||||
*/
|
||||
public static NamespaceAndName fromString(String namespaceAndName) {
|
||||
String[] parts = namespaceAndName.split("/");
|
||||
if (parts.length != 2) {
|
||||
throw new IllegalArgumentException("namespace and name must be divided by a slash (/)");
|
||||
}
|
||||
return new NamespaceAndName(parts[0], parts[1]);
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -209,6 +210,28 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per
|
||||
return Collections.unmodifiableCollection(permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the permission for the given user, if present, or an empty {@link Optional} otherwise.
|
||||
*
|
||||
* @since 2.38.0
|
||||
*/
|
||||
public Optional<RepositoryPermission> findUserPermission(String userId) {
|
||||
return findPermission(userId, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the permission for the given group, if present, or an empty {@link Optional} otherwise.
|
||||
*
|
||||
* @since 2.38.0
|
||||
*/
|
||||
public Optional<RepositoryPermission> findGroupPermission(String groupId) {
|
||||
return findPermission(groupId, true);
|
||||
}
|
||||
|
||||
private Optional<RepositoryPermission> findPermission(String x, boolean isGroup) {
|
||||
return getPermissions().stream().filter(p -> p.isGroupPermission() == isGroup && p.getName().equals(x)).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type (hg, git, svn ...) of the {@link Repository}.
|
||||
*
|
||||
|
||||
@@ -41,7 +41,7 @@ import static java.util.Optional.empty;
|
||||
import static java.util.Optional.of;
|
||||
|
||||
@Slf4j
|
||||
public class CliExecutionExceptionHandler implements CommandLine.IExecutionExceptionHandler {
|
||||
class CliExecutionExceptionHandler implements CommandLine.IExecutionExceptionHandler {
|
||||
|
||||
private final I18nCollector i18nCollector;
|
||||
private final String languageCode;
|
||||
|
||||
@@ -32,7 +32,7 @@ import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CliParameterExceptionHandler implements IParameterExceptionHandler {
|
||||
class CliParameterExceptionHandler implements IParameterExceptionHandler {
|
||||
|
||||
private final String languageCode;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import java.util.ListResourceBundle;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CombinedResourceBundle extends ListResourceBundle {
|
||||
class CombinedResourceBundle extends ListResourceBundle {
|
||||
|
||||
private final Object[][] contents;
|
||||
|
||||
|
||||
@@ -28,12 +28,13 @@ import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Injector;
|
||||
import picocli.CommandLine;
|
||||
|
||||
public class CommandFactory implements CommandLine.IFactory {
|
||||
class CommandFactory implements CommandLine.IFactory {
|
||||
|
||||
private final Injector injector;
|
||||
|
||||
public CommandFactory(Injector injector, CliContext context) {
|
||||
this.injector = injector.createChildInjector(new CliContextModule(context));
|
||||
PermissionDescriptionResolverFactory permissionDescriptionResolverFactory = injector.getInstance(PermissionDescriptionResolverFactory.class);
|
||||
this.injector = injector.createChildInjector(new CliContextModule(permissionDescriptionResolverFactory, context));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,14 +45,17 @@ public class CommandFactory implements CommandLine.IFactory {
|
||||
static class CliContextModule extends AbstractModule {
|
||||
|
||||
private final CliContext context;
|
||||
private final PermissionDescriptionResolver permissionDescriptionResolver;
|
||||
|
||||
private CliContextModule(CliContext context) {
|
||||
private CliContextModule(PermissionDescriptionResolverFactory permissionDescriptionResolverFactory, CliContext context) {
|
||||
this.context = context;
|
||||
permissionDescriptionResolver = permissionDescriptionResolverFactory.createResolver(context.getLocale());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(CliContext.class).toInstance(context);
|
||||
bind(PermissionDescriptionResolver.class).toInstance(permissionDescriptionResolver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Singleton
|
||||
public class CommandRegistry {
|
||||
class CommandRegistry {
|
||||
|
||||
private final RegisteredCommandCollector commandCollector;
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ package sonia.scm.cli;
|
||||
|
||||
import picocli.CommandLine;
|
||||
|
||||
public class HelpMixin {
|
||||
class HelpMixin {
|
||||
|
||||
@CommandLine.Option(names = {"--help", "-h"}, usageHelp = true, descriptionKey = "scm.help.usage.description.0")
|
||||
private boolean usageHelp;
|
||||
|
||||
@@ -27,4 +27,4 @@ package sonia.scm.cli;
|
||||
import picocli.CommandLine;
|
||||
|
||||
@CommandLine.Command(name = "logout")
|
||||
public class LogoutCommand {}
|
||||
class LogoutCommand {}
|
||||
|
||||
@@ -28,7 +28,7 @@ package sonia.scm.cli;
|
||||
* Exception is thrown if a command is registered with parent which does not exist.
|
||||
* @since 2.33.0
|
||||
*/
|
||||
public class NonExistingParentCommandException extends CliException {
|
||||
class NonExistingParentCommandException extends CliException {
|
||||
public NonExistingParentCommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.cli;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import sonia.scm.i18n.I18nCollector;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.Optional.empty;
|
||||
import static java.util.Optional.of;
|
||||
|
||||
public class PermissionDescriptionResolver {
|
||||
private final I18nCollector i18nCollector;
|
||||
private final Locale locale;
|
||||
|
||||
PermissionDescriptionResolver(I18nCollector i18nCollector, Locale locale) {
|
||||
this.i18nCollector = i18nCollector;
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
public Optional<String> getDescription(String verb) {
|
||||
try {
|
||||
i18nCollector.findJson(locale.getLanguage());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("failed to load i18n package", e);
|
||||
}
|
||||
return getVerbDescriptionFromI18nBundle(verb);
|
||||
}
|
||||
|
||||
private Optional<String> getVerbDescriptionFromI18nBundle(String verb) {
|
||||
Optional<JsonNode> jsonNode;
|
||||
try {
|
||||
jsonNode = i18nCollector.findJson(locale.getLanguage());
|
||||
} catch (IOException e) {
|
||||
return empty();
|
||||
}
|
||||
if (jsonNode.isEmpty()) {
|
||||
return empty();
|
||||
}
|
||||
JsonNode verbsNode = jsonNode.get().get("verbs");
|
||||
if (verbsNode == null) {
|
||||
return empty();
|
||||
}
|
||||
JsonNode repositoryNode = verbsNode.get("repository");
|
||||
if (repositoryNode == null) {
|
||||
return empty();
|
||||
}
|
||||
JsonNode permissionNode = repositoryNode.get(verb);
|
||||
if (permissionNode == null) {
|
||||
return empty();
|
||||
}
|
||||
JsonNode displayNameNode = permissionNode.get("displayName");
|
||||
if (displayNameNode == null) {
|
||||
return empty();
|
||||
}
|
||||
|
||||
return of(displayNameNode.asText());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.cli;
|
||||
|
||||
import sonia.scm.i18n.I18nCollector;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Locale;
|
||||
|
||||
class PermissionDescriptionResolverFactory {
|
||||
|
||||
private final I18nCollector i18nCollector;
|
||||
|
||||
@Inject
|
||||
PermissionDescriptionResolverFactory(I18nCollector i18nCollector) {
|
||||
this.i18nCollector = i18nCollector;
|
||||
}
|
||||
|
||||
PermissionDescriptionResolver createResolver(Locale locale) {
|
||||
return new PermissionDescriptionResolver(i18nCollector, locale);
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ import picocli.CommandLine;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@CommandLine.Command(name = "ping", hidden = true)
|
||||
public class PingCommand implements Runnable {
|
||||
class PingCommand implements Runnable {
|
||||
|
||||
private final CliContext context;
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class RegisteredCommandCollector {
|
||||
class RegisteredCommandCollector {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RegisteredCommandCollector.class);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
public class RegisteredCommandNode {
|
||||
class RegisteredCommandNode {
|
||||
private final String name;
|
||||
private final Class<?> command;
|
||||
private final List<RegisteredCommandNode> children = new ArrayList<>();
|
||||
|
||||
@@ -27,4 +27,4 @@ package sonia.scm.cli;
|
||||
import picocli.CommandLine;
|
||||
|
||||
@CommandLine.Command(name = "scm")
|
||||
public class ScmManagerCommand {}
|
||||
class ScmManagerCommand {}
|
||||
|
||||
@@ -32,7 +32,7 @@ import sonia.scm.SCMContextProvider;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@CommandLine.Command(name = "version")
|
||||
public class VersionCommand implements Runnable{
|
||||
class VersionCommand implements Runnable{
|
||||
|
||||
private static final String TEMPLATE = String.join("\n","Client Version: {{client.version}}", "Server Version: {{server.version}}");
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ import picocli.CommandLine;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.group.Group;
|
||||
import sonia.scm.group.GroupManager;
|
||||
import sonia.scm.repository.cli.GroupCommand;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -29,7 +29,6 @@ import picocli.CommandLine;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.group.Group;
|
||||
import sonia.scm.group.GroupManager;
|
||||
import sonia.scm.repository.cli.GroupCommand;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ import picocli.CommandLine;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.group.Group;
|
||||
import sonia.scm.group.GroupManager;
|
||||
import sonia.scm.repository.cli.GroupCommand;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collections;
|
||||
|
||||
@@ -29,7 +29,6 @@ import picocli.CommandLine;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.group.Group;
|
||||
import sonia.scm.group.GroupManager;
|
||||
import sonia.scm.repository.cli.GroupCommand;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.cli.Table;
|
||||
import sonia.scm.cli.TemplateRenderer;
|
||||
import sonia.scm.group.GroupManager;
|
||||
import sonia.scm.repository.cli.GroupCommand;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -29,7 +29,6 @@ import picocli.CommandLine;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.group.Group;
|
||||
import sonia.scm.group.GroupManager;
|
||||
import sonia.scm.repository.cli.GroupCommand;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ import picocli.CommandLine;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.group.Group;
|
||||
import sonia.scm.group.GroupManager;
|
||||
import sonia.scm.repository.cli.GroupCommand;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import picocli.CommandLine;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryPermission;
|
||||
import sonia.scm.repository.RepositoryRoleManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
class RepositoryPermissionBaseCommand {
|
||||
|
||||
private final RepositoryManager repositoryManager;
|
||||
private final RepositoryRoleManager roleManager;
|
||||
@CommandLine.Mixin
|
||||
private final RepositoryTemplateRenderer templateRenderer;
|
||||
|
||||
@Inject
|
||||
RepositoryPermissionBaseCommand(RepositoryManager repositoryManager, RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer) {
|
||||
this.repositoryManager = repositoryManager;
|
||||
this.roleManager = roleManager;
|
||||
this.templateRenderer = templateRenderer;
|
||||
}
|
||||
|
||||
void modifyRepository(String repositoryName, Predicate<Repository> modifier) {
|
||||
NamespaceAndName namespaceAndName;
|
||||
try {
|
||||
namespaceAndName = NamespaceAndName.fromString(repositoryName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
templateRenderer.renderInvalidInputError();
|
||||
return;
|
||||
}
|
||||
|
||||
Repository repository = repositoryManager.get(namespaceAndName);
|
||||
if (repository != null) {
|
||||
if (modifier.test(repository)) {
|
||||
repositoryManager.modify(repository);
|
||||
}
|
||||
} else {
|
||||
templateRenderer.renderNotFoundError();
|
||||
}
|
||||
}
|
||||
|
||||
void replacePermission(Repository repository, RepositoryPermission permission) {
|
||||
this.removeExistingPermission(repository, permission.getName(), permission.isGroupPermission());
|
||||
repository.addPermission(permission);
|
||||
}
|
||||
|
||||
void removeExistingPermission(Repository repository, String name, boolean forGroup) {
|
||||
if (!forGroup) {
|
||||
repository.findUserPermission(name).ifPresent(repository::removePermission);
|
||||
} else {
|
||||
repository.findGroupPermission(name).ifPresent(repository::removePermission);
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<String> getPermissionsAsModifiableSet(Repository repository, String name, boolean forGroup) {
|
||||
return this.getExistingPermissions(repository, name, forGroup)
|
||||
.map(this::getVerbs)
|
||||
.map(HashSet::new)
|
||||
.orElseGet(HashSet::new);
|
||||
}
|
||||
|
||||
private Optional<RepositoryPermission> getExistingPermissions(Repository repo, String name, boolean forGroup) {
|
||||
if (!forGroup) {
|
||||
return repo.findUserPermission(name);
|
||||
} else {
|
||||
return repo.findGroupPermission(name);
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<String> getVerbs(RepositoryPermission permission) {
|
||||
if (permission.getRole() == null) {
|
||||
return permission.getVerbs();
|
||||
} else {
|
||||
return roleManager.get(permission.getRole()).getVerbs();
|
||||
}
|
||||
}
|
||||
|
||||
void renderRoleNotFoundError() {
|
||||
templateRenderer.renderRoleNotFoundError();
|
||||
}
|
||||
|
||||
void renderVerbNotFoundError() {
|
||||
templateRenderer.renderVerbNotFoundError();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Value
|
||||
class RepositoryPermissionBean {
|
||||
|
||||
boolean groupPermission;
|
||||
String name;
|
||||
String role;
|
||||
Collection<String> verbs;
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import picocli.CommandLine;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.cli.PermissionDescriptionResolver;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryPermission;
|
||||
import sonia.scm.repository.RepositoryRoleManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
@CommandLine.Command(name = "add-permissions")
|
||||
@ParentCommand(value = RepositoryCommand.class)
|
||||
class RepositoryPermissionsAddCommand extends RepositoryPermissionBaseCommand implements Runnable {
|
||||
|
||||
private final PermissionDescriptionResolver permissionDescriptionResolver;
|
||||
|
||||
@CommandLine.Parameters(paramLabel = "namespace/name", index = "0", descriptionKey = "scm.repo.add-permissions.repository")
|
||||
private String repositoryName;
|
||||
@CommandLine.Parameters(paramLabel = "name", index = "1", descriptionKey = "scm.repo.add-permissions.name")
|
||||
private String name;
|
||||
@CommandLine.Parameters(paramLabel = "verbs", index = "2..", arity = "1..", descriptionKey = "scm.repo.add-permissions.verbs")
|
||||
private String[] verbs = new String[0];
|
||||
|
||||
@CommandLine.Option(names = {"--group", "-g"}, descriptionKey = "scm.repo.add-permissions.forGroup")
|
||||
private boolean forGroup;
|
||||
|
||||
@Inject
|
||||
RepositoryPermissionsAddCommand(RepositoryManager repositoryManager, RepositoryPermissionBaseCommand permissionCommandManager, RepositoryRoleManager roleManager, PermissionDescriptionResolver permissionDescriptionResolver, RepositoryTemplateRenderer templateRenderer) {
|
||||
super(repositoryManager, roleManager, templateRenderer);
|
||||
this.permissionDescriptionResolver = permissionDescriptionResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
modifyRepository(
|
||||
repositoryName,
|
||||
repository -> {
|
||||
if (!Arrays.stream(verbs).allMatch(this::verifyVerbExists)) {
|
||||
return false;
|
||||
}
|
||||
Set<String> resultingVerbs =
|
||||
getPermissionsAsModifiableSet(repository, name, forGroup);
|
||||
if (resultingVerbs.containsAll(asList(this.verbs))) {
|
||||
return false;
|
||||
}
|
||||
resultingVerbs.addAll(asList(this.verbs));
|
||||
replacePermission(repository, new RepositoryPermission(name, resultingVerbs, forGroup));
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private boolean verifyVerbExists(String verb) {
|
||||
if (permissionDescriptionResolver.getDescription(verb).isEmpty()) {
|
||||
renderVerbNotFoundError();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setRepositoryName(String repositoryName) {
|
||||
this.repositoryName = repositoryName;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setVerbs(String... verbs) {
|
||||
this.verbs = verbs;
|
||||
}
|
||||
|
||||
public void setForGroup(boolean forGroup) {
|
||||
this.forGroup = forGroup;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import picocli.CommandLine;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.cli.PermissionDescriptionResolver;
|
||||
import sonia.scm.repository.RepositoryRole;
|
||||
import sonia.scm.repository.RepositoryRoleManager;
|
||||
import sonia.scm.security.RepositoryPermissionProvider;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
@CommandLine.Command(name = "available-permissions")
|
||||
@ParentCommand(value = RepositoryCommand.class)
|
||||
class RepositoryPermissionsAvailableCommand implements Runnable {
|
||||
|
||||
@CommandLine.Option(names = {"--roles", "-r"}, descriptionKey = "scm.repo.available-permissions.roles-only")
|
||||
private boolean roles;
|
||||
@CommandLine.Option(names = {"--verbs", "-v"}, descriptionKey = "scm.repo.available-permissions.verbs-only")
|
||||
private boolean verbs;
|
||||
|
||||
@CommandLine.Mixin
|
||||
private final RepositoryTemplateRenderer templateRenderer;
|
||||
private final RepositoryPermissionProvider repositoryPermissionProvider;
|
||||
private final PermissionDescriptionResolver permissionDescriptionResolver;
|
||||
private final RepositoryRoleManager repositoryRoleManager;
|
||||
|
||||
@Inject
|
||||
public RepositoryPermissionsAvailableCommand(RepositoryTemplateRenderer templateRenderer, RepositoryPermissionProvider repositoryPermissionProvider, PermissionDescriptionResolver permissionDescriptionResolver, RepositoryRoleManager repositoryRoleManager) {
|
||||
this.templateRenderer = templateRenderer;
|
||||
this.repositoryPermissionProvider = repositoryPermissionProvider;
|
||||
this.permissionDescriptionResolver = permissionDescriptionResolver;
|
||||
this.repositoryRoleManager = repositoryRoleManager;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setRoles(boolean roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setVerbs(boolean verbs) {
|
||||
this.verbs = verbs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (roles) {
|
||||
templateRenderer.renderRoles(getRoleBeans());
|
||||
} else if (verbs) {
|
||||
templateRenderer.renderVerbs(getVerbBeans());
|
||||
} else {
|
||||
templateRenderer.render(getRoleBeans(), getVerbBeans());
|
||||
}
|
||||
}
|
||||
|
||||
private List<VerbBean> getVerbBeans() {
|
||||
return repositoryPermissionProvider.availableVerbs().stream().map(this::createBean).collect(toList());
|
||||
}
|
||||
|
||||
private List<RoleBean> getRoleBeans() {
|
||||
return repositoryRoleManager.getAll().stream().map(this::createBean).collect(toList());
|
||||
}
|
||||
|
||||
private VerbBean createBean(String verb) {
|
||||
return new VerbBean(verb, permissionDescriptionResolver.getDescription(verb).orElse(verb));
|
||||
}
|
||||
|
||||
private RoleBean createBean(RepositoryRole role) {
|
||||
return new RoleBean(role.getName(), role.getVerbs());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import picocli.CommandLine;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryRoleManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@CommandLine.Command(name = "clear-permissions")
|
||||
@ParentCommand(value = RepositoryCommand.class)
|
||||
class RepositoryPermissionsClearCommand extends RepositoryPermissionBaseCommand implements Runnable {
|
||||
|
||||
@CommandLine.Parameters(paramLabel = "namespace/name", index = "0", descriptionKey = "scm.repo.clear-permissions.repository")
|
||||
private String repositoryName;
|
||||
@CommandLine.Parameters(paramLabel = "name", index = "1", descriptionKey = "scm.repo.clear-permissions.name")
|
||||
private String name;
|
||||
|
||||
@CommandLine.Option(names = {"--group", "-g"}, descriptionKey = "scm.repo.clear-permissions.forGroup")
|
||||
private boolean forGroup;
|
||||
|
||||
@Inject
|
||||
public RepositoryPermissionsClearCommand(RepositoryManager repositoryManager, RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer) {
|
||||
super(repositoryManager, roleManager, templateRenderer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
modifyRepository(
|
||||
repositoryName,
|
||||
repo -> {
|
||||
removeExistingPermission(repo, name, forGroup);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setRepositoryName(String repositoryName) {
|
||||
this.repositoryName = repositoryName;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setForGroup(boolean forGroup) {
|
||||
this.forGroup = forGroup;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import picocli.CommandLine;
|
||||
import sonia.scm.cli.CommandValidator;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.cli.PermissionDescriptionResolver;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryPermission;
|
||||
import sonia.scm.repository.RepositoryRoleManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@CommandLine.Command(name = "list-permissions")
|
||||
@ParentCommand(value = RepositoryCommand.class)
|
||||
class RepositoryPermissionsListCommand implements Runnable {
|
||||
|
||||
@CommandLine.Mixin
|
||||
private final RepositoryTemplateRenderer templateRenderer;
|
||||
@CommandLine.Mixin
|
||||
private final CommandValidator validator;
|
||||
private final RepositoryManager manager;
|
||||
private final RepositoryRoleManager roleManager;
|
||||
private final PermissionDescriptionResolver permissionDescriptionResolver;
|
||||
|
||||
@CommandLine.Parameters(paramLabel = "namespace/name", index = "0", descriptionKey = "scm.repo.list-permissions.repository")
|
||||
private String repository;
|
||||
@CommandLine.Option(names = {"--verbose", "-v"}, descriptionKey = "scm.repo.list-permissions.verbose")
|
||||
private boolean verbose;
|
||||
@CommandLine.Option(names = {"--keys", "-k"}, descriptionKey = "scm.repo.list-permissions.keys")
|
||||
private boolean keys;
|
||||
|
||||
@Inject
|
||||
public RepositoryPermissionsListCommand(RepositoryTemplateRenderer templateRenderer, CommandValidator validator, RepositoryManager manager, RepositoryRoleManager roleManager, PermissionDescriptionResolver permissionDescriptionResolver) {
|
||||
this.templateRenderer = templateRenderer;
|
||||
this.validator = validator;
|
||||
this.manager = manager;
|
||||
this.roleManager = roleManager;
|
||||
this.permissionDescriptionResolver = permissionDescriptionResolver;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setRepository(String repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setVerbose(boolean verbose) {
|
||||
this.verbose = verbose;
|
||||
}
|
||||
|
||||
public void setKeys(boolean keys) {
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
validator.validate();
|
||||
String[] splitRepo = repository.split("/");
|
||||
if (splitRepo.length == 2) {
|
||||
Repository repo = manager.get(new NamespaceAndName(splitRepo[0], splitRepo[1]));
|
||||
|
||||
if (repo != null) {
|
||||
Collection<RepositoryPermissionBean> permissions =
|
||||
repo.getPermissions().stream().map(this::createPermissionBean).collect(Collectors.toList());
|
||||
if (verbose) {
|
||||
templateRenderer.renderVerbose(permissions);
|
||||
} else {
|
||||
templateRenderer.render(permissions);
|
||||
}
|
||||
} else {
|
||||
templateRenderer.renderNotFoundError();
|
||||
}
|
||||
} else {
|
||||
templateRenderer.renderInvalidInputError();
|
||||
}
|
||||
}
|
||||
|
||||
private RepositoryPermissionBean createPermissionBean(RepositoryPermission permission) {
|
||||
Collection<String> effectiveVerbs;
|
||||
if (permission.getRole() == null) {
|
||||
effectiveVerbs = permission.getVerbs();
|
||||
} else {
|
||||
effectiveVerbs = roleManager.get(permission.getRole()).getVerbs();
|
||||
}
|
||||
return new RepositoryPermissionBean(
|
||||
permission.isGroupPermission(),
|
||||
permission.getName(),
|
||||
permission.getRole() == null? "CUSTOM": permission.getRole(),
|
||||
keys? effectiveVerbs: getDescriptions(effectiveVerbs)
|
||||
);
|
||||
}
|
||||
|
||||
private Collection<String> getDescriptions(Collection<String> effectiveVerbs) {
|
||||
return effectiveVerbs.stream().map(this::getDescription).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String getDescription(String verb) {
|
||||
return permissionDescriptionResolver.getDescription(verb).orElse(verb);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import picocli.CommandLine;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryPermission;
|
||||
import sonia.scm.repository.RepositoryRoleManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
@CommandLine.Command(name = "remove-permissions")
|
||||
@ParentCommand(value = RepositoryCommand.class)
|
||||
class RepositoryPermissionsRemoveCommand extends RepositoryPermissionBaseCommand implements Runnable {
|
||||
|
||||
@CommandLine.Parameters(paramLabel = "namespace/name", index = "0", descriptionKey = "scm.repo.remove-permissions.repository")
|
||||
private String repositoryName;
|
||||
@CommandLine.Parameters(paramLabel = "name", index = "1", descriptionKey = "scm.repo.remove-permissions.name")
|
||||
private String name;
|
||||
@CommandLine.Parameters(paramLabel = "verbs", index = "2..", arity = "1..", descriptionKey = "scm.repo.remove-permissions.verbs")
|
||||
private String[] verbs = new String[0];
|
||||
|
||||
@CommandLine.Option(names = {"--group", "-g"}, descriptionKey = "scm.repo.remove-permissions.forGroup")
|
||||
private boolean forGroup;
|
||||
|
||||
@Inject
|
||||
public RepositoryPermissionsRemoveCommand(RepositoryManager repositoryManager, RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer) {
|
||||
super(repositoryManager, roleManager, templateRenderer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
modifyRepository(
|
||||
repositoryName,
|
||||
repository -> {
|
||||
Set<String> resultingVerbs =
|
||||
getPermissionsAsModifiableSet(repository, name, forGroup);
|
||||
if (resultingVerbs.stream().noneMatch(verb -> asList(verbs).contains(verb))) {
|
||||
return false;
|
||||
}
|
||||
resultingVerbs.removeAll(asList(this.verbs));
|
||||
replacePermission(repository, new RepositoryPermission(name, resultingVerbs, forGroup));
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setRepositoryName(String repositoryName) {
|
||||
this.repositoryName = repositoryName;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setVerbs(String... verbs) {
|
||||
this.verbs = verbs;
|
||||
}
|
||||
|
||||
public void setForGroup(boolean forGroup) {
|
||||
this.forGroup = forGroup;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import picocli.CommandLine;
|
||||
import sonia.scm.cli.ParentCommand;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryPermission;
|
||||
import sonia.scm.repository.RepositoryRoleManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@CommandLine.Command(name = "set-role")
|
||||
@ParentCommand(value = RepositoryCommand.class)
|
||||
class RepositoryPermissionsSetRoleCommand extends RepositoryPermissionBaseCommand implements Runnable {
|
||||
|
||||
private final RepositoryRoleManager roleManager;
|
||||
|
||||
@CommandLine.Parameters(paramLabel = "namespace/name", index = "0", descriptionKey = "scm.repo.set-role.repository")
|
||||
private String repositoryName;
|
||||
@CommandLine.Parameters(paramLabel = "name", index = "1", descriptionKey = "scm.repo.set-role.name")
|
||||
private String name;
|
||||
@CommandLine.Parameters(paramLabel = "role", index = "2", descriptionKey = "scm.repo.set-role.role")
|
||||
private String role;
|
||||
|
||||
@CommandLine.Option(names = {"--group", "-g"}, descriptionKey = "scm.repo.set-role.forGroup")
|
||||
private boolean forGroup;
|
||||
|
||||
@Inject
|
||||
public RepositoryPermissionsSetRoleCommand(RepositoryManager repositoryManager, RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer) {
|
||||
super(repositoryManager, roleManager, templateRenderer);
|
||||
this.roleManager = roleManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
modifyRepository(
|
||||
repositoryName,
|
||||
repository -> {
|
||||
if (roleManager.get(role) == null) {
|
||||
renderRoleNotFoundError();
|
||||
return false;
|
||||
}
|
||||
replacePermission(repository, new RepositoryPermission(name, role, forGroup));
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setRepositoryName(String repositoryName) {
|
||||
this.repositoryName = repositoryName;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public void setForGroup(boolean forGroup) {
|
||||
this.forGroup = forGroup;
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import sonia.scm.cli.CliContext;
|
||||
import sonia.scm.cli.ExitCode;
|
||||
import sonia.scm.cli.Table;
|
||||
@@ -33,7 +32,11 @@ import sonia.scm.repository.Repository;
|
||||
import sonia.scm.template.TemplateEngineFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collections;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Map.entry;
|
||||
|
||||
class RepositoryTemplateRenderer extends TemplateRenderer {
|
||||
|
||||
@@ -42,9 +45,19 @@ class RepositoryTemplateRenderer extends TemplateRenderer {
|
||||
"{{#cols}}{{value}}{{/cols}}",
|
||||
"{{/rows}}"
|
||||
);
|
||||
private static final String TABLE_TEMPLATE = String.join("\n",
|
||||
"{{#rows}}",
|
||||
"{{#cols}}{{#row.first}}{{#upper}}{{value}}{{/upper}}{{/row.first}}{{^row.first}}{{value}}{{/row.first}}{{^last}} {{/last}}{{/cols}}",
|
||||
"{{/rows}}"
|
||||
);
|
||||
private static final String INVALID_INPUT_TEMPLATE = "{{i18n.repoInvalidInput}}";
|
||||
private static final String NOT_FOUND_TEMPLATE = "{{i18n.repoNotFound}}";
|
||||
|
||||
private static final String TYPE_HEADER_KEY = "scm.repo.permissions.type";
|
||||
private static final String NAME_HEADER_KEY = "scm.repo.permissions.name";
|
||||
private static final String ROLE_HEADER_KEY = "scm.repo.permissions.role";
|
||||
private static final String VERBS_HEADER_KEY = "scm.repo.permissions.verbs";
|
||||
|
||||
private final CliContext context;
|
||||
private final RepositoryToRepositoryCommandDtoMapper mapper;
|
||||
|
||||
@@ -66,17 +79,60 @@ class RepositoryTemplateRenderer extends TemplateRenderer {
|
||||
table.addLabelValueRow("repoLastModified", bean.getLastModified());
|
||||
table.addLabelValueRow("repoUrl", bean.getUrl());
|
||||
table.addLabelValueRow("repoDescription", bean.getDescription());
|
||||
renderToStdout(DETAILS_TABLE_TEMPLATE, ImmutableMap.of("rows", table, "repo", bean));
|
||||
renderToStdout(DETAILS_TABLE_TEMPLATE, Map.of("rows", table, "repo", bean));
|
||||
}
|
||||
|
||||
public void render(Collection<RepositoryPermissionBean> permissions) {
|
||||
Table table = createTable();
|
||||
table.addHeader(TYPE_HEADER_KEY, NAME_HEADER_KEY, ROLE_HEADER_KEY);
|
||||
permissions.forEach(permission -> addPermissionToTable(table, permission));
|
||||
renderToStdout(TABLE_TEMPLATE, Map.ofEntries(entry("rows", table), entry("permissions", permissions)));
|
||||
}
|
||||
|
||||
public void renderVerbose(Collection<RepositoryPermissionBean> permissions) {
|
||||
Table table = createTable();
|
||||
table.addHeader(TYPE_HEADER_KEY, NAME_HEADER_KEY, ROLE_HEADER_KEY, VERBS_HEADER_KEY);
|
||||
permissions.forEach(permission -> addVerbosePermissionToTable(table, permission));
|
||||
renderToStdout(TABLE_TEMPLATE, Map.ofEntries(entry("rows", table), entry("permissions", permissions)));
|
||||
}
|
||||
|
||||
private void addPermissionToTable(Table table, RepositoryPermissionBean permission) {
|
||||
table.addRow(
|
||||
getBundle().getString(permission.isGroupPermission()? "scm.repo.permissions.isGroup": "scm.repo.permissions.isUser"),
|
||||
permission.getName(),
|
||||
permission.getRole()
|
||||
);
|
||||
}
|
||||
|
||||
private void addVerbosePermissionToTable(Table table, RepositoryPermissionBean permission) {
|
||||
table.addRow(
|
||||
getBundle().getString(permission.isGroupPermission()? "scm.repo.permissions.isGroup": "scm.repo.permissions.isUser"),
|
||||
permission.getName(),
|
||||
permission.getRole(),
|
||||
String.join(", ", permission.getVerbs())
|
||||
);
|
||||
}
|
||||
|
||||
public void renderInvalidInputError() {
|
||||
renderToStderr(INVALID_INPUT_TEMPLATE, Collections.emptyMap());
|
||||
renderToStderr(INVALID_INPUT_TEMPLATE, emptyMap());
|
||||
context.getStderr().println();
|
||||
context.exit(ExitCode.USAGE);
|
||||
}
|
||||
|
||||
public void renderNotFoundError() {
|
||||
renderToStderr(NOT_FOUND_TEMPLATE, Collections.emptyMap());
|
||||
renderToStderr(NOT_FOUND_TEMPLATE, emptyMap());
|
||||
context.getStderr().println();
|
||||
context.exit(ExitCode.NOT_FOUND);
|
||||
}
|
||||
|
||||
void renderRoleNotFoundError() {
|
||||
renderToStderr("{{i18n.roleNotFound}}", emptyMap());
|
||||
context.getStderr().println();
|
||||
context.exit(ExitCode.NOT_FOUND);
|
||||
}
|
||||
|
||||
void renderVerbNotFoundError() {
|
||||
renderToStderr("{{i18n.verbNotFound}}", emptyMap());
|
||||
context.getStderr().println();
|
||||
context.exit(ExitCode.NOT_FOUND);
|
||||
}
|
||||
@@ -85,4 +141,32 @@ class RepositoryTemplateRenderer extends TemplateRenderer {
|
||||
renderDefaultError(exception);
|
||||
context.exit(ExitCode.SERVER_ERROR);
|
||||
}
|
||||
|
||||
public void renderVerbs(Collection<VerbBean> verbs) {
|
||||
Table table = createTable();
|
||||
table.addHeader("scm.repo.permissions.verb", "scm.repo.permissions.description");
|
||||
verbs.forEach(verb -> addVerbToTable(table, verb));
|
||||
renderToStdout(TABLE_TEMPLATE, Map.ofEntries(entry("rows", table), entry("verbs", verbs)));
|
||||
}
|
||||
|
||||
private void addVerbToTable(Table table, VerbBean verb) {
|
||||
table.addRow(verb.getVerb(), verb.getDescription());
|
||||
}
|
||||
|
||||
public void renderRoles(Collection<RoleBean> roles) {
|
||||
Table table = createTable();
|
||||
table.addHeader(ROLE_HEADER_KEY, VERBS_HEADER_KEY);
|
||||
roles.forEach(role -> addRoleToTable(table, role));
|
||||
renderToStdout(TABLE_TEMPLATE, Map.ofEntries(entry("rows", table), entry("roles", roles)));
|
||||
}
|
||||
|
||||
private void addRoleToTable(Table table, RoleBean role) {
|
||||
table.addRow(role.getName(), String.join(", ", role.getVerbs()));
|
||||
}
|
||||
|
||||
public void render(Collection<RoleBean> roles, Collection<VerbBean> verbs) {
|
||||
renderRoles(roles);
|
||||
renderToStdout("\n", emptyMap());
|
||||
renderVerbs(verbs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.util.Optional;
|
||||
|
||||
@Mapper
|
||||
public abstract class RepositoryToRepositoryCommandDtoMapper {
|
||||
abstract class RepositoryToRepositoryCommandDtoMapper {
|
||||
|
||||
@Inject
|
||||
private RepositoryServiceFactory serviceFactory;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Value
|
||||
class RoleBean {
|
||||
|
||||
String name;
|
||||
Collection<String> verbs;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
class VerbBean {
|
||||
String verb;
|
||||
String description;
|
||||
}
|
||||
@@ -74,7 +74,7 @@ public class I18nServlet extends HttpServlet {
|
||||
if (json.isPresent()) {
|
||||
write(response, json.get());
|
||||
} else {
|
||||
LOG.debug("could not find translation for lanugage {}", languageCode);
|
||||
LOG.debug("could not find translation for language {}", languageCode);
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
|
||||
@@ -136,15 +136,15 @@
|
||||
"verbs": {
|
||||
"repository": {
|
||||
"read": {
|
||||
"displayName": "Repository Lesen",
|
||||
"displayName": "Repository lesen",
|
||||
"description": "Darf das Repository im SCM-Manager sehen."
|
||||
},
|
||||
"modify": {
|
||||
"displayName": "Repository Modifizieren",
|
||||
"displayName": "Repository modifizieren",
|
||||
"description": "Darf die Eigenschaften des Repository verändern."
|
||||
},
|
||||
"delete": {
|
||||
"displayName": "Repository Löschen",
|
||||
"displayName": "Repository löschen",
|
||||
"description": "Darf das Repository löschen."
|
||||
},
|
||||
"rename": {
|
||||
|
||||
@@ -91,6 +91,52 @@ repoDeletePrompt= If you really want to delete this repository please pass --yes
|
||||
scm.repo.modify.usage.description.0 = Modify repository on server
|
||||
scm.repo.modify.repository = Repository namespace/name
|
||||
|
||||
### List repository permissions
|
||||
scm.repo.list-permissions.usage.description.0 = List permissions for repository
|
||||
scm.repo.list-permissions.repository = Repository namespace/name
|
||||
scm.repo.list-permissions.verbose = Show single detailed permissions
|
||||
scm.repo.list-permissions.keys = Use keys instead of descriptive permission
|
||||
|
||||
scm.repo.available-permissions.usage.description.0 = List available permissions for repository
|
||||
scm.repo.available-permissions.roles-only = List roles only
|
||||
scm.repo.available-permissions.verbs-only = List verbs only
|
||||
|
||||
scm.repo.set-role.usage.description.0 = Set role permission for user or group
|
||||
scm.repo.set-role.repository = Repository namespace/name
|
||||
scm.repo.set-role.name = User or group name to set role for
|
||||
scm.repo.set-role.role = Permission role to grant to user or group
|
||||
scm.repo.set-role.forGroup = Set role for a group, not for a user
|
||||
|
||||
scm.repo.add-permissions.usage.description.0 = Add single permission for user or group
|
||||
scm.repo.add-permissions.repository = Repository namespace/name
|
||||
scm.repo.add-permissions.name = User or group name to grant permission to
|
||||
scm.repo.add-permissions.verbs = Single permissions to grant to user or group
|
||||
scm.repo.add-permissions.forGroup = Add permission for a group, not for a user
|
||||
|
||||
scm.repo.remove-permissions.usage.description.0 = Revoke single permission from user or group
|
||||
scm.repo.remove-permissions.repository = Repository namespace/name
|
||||
scm.repo.remove-permissions.name = User or group name to revoke permission from
|
||||
scm.repo.remove-permissions.verbs = Single permissions to revoke from user or group
|
||||
scm.repo.remove-permissions.forGroup = Revoke permission from a group, not for a user
|
||||
|
||||
scm.repo.clear-permission.usage.description.0 = Revoke all permissions from user or group
|
||||
scm.repo.clear-permission.repository = Repository namespace/name
|
||||
scm.repo.clear-permission.name = User or group name to revoke permissions from
|
||||
scm.repo.clear-permission.forGroup = Revoke permissions from a group, not for a user
|
||||
|
||||
scm.repo.permissions.type = Type
|
||||
scm.repo.permissions.isUser = user
|
||||
scm.repo.permissions.isGroup = group
|
||||
scm.repo.permissions.name = Name
|
||||
scm.repo.permissions.role = Role
|
||||
scm.repo.permissions.verbs = Verbs
|
||||
scm.repo.permissions.custom = custom
|
||||
scm.repo.permissions.verb = Verb
|
||||
scm.repo.permissions.description = Description
|
||||
|
||||
roleNotFound= Could not find permission role
|
||||
verbNotFound= Could not find single permission
|
||||
|
||||
## User
|
||||
scm.user.username = Username
|
||||
scm.user.displayName = Display Name
|
||||
|
||||
@@ -101,6 +101,52 @@ repoDeletePrompt= Wenn dieses Repository endg
|
||||
scm.repo.modify.usage.description.0 = Aktualisiert ein Repository auf dem Server
|
||||
scm.repo.modify.repository = Repository Namespace/Name
|
||||
|
||||
### List repository permissions
|
||||
scm.repo.list-permissions.usage.description.0 = Listet die Berechtigungen f<>r das Repository
|
||||
scm.repo.list-permissions.repository = Repository Namespace/Name
|
||||
scm.repo.list-permissions.verbose = Zeigt detaillierte einzelne Berechtigungen
|
||||
scm.repo.list-permissions.keys = Nutze Schl<68>ssel anstelle der Beschreibungen
|
||||
|
||||
scm.repo.available-permissions.usage.description.0 = Listet die verf<72>gbaren Berechtigungen
|
||||
scm.repo.available-permissions.roles-only = Zeigt nur die Rollen
|
||||
scm.repo.available-permissions.verbs-only = Zeigt nur die Einzelberechtigungen
|
||||
|
||||
scm.repo.set-role.usage.description.0 = Setze eine Rolle f<>r einen Benutzer oder eine Gruppe
|
||||
scm.repo.set-role.repository = Repository Namespace/Name
|
||||
scm.repo.set-role.name = Name des Benutzers oder der Gruppe f<>r die Berechtigung
|
||||
scm.repo.set-role.role = Rolle, die f<>r den Benutzer oder die Gruppe gesetzt werden soll
|
||||
scm.repo.set-role.forGroup = Setzt die Berechtigung f<>r eine Gruppe, nicht f<>r einen Benutzer
|
||||
|
||||
scm.repo.add-permissions.usage.description.0 = F<EFBFBD>gt eine einzelne Berechtigung f<>r einen Benutzer oder eine Gruppe hinzu
|
||||
scm.repo.add-permissions.repository = Repository Namespace/Name
|
||||
scm.repo.add-permissions.name = Name des Benutzers oder der Gruppe f<>r die Berechtigung
|
||||
scm.repo.add-permissions.verbs = Einzelne Berechtigungen, die f<>r den Benutzer oder die Gruppe hinzugef<65>gt werden sollen
|
||||
scm.repo.add-permissions.forGroup = Setzt die Berechtigung f<>r eine Gruppe, nicht f<>r einen Benutzer
|
||||
|
||||
scm.repo.remove-permissions.usage.description.0 = Entzieht einem Benutzer oder einer Gruppe eine einzelne Berechtigung
|
||||
scm.repo.remove-permissions.repository = Repository Namespace/Name
|
||||
scm.repo.remove-permissions.name = Name des Benutzers oder der Gruppe, dem/der die Berechtigung entzogen werden soll
|
||||
scm.repo.remove-permissions.verbs = Einzelne Berechtigungen, die dem Benutzer oder der Gruppe entzogen werden sollen
|
||||
scm.repo.remove-permissions.forGroup = Entziehe die Berechtigung einer Gruppe, nicht einem Benutzer
|
||||
|
||||
scm.repo.clear-permission.usage.description.0 = Entzieht einem Benutzer oder einer Gruppe alle Berechtigungen
|
||||
scm.repo.clear-permission.repository = Repository Namespace/Name
|
||||
scm.repo.clear-permission.name = Name des Benutzers oder der Gruppe, dem/der die Berechtigung entzogen werden soll
|
||||
scm.repo.clear-permission.forGroup = Entziehe die Berechtigung einer Gruppe, nicht einem Benutzer
|
||||
|
||||
scm.repo.permissions.type = Typ
|
||||
scm.repo.permissions.isUser = Benutzer
|
||||
scm.repo.permissions.isGroup = Gruppe
|
||||
scm.repo.permissions.name = Name
|
||||
scm.repo.permissions.role = Rolle
|
||||
scm.repo.permissions.verbs = Verben
|
||||
scm.repo.permissions.custom = individuell
|
||||
scm.repo.permissions.verb = Verb
|
||||
scm.repo.permissions.description = Beschreibung
|
||||
|
||||
roleNotFound= Berechtigungsrolle konnte nicht gefunden werden
|
||||
verbNotFound= konnte nicht gefunden werden
|
||||
|
||||
## User
|
||||
scm.user.username = Benutzername
|
||||
scm.user.displayName = Anzeigename
|
||||
|
||||
@@ -25,8 +25,10 @@
|
||||
package sonia.scm.cli;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.inject.Binder;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -35,6 +37,7 @@ import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import picocli.CommandLine;
|
||||
import sonia.scm.i18n.I18nCollector;
|
||||
import sonia.scm.plugin.PluginLoader;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -44,6 +47,7 @@ import java.util.Locale;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -64,6 +68,8 @@ class CliProcessorTest {
|
||||
private CliExecutionExceptionHandler executionExceptionHandler;
|
||||
@Mock
|
||||
private CliParameterExceptionHandler parameterExceptionHandler;
|
||||
@Mock
|
||||
private PermissionDescriptionResolverFactory permissionDescriptionResolverFactory;
|
||||
|
||||
@BeforeEach
|
||||
void mockPluginLoader() {
|
||||
@@ -83,7 +89,7 @@ class CliProcessorTest {
|
||||
@Test
|
||||
void shouldExecutePingCommand() {
|
||||
when(registry.createCommandTree()).thenReturn(ImmutableList.of(new RegisteredCommandNode("ping", PingCommand.class)));
|
||||
Injector injector = Guice.createInjector();
|
||||
Injector injector = Guice.createInjector(new MockedModule());
|
||||
CliProcessor cliProcessor = new CliProcessor(registry, injector, exceptionHandlerFactory, pluginLoader);
|
||||
|
||||
cliProcessor.execute(context, "ping");
|
||||
@@ -94,7 +100,7 @@ class CliProcessorTest {
|
||||
@Test
|
||||
void shouldExecutePingCommandWithExitCode0() {
|
||||
when(registry.createCommandTree()).thenReturn(ImmutableList.of(new RegisteredCommandNode("ping", PingCommand.class)));
|
||||
Injector injector = Guice.createInjector();
|
||||
Injector injector = Guice.createInjector(new MockedModule());
|
||||
CliProcessor cliProcessor = new CliProcessor(registry, injector, exceptionHandlerFactory, pluginLoader);
|
||||
|
||||
int exitCode = cliProcessor.execute(context, "ping");
|
||||
@@ -177,7 +183,7 @@ class CliProcessorTest {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
when(context.getStdout()).thenReturn(new PrintWriter(baos));
|
||||
|
||||
Injector injector = Guice.createInjector();
|
||||
Injector injector = Guice.createInjector(new MockedModule());
|
||||
CliProcessor cliProcessor = new CliProcessor(registry, injector, exceptionHandlerFactory, pluginLoader);
|
||||
|
||||
cliProcessor.execute(context, args);
|
||||
@@ -210,4 +216,14 @@ class CliProcessorTest {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static class MockedModule implements Module {
|
||||
|
||||
@Override
|
||||
public void configure(Binder binder) {
|
||||
I18nCollector i18nCollector = mock(I18nCollector.class);
|
||||
binder.bind(PermissionDescriptionResolverFactory.class)
|
||||
.toInstance(new PermissionDescriptionResolverFactory(i18nCollector));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.cli.CliContext;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
|
||||
@@ -33,10 +33,7 @@ import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryTestData;
|
||||
import sonia.scm.repository.cli.RepositoryGetCommand;
|
||||
import sonia.scm.repository.cli.RepositoryTemplateRenderer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
@@ -41,8 +40,6 @@ import java.util.Map;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyMap;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.cli.PermissionDescriptionResolver;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryPermission;
|
||||
import sonia.scm.repository.RepositoryRole;
|
||||
import sonia.scm.repository.RepositoryRoleManager;
|
||||
import sonia.scm.repository.RepositoryTestData;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Optional.empty;
|
||||
import static java.util.Optional.of;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.groups.Tuple.tuple;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RepositoryPermissionsAddCommandTest {
|
||||
|
||||
@Mock
|
||||
private RepositoryManager repositoryManager;
|
||||
@Mock
|
||||
private RepositoryRoleManager roleManager;
|
||||
@Mock
|
||||
private PermissionDescriptionResolver permissionDescriptionResolver;
|
||||
@Mock
|
||||
private RepositoryTemplateRenderer templateRenderer;
|
||||
|
||||
@InjectMocks
|
||||
private RepositoryPermissionsAddCommand command;
|
||||
|
||||
private final Repository repository = RepositoryTestData.createHeartOfGold();
|
||||
|
||||
@Nested
|
||||
class ForExistingRepository {
|
||||
|
||||
@BeforeEach
|
||||
void mockRepository() {
|
||||
when(repositoryManager.get(new NamespaceAndName("hitchhiker", "HeartOfGold")))
|
||||
.thenReturn(repository);
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ForExistingVerbs {
|
||||
|
||||
@BeforeEach
|
||||
void mockVerbs() {
|
||||
when(permissionDescriptionResolver.getDescription(anyString()))
|
||||
.thenAnswer(invocation -> of(invocation.getArgument(0, String.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSetMultipleVerbsForNewUser() {
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("trillian");
|
||||
command.setVerbs("read", "pull", "push");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager).modify(argThat(argument -> {
|
||||
assertThat(argument.getPermissions()).extracting("name", "verbs", "groupPermission")
|
||||
.containsExactly(tuple("trillian", Set.of("read", "pull", "push"), false));
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAddNewVerbToExistingVerbsForUser() {
|
||||
repository.setPermissions(
|
||||
List.of(
|
||||
new RepositoryPermission("trillian", List.of("read"), false)
|
||||
)
|
||||
);
|
||||
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("trillian");
|
||||
command.setVerbs("write");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager).modify(argThat(argument -> {
|
||||
assertThat(argument.getPermissions()).extracting("name", "verbs", "groupPermission")
|
||||
.containsExactly(tuple("trillian", Set.of("read", "write"), false));
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAddNewVerbToExistingVerbsForGroup() {
|
||||
repository.setPermissions(
|
||||
List.of(
|
||||
new RepositoryPermission("hog", List.of("read"), true)
|
||||
)
|
||||
);
|
||||
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("hog");
|
||||
command.setVerbs("write");
|
||||
command.setForGroup(true);
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager).modify(argThat(argument -> {
|
||||
assertThat(argument.getPermissions()).extracting("name", "verbs", "groupPermission")
|
||||
.containsExactly(tuple("hog", Set.of("read", "write"), true));
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAddNewVerbToRoleAndReplaceRoleWithCustomPermissionsForUser() {
|
||||
repository.setPermissions(
|
||||
List.of(
|
||||
new RepositoryPermission("trillian", "READ", false)
|
||||
)
|
||||
);
|
||||
when(roleManager.get("READ"))
|
||||
.thenReturn(new RepositoryRole("READ", List.of("read", "pull"), ""));
|
||||
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("trillian");
|
||||
command.setVerbs("write");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager).modify(argThat(argument -> {
|
||||
assertThat(argument.getPermissions()).extracting("name", "verbs", "groupPermission")
|
||||
.containsExactly(tuple("trillian", Set.of("pull", "read", "write"), false));
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotModifyRoleIfNewVerbIsPartOfRole() {
|
||||
repository.setPermissions(
|
||||
List.of(
|
||||
new RepositoryPermission("trillian", "READ", false)
|
||||
)
|
||||
);
|
||||
when(roleManager.get("READ"))
|
||||
.thenReturn(new RepositoryRole("READ", List.of("read", "pull"), ""));
|
||||
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("trillian");
|
||||
command.setVerbs("read");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager, never()).modify(any());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleMissingVerb() {
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("trillian");
|
||||
command.setVerbs("make-party");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderVerbNotFoundError();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleIllegalNamespaceNameParameter() {
|
||||
command.setRepositoryName("illegal name");
|
||||
command.setName("trillian");
|
||||
command.setVerbs("write");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderInvalidInputError();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleNotExistingRepository() {
|
||||
command.setRepositoryName("no/repository");
|
||||
command.setName("trillian");
|
||||
command.setVerbs("write");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderNotFoundError();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.cli.PermissionDescriptionResolver;
|
||||
import sonia.scm.repository.RepositoryRole;
|
||||
import sonia.scm.repository.RepositoryRoleManager;
|
||||
import sonia.scm.security.RepositoryPermissionProvider;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Optional.empty;
|
||||
import static java.util.Optional.of;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RepositoryPermissionsAvailableCommandTest {
|
||||
|
||||
@Mock
|
||||
private RepositoryTemplateRenderer templateRenderer;
|
||||
@Mock
|
||||
private RepositoryPermissionProvider repositoryPermissionProvider;
|
||||
@Mock
|
||||
private PermissionDescriptionResolver permissionDescriptionResolver;
|
||||
@Mock
|
||||
private RepositoryRoleManager repositoryRoleManager;
|
||||
|
||||
@InjectMocks
|
||||
private RepositoryPermissionsAvailableCommand command;
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<Collection<VerbBean>> verbsCaptor;
|
||||
@Captor
|
||||
private ArgumentCaptor<Collection<RoleBean>> rolesCaptor;
|
||||
|
||||
@Test
|
||||
void shouldListVerbs() {
|
||||
doNothing().when(templateRenderer).render(any(), verbsCaptor.capture());
|
||||
when(repositoryPermissionProvider.availableVerbs())
|
||||
.thenReturn(List.of("read", "write"));
|
||||
when(permissionDescriptionResolver.getDescription("read"))
|
||||
.thenReturn(of("read repository"));
|
||||
when(permissionDescriptionResolver.getDescription("write"))
|
||||
.thenReturn(of("write repository"));
|
||||
|
||||
command.run();
|
||||
|
||||
Collection<VerbBean> capturedVerbs = verbsCaptor.getValue();
|
||||
|
||||
assertThat(capturedVerbs).
|
||||
extracting("verb")
|
||||
.containsExactly("read", "write");
|
||||
assertThat(capturedVerbs).
|
||||
extracting("description")
|
||||
.containsExactly("read repository", "write repository");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleMissingDescription() {
|
||||
doNothing().when(templateRenderer).render(any(), verbsCaptor.capture());
|
||||
when(repositoryPermissionProvider.availableVerbs())
|
||||
.thenReturn(List.of("unknown"));
|
||||
when(permissionDescriptionResolver.getDescription("unknown"))
|
||||
.thenReturn(empty());
|
||||
|
||||
command.run();
|
||||
|
||||
Collection<VerbBean> capturedVerbs = verbsCaptor.getValue();
|
||||
|
||||
assertThat(capturedVerbs).
|
||||
extracting("verb")
|
||||
.containsExactly("unknown");
|
||||
assertThat(capturedVerbs).
|
||||
extracting("description")
|
||||
.containsExactly("unknown");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRenderRoles() {
|
||||
doNothing().when(templateRenderer).render(rolesCaptor.capture(), any());
|
||||
when(repositoryRoleManager.getAll())
|
||||
.thenReturn(List.of(new RepositoryRole("READ", List.of("read", "pull"), null)));
|
||||
|
||||
command.run();
|
||||
|
||||
Collection<RoleBean> capturedRoles = rolesCaptor.getValue();
|
||||
|
||||
assertThat(capturedRoles)
|
||||
.extracting("name")
|
||||
.containsExactly("READ");
|
||||
assertThat(capturedRoles)
|
||||
.extracting("verbs")
|
||||
.map(c -> ((Collection) c).stream().collect(toList())) // to satisfy equal in the comparison, we have to use this form
|
||||
.containsExactly(List.of("read", "pull"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRenderRolesOnlyWithFlag() {
|
||||
command.setRoles(true);
|
||||
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderRoles(emptyList());
|
||||
verify(templateRenderer, never()).renderVerbs(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRenderVerbsOnlyWithFlag() {
|
||||
command.setVerbs(true);
|
||||
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderVerbs(emptyList());
|
||||
verify(templateRenderer, never()).renderRoles(any());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryPermission;
|
||||
import sonia.scm.repository.RepositoryTestData;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RepositoryPermissionsClearCommandTest {
|
||||
|
||||
@Mock
|
||||
private RepositoryManager repositoryManager;
|
||||
@Mock
|
||||
private RepositoryTemplateRenderer templateRenderer;
|
||||
|
||||
@InjectMocks
|
||||
private RepositoryPermissionsClearCommand command;
|
||||
|
||||
private final Repository repository = RepositoryTestData.createHeartOfGold();
|
||||
|
||||
@Nested
|
||||
class ForExistingRepository {
|
||||
|
||||
@BeforeEach
|
||||
void mockRepository() {
|
||||
when(repositoryManager.get(new NamespaceAndName("hitchhiker", "HeartOfGold")))
|
||||
.thenReturn(repository);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldClearPermissionsForUser() {
|
||||
repository.setPermissions(
|
||||
List.of(
|
||||
new RepositoryPermission("trillian", List.of("read"), false)
|
||||
)
|
||||
);
|
||||
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("trillian");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager).modify(argThat(argument -> {
|
||||
assertThat(argument.getPermissions()).isEmpty();
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldClearPermissionsForGroup() {
|
||||
repository.setPermissions(
|
||||
List.of(
|
||||
new RepositoryPermission("hog", "READ", true)
|
||||
)
|
||||
);
|
||||
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("hog");
|
||||
command.setForGroup(true);
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager).modify(argThat(argument -> {
|
||||
assertThat(argument.getPermissions()).isEmpty();
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleIllegalNamespaceNameParameter() {
|
||||
command.setRepositoryName("illegal name");
|
||||
command.setName("trillian");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderInvalidInputError();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleNotExistingRepository() {
|
||||
command.setRepositoryName("no/repository");
|
||||
command.setName("trillian");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderNotFoundError();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.cli.CommandValidator;
|
||||
import sonia.scm.cli.PermissionDescriptionResolver;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryPermission;
|
||||
import sonia.scm.repository.RepositoryRole;
|
||||
import sonia.scm.repository.RepositoryRoleManager;
|
||||
import sonia.scm.repository.RepositoryTestData;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Optional.of;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.groups.Tuple.tuple;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RepositoryPermissionsListCommandTest {
|
||||
|
||||
@Mock
|
||||
private RepositoryTemplateRenderer templateRenderer;
|
||||
@Mock
|
||||
private CommandValidator validator;
|
||||
@Mock
|
||||
private RepositoryManager manager;
|
||||
@Mock
|
||||
private RepositoryRoleManager roleManager;
|
||||
@Mock
|
||||
private PermissionDescriptionResolver permissionDescriptionResolver;
|
||||
|
||||
@InjectMocks
|
||||
private RepositoryPermissionsListCommand command;
|
||||
|
||||
@Test
|
||||
void shouldPrintNotFoundErrorForUnknownRepository() {
|
||||
command.setRepository("hg2g/hog");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderNotFoundError();
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ForExistingRepository {
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<Collection<RepositoryPermissionBean>> permissionsCaptor;
|
||||
|
||||
private final Repository repository = RepositoryTestData.createHeartOfGold();
|
||||
|
||||
@BeforeEach
|
||||
void mockRepository() {
|
||||
when(manager.get(new NamespaceAndName("hitchhiker", "HeartOfGold")))
|
||||
.thenReturn(repository);
|
||||
command.setRepository("hitchhiker/HeartOfGold");
|
||||
}
|
||||
|
||||
@Nested
|
||||
class WithoutVerboseFlag {
|
||||
|
||||
@BeforeEach
|
||||
void setUpRenderer() {
|
||||
doNothing().when(templateRenderer).render(permissionsCaptor.capture());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRenderEmptyTableWithoutPermissions() {
|
||||
command.run();
|
||||
|
||||
Collection<RepositoryPermissionBean> beans = permissionsCaptor.getValue();
|
||||
assertThat(beans).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldListCustomUserPermission() {
|
||||
RepositoryPermission permission = new RepositoryPermission("trillian", List.of("read", "write"), false);
|
||||
repository.setPermissions(List.of(permission));
|
||||
when(permissionDescriptionResolver.getDescription("read"))
|
||||
.thenReturn(of("read repository"));
|
||||
when(permissionDescriptionResolver.getDescription("write"))
|
||||
.thenReturn(of("write repository"));
|
||||
|
||||
command.run();
|
||||
|
||||
Collection<RepositoryPermissionBean> beans = permissionsCaptor.getValue();
|
||||
assertThat(beans).extracting("groupPermission", "name", "role")
|
||||
.containsExactly(tuple(false, "trillian", "CUSTOM"));
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class WithVerboseFlag {
|
||||
|
||||
@BeforeEach
|
||||
void setUpRenderer() {
|
||||
doNothing().when(templateRenderer).renderVerbose(permissionsCaptor.capture());
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setVerbose() {
|
||||
command.setVerbose(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldListUserPermissionWithVerbs() {
|
||||
RepositoryPermission permission = new RepositoryPermission("trillian", List.of("read", "write"), false);
|
||||
repository.setPermissions(List.of(permission));
|
||||
when(permissionDescriptionResolver.getDescription("read"))
|
||||
.thenReturn(of("read repository"));
|
||||
when(permissionDescriptionResolver.getDescription("write"))
|
||||
.thenReturn(of("write repository"));
|
||||
|
||||
command.run();
|
||||
|
||||
Collection<RepositoryPermissionBean> beans = permissionsCaptor.getValue();
|
||||
assertThat(beans).extracting("groupPermission", "name", "role", "verbs")
|
||||
.containsExactly(tuple(false, "trillian", "CUSTOM", List.of("read repository", "write repository")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldListUserPermissionWithRole() {
|
||||
RepositoryPermission permission = new RepositoryPermission("trillian", "READ", false);
|
||||
repository.setPermissions(List.of(permission));
|
||||
when(roleManager.get("READ"))
|
||||
.thenReturn(new RepositoryRole("READ", List.of("read", "pull"), ""));
|
||||
when(permissionDescriptionResolver.getDescription("read"))
|
||||
.thenReturn(of("read repository"));
|
||||
when(permissionDescriptionResolver.getDescription("pull"))
|
||||
.thenReturn(of("clone/checkout repository"));
|
||||
|
||||
command.run();
|
||||
|
||||
Collection<RepositoryPermissionBean> beans = permissionsCaptor.getValue();
|
||||
assertThat(beans).extracting("groupPermission", "name", "verbs")
|
||||
.containsExactly(tuple(false, "trillian", List.of("read repository", "clone/checkout repository")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldListUserPermissionWithVerbsAsKeys() {
|
||||
RepositoryPermission permission = new RepositoryPermission("trillian", List.of("read", "write"), false);
|
||||
repository.setPermissions(List.of(permission));
|
||||
|
||||
command.setKeys(true);
|
||||
command.run();
|
||||
|
||||
Collection<RepositoryPermissionBean> beans = permissionsCaptor.getValue();
|
||||
assertThat(beans).extracting("groupPermission", "name", "role")
|
||||
.containsExactly(tuple(false, "trillian", "CUSTOM"));
|
||||
assertThat(beans).extracting("verbs")
|
||||
.map(c -> ((Collection) c).stream().collect(toList())) // to satisfy equal in the comparison, we have to use this form
|
||||
.containsExactly(List.of("read", "write"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryPermission;
|
||||
import sonia.scm.repository.RepositoryRole;
|
||||
import sonia.scm.repository.RepositoryRoleManager;
|
||||
import sonia.scm.repository.RepositoryTestData;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.groups.Tuple.tuple;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RepositoryPermissionsRemoveCommandTest {
|
||||
|
||||
@Mock
|
||||
private RepositoryManager repositoryManager;
|
||||
@Mock
|
||||
private RepositoryRoleManager roleManager;
|
||||
@Mock
|
||||
private RepositoryTemplateRenderer templateRenderer;
|
||||
|
||||
@InjectMocks
|
||||
private RepositoryPermissionsRemoveCommand command;
|
||||
|
||||
private final Repository repository = RepositoryTestData.createHeartOfGold();
|
||||
|
||||
@Nested
|
||||
class ForExistingRepository {
|
||||
|
||||
@BeforeEach
|
||||
void mockRepository() {
|
||||
when(repositoryManager.get(new NamespaceAndName("hitchhiker", "HeartOfGold")))
|
||||
.thenReturn(repository);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRemoveMultipleVerbsFromExistingVerbsForUser() {
|
||||
repository.setPermissions(
|
||||
List.of(
|
||||
new RepositoryPermission("dent", List.of("read", "write", "push", "pull"), false)
|
||||
)
|
||||
);
|
||||
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("dent");
|
||||
command.setVerbs("write", "push", "pull");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager).modify(argThat(argument -> {
|
||||
assertThat(argument.getPermissions()).extracting("name", "verbs", "groupPermission")
|
||||
.containsExactly(tuple("dent", Set.of("read"), false));
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRemoveNewVerbFromExistingVerbsForGroup() {
|
||||
repository.setPermissions(
|
||||
List.of(
|
||||
new RepositoryPermission("hog", List.of("read", "write"), true)
|
||||
)
|
||||
);
|
||||
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("hog");
|
||||
command.setVerbs("write");
|
||||
command.setForGroup(true);
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager).modify(argThat(argument -> {
|
||||
assertThat(argument.getPermissions()).extracting("name", "verbs", "groupPermission")
|
||||
.containsExactly(tuple("hog", Set.of("read"), true));
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRemoveNewVerbToRoleAndReplaceRoleWithCustomPermissionsForUser() {
|
||||
repository.setPermissions(
|
||||
List.of(
|
||||
new RepositoryPermission("dent", "READ", false)
|
||||
)
|
||||
);
|
||||
when(roleManager.get("READ"))
|
||||
.thenReturn(new RepositoryRole("READ", List.of("read", "pull"), ""));
|
||||
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("dent");
|
||||
command.setVerbs("pull");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager).modify(argThat(argument -> {
|
||||
assertThat(argument.getPermissions()).extracting("name", "verbs", "groupPermission")
|
||||
.containsExactly(tuple("dent", Set.of("read"), false));
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotModifyRepositoryIfVerbsAreNotSet() {
|
||||
repository.setPermissions(
|
||||
List.of(
|
||||
new RepositoryPermission("dent", List.of("read", "write"), false)
|
||||
)
|
||||
);
|
||||
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("dent");
|
||||
command.setVerbs("push", "pull");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager, never()).modify(any());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleIllegalNamespaceNameParameter() {
|
||||
command.setRepositoryName("illegal name");
|
||||
command.setName("trillian");
|
||||
command.setVerbs("write");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderInvalidInputError();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleNotExistingRepository() {
|
||||
command.setRepositoryName("no/repository");
|
||||
command.setName("trillian");
|
||||
command.setVerbs("write");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderNotFoundError();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.cli;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryPermission;
|
||||
import sonia.scm.repository.RepositoryRole;
|
||||
import sonia.scm.repository.RepositoryRoleManager;
|
||||
import sonia.scm.repository.RepositoryTestData;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.groups.Tuple.tuple;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RepositoryPermissionsSetRoleCommandTest {
|
||||
|
||||
@Mock
|
||||
private RepositoryManager repositoryManager;
|
||||
@Mock
|
||||
private RepositoryRoleManager roleManager;
|
||||
@Mock
|
||||
private RepositoryTemplateRenderer templateRenderer;
|
||||
|
||||
@InjectMocks
|
||||
private RepositoryPermissionsSetRoleCommand command;
|
||||
|
||||
private final Repository repository = RepositoryTestData.createHeartOfGold();
|
||||
|
||||
@Nested
|
||||
class ForExistingRepository {
|
||||
|
||||
@BeforeEach
|
||||
void mockRepository() {
|
||||
when(repositoryManager.get(new NamespaceAndName("hitchhiker", "HeartOfGold")))
|
||||
.thenReturn(repository);
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ForExistingRole {
|
||||
|
||||
@BeforeEach
|
||||
void mockRole() {
|
||||
when(roleManager.get(any())).thenAnswer(
|
||||
invocation -> new RepositoryRole(invocation.getArgument(0, String.class), emptyList(), null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSetRoleForUser() {
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("trillian");
|
||||
command.setRole("OWNER");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager).modify(argThat(argument -> {
|
||||
assertThat(argument.getPermissions()).extracting("name", "role", "groupPermission")
|
||||
.containsExactly(tuple("trillian", "OWNER", false));
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSetRoleForGroup() {
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("crew");
|
||||
command.setRole("READ");
|
||||
command.setForGroup(true);
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager).modify(argThat(argument -> {
|
||||
assertThat(argument.getPermissions()).extracting("name", "role", "groupPermission")
|
||||
.containsExactly(tuple("crew", "READ", true));
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReplaceRepositoryPermissionForUser() {
|
||||
repository.setPermissions(
|
||||
List.of(
|
||||
new RepositoryPermission("trillian", List.of("read"), false)
|
||||
)
|
||||
);
|
||||
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("trillian");
|
||||
command.setRole("OWNER");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager).modify(argThat(argument -> {
|
||||
assertThat(argument.getPermissions()).extracting("name", "role", "groupPermission")
|
||||
.containsExactly(tuple("trillian", "OWNER", false));
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReplaceRepositoryPermissionForGroup() {
|
||||
repository.setPermissions(
|
||||
List.of(
|
||||
new RepositoryPermission("trillian", List.of("read"), true)
|
||||
)
|
||||
);
|
||||
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("trillian");
|
||||
command.setRole("OWNER");
|
||||
command.setForGroup(true);
|
||||
|
||||
command.run();
|
||||
|
||||
verify(repositoryManager).modify(argThat(argument -> {
|
||||
assertThat(argument.getPermissions()).extracting("name", "role", "groupPermission")
|
||||
.containsExactly(tuple("trillian", "OWNER", true));
|
||||
return true;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleMissingRole() {
|
||||
command.setRepositoryName("hitchhiker/HeartOfGold");
|
||||
command.setName("trillian");
|
||||
command.setRole("FUNNY");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderRoleNotFoundError();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleIllegalNamespaceNameParameter() {
|
||||
command.setRepositoryName("illegal name");
|
||||
command.setName("trillian");
|
||||
command.setRole("READ");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderInvalidInputError();
|
||||
verify(repositoryManager, never()).modify(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleNotExistingRepository() {
|
||||
command.setRepositoryName("no/repository");
|
||||
command.setName("trillian");
|
||||
command.setRole("READ");
|
||||
|
||||
command.run();
|
||||
|
||||
verify(templateRenderer).renderNotFoundError();
|
||||
verify(repositoryManager, never()).modify(any());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user