Add namespace cli commands (#2093)

Adds the CLI commands that are available to handle repository permissions on repositories for namespaces.

Co-authored-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
This commit is contained in:
René Pfeuffer
2022-07-26 09:58:00 +02:00
committed by GitHub
parent c190821a5e
commit 67a9dce7e6
38 changed files with 1599 additions and 403 deletions

View File

@@ -0,0 +1,60 @@
/*
* 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.cli.ParentCommand;
import sonia.scm.cli.TemplateRenderer;
import sonia.scm.repository.RepositoryManager;
import javax.inject.Inject;
import static java.util.Collections.emptyMap;
@ParentCommand(value = NamespaceCommand.class)
@CommandLine.Command(name = "list", aliases = "ls")
class NamespaceListCommand implements Runnable {
@CommandLine.Mixin
private final TemplateRenderer templateRenderer;
private final RepositoryManager manager;
@Inject
public NamespaceListCommand(RepositoryManager manager, TemplateRenderer templateRenderer, RepositoryToRepositoryCommandDtoMapper mapper) {
this.manager = manager;
this.templateRenderer = templateRenderer;
}
@Override
public void run() {
manager.getAllNamespaces()
.forEach(this::render);
}
private void render(String namespace) {
templateRenderer.renderToStdout(namespace + "\n", emptyMap());
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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 sonia.scm.repository.Namespace;
import sonia.scm.repository.NamespaceManager;
import java.util.Optional;
import static java.util.Optional.empty;
class NamespacePermissionBaseAdapter implements PermissionBaseAdapter<Namespace> {
private final NamespaceManager namespaceManager;
private final RepositoryTemplateRenderer templateRenderer;
NamespacePermissionBaseAdapter(NamespaceManager namespaceManager, RepositoryTemplateRenderer templateRenderer) {
this.namespaceManager = namespaceManager;
this.templateRenderer = templateRenderer;
}
@Override
public Optional<Namespace> get(String namespace) {
Optional<Namespace> ns = namespaceManager.get(namespace);
if (ns.isPresent()) {
return ns;
} else {
templateRenderer.renderNotFoundError();
return empty();
}
}
@Override
public void modify(Namespace namespace) {
namespaceManager.modify(namespace);
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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.Namespace;
import sonia.scm.repository.NamespaceManager;
import sonia.scm.repository.RepositoryRoleManager;
import javax.inject.Inject;
@CommandLine.Command(name = "add-permissions")
@ParentCommand(value = NamespaceCommand.class)
class NamespacePermissionsAddCommand extends PermissionsAddCommand<Namespace> implements Runnable {
@CommandLine.Parameters(paramLabel = "namespace", index = "0", descriptionKey = "scm.namespace.add-permissions.namespace")
private String namespace;
@Inject
NamespacePermissionsAddCommand(NamespaceManager namespaceManager, RepositoryRoleManager roleManager, PermissionDescriptionResolver permissionDescriptionResolver, RepositoryTemplateRenderer templateRenderer) {
super(roleManager, permissionDescriptionResolver, templateRenderer, new NamespacePermissionBaseAdapter(namespaceManager, templateRenderer));
}
@Override
String getIdentifier() {
return namespace;
}
@VisibleForTesting
void setNamespace(String namespace) {
this.namespace = namespace;
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.cli.ParentCommand;
import sonia.scm.cli.PermissionDescriptionResolver;
import sonia.scm.repository.RepositoryRoleManager;
import sonia.scm.security.RepositoryPermissionProvider;
import javax.inject.Inject;
@CommandLine.Command(name = "available-permissions")
@ParentCommand(value = NamespaceCommand.class)
class NamespacePermissionsAvailableCommand extends PermissionsAvailableCommand {
@Inject
public NamespacePermissionsAvailableCommand(RepositoryTemplateRenderer templateRenderer, RepositoryPermissionProvider repositoryPermissionProvider, PermissionDescriptionResolver permissionDescriptionResolver, RepositoryRoleManager repositoryRoleManager) {
super(templateRenderer, repositoryPermissionProvider, permissionDescriptionResolver, repositoryRoleManager);
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.cli.ParentCommand;
import sonia.scm.repository.Namespace;
import sonia.scm.repository.NamespaceManager;
import sonia.scm.repository.RepositoryRoleManager;
import javax.inject.Inject;
@CommandLine.Command(name = "clear-permissions")
@ParentCommand(value = NamespaceCommand.class)
class NamespacePermissionsClearCommand extends PermissionClearCommand<Namespace> {
@CommandLine.Parameters(paramLabel = "namespace", index = "0", descriptionKey = "scm.namespace.clear-permissions.namespace")
private String namespace;
@Inject
public NamespacePermissionsClearCommand(NamespaceManager namespaceManager, RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer) {
super(roleManager, templateRenderer, new NamespacePermissionBaseAdapter(namespaceManager, templateRenderer));
}
@Override
String getIdentifier() {
return namespace;
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.repository.Namespace;
import sonia.scm.repository.NamespaceManager;
import javax.inject.Inject;
@CommandLine.Command(name = "list-permissions")
@ParentCommand(value = NamespaceCommand.class)
class NamespacePermissionsListCommand extends PermissionsListCommand<Namespace> {
@CommandLine.Parameters(paramLabel = "namespace", index = "0", descriptionKey = "scm.namespace.list-permissions.namespace")
private String namespace;
@Inject
public NamespacePermissionsListCommand(RepositoryTemplateRenderer templateRenderer, CommandValidator validator, NamespaceManager manager, RepositoryPermissionBeanMapper beanMapper) {
super(templateRenderer, validator, new NamespacePermissionBaseAdapter(manager, templateRenderer), beanMapper);
}
@Override
String getIdentifier() {
return namespace;
}
@VisibleForTesting
void setNamespace(String namespace) {
this.namespace = namespace;
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.Namespace;
import sonia.scm.repository.NamespaceManager;
import sonia.scm.repository.RepositoryRoleManager;
import javax.inject.Inject;
@CommandLine.Command(name = "remove-permissions")
@ParentCommand(value = NamespaceCommand.class)
class NamespacePermissionsRemoveCommand extends PermissionsRemoveCommand<Namespace> {
@CommandLine.Parameters(paramLabel = "namespace", index = "0", descriptionKey = "scm.namespace.remove-permissions.namespace")
private String namespace;
@Inject
public NamespacePermissionsRemoveCommand(NamespaceManager namespaceManager, RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer) {
super(roleManager, templateRenderer, new NamespacePermissionBaseAdapter(namespaceManager, templateRenderer));
}
@Override
String getIdentifier() {
return namespace;
}
@VisibleForTesting
void setNamespace(String namespace) {
this.namespace = namespace;
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.Namespace;
import sonia.scm.repository.NamespaceManager;
import sonia.scm.repository.RepositoryRoleManager;
import javax.inject.Inject;
@CommandLine.Command(name = "set-role")
@ParentCommand(value = NamespaceCommand.class)
class NamespacePermissionsSetRoleCommand extends PermissionsSetRoleCommand<Namespace> {
@CommandLine.Parameters(paramLabel = "namespace", index = "0", descriptionKey = "scm.namespace.set-role.namespace")
private String namespace;
@Inject
public NamespacePermissionsSetRoleCommand(NamespaceManager namespaceManager, RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer) {
super(roleManager, templateRenderer, new NamespacePermissionBaseAdapter(namespaceManager, templateRenderer));
}
@Override
protected String getIdentifier() {
return namespace;
}
@VisibleForTesting
void setNamespace(String namespace) {
this.namespace = namespace;
}
}

View File

@@ -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 sonia.scm.repository.RepositoryPermissionHolder;
import java.util.Optional;
public interface PermissionBaseAdapter<T extends RepositoryPermissionHolder> {
Optional<T> get(String identifier);
void modify(T object);
}

View File

@@ -25,10 +25,8 @@
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.RepositoryPermissionHolder;
import sonia.scm.repository.RepositoryRoleManager;
import javax.inject.Inject;
@@ -37,64 +35,60 @@ import java.util.HashSet;
import java.util.Optional;
import java.util.function.Predicate;
class RepositoryPermissionBaseCommand {
abstract class PermissionBaseCommand<T extends RepositoryPermissionHolder> {
private final RepositoryManager repositoryManager;
private final RepositoryRoleManager roleManager;
@CommandLine.Mixin
private final RepositoryTemplateRenderer templateRenderer;
private final PermissionBaseAdapter<T> adapter;
@Inject
RepositoryPermissionBaseCommand(RepositoryManager repositoryManager, RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer) {
this.repositoryManager = repositoryManager;
PermissionBaseCommand(RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer, PermissionBaseAdapter<T> adapter) {
this.roleManager = roleManager;
this.templateRenderer = templateRenderer;
this.adapter = adapter;
}
void modifyRepository(String repositoryName, Predicate<Repository> modifier) {
NamespaceAndName namespaceAndName;
try {
namespaceAndName = NamespaceAndName.fromString(repositoryName);
} catch (IllegalArgumentException e) {
templateRenderer.renderInvalidInputError();
return;
}
Optional<T> get(String identifier) {
return adapter.get(identifier);
}
Repository repository = repositoryManager.get(namespaceAndName);
if (repository != null) {
if (modifier.test(repository)) {
repositoryManager.modify(repository);
}
} else {
templateRenderer.renderNotFoundError();
void set(T object) {
adapter.modify(object);
}
void modify(String identifier, Predicate<T> modifier) {
Optional<T> ns = get(identifier);
if (ns.isPresent() && modifier.test(ns.get())) {
set(ns.get());
}
}
void replacePermission(Repository repository, RepositoryPermission permission) {
this.removeExistingPermission(repository, permission.getName(), permission.isGroupPermission());
repository.addPermission(permission);
void replacePermission(T namespace, RepositoryPermission permission) {
removeExistingPermission(namespace, permission.getName(), permission.isGroupPermission());
namespace.addPermission(permission);
}
void removeExistingPermission(Repository repository, String name, boolean forGroup) {
void removeExistingPermission(T permissionHolder, String name, boolean forGroup) {
if (!forGroup) {
repository.findUserPermission(name).ifPresent(repository::removePermission);
permissionHolder.findUserPermission(name).ifPresent(permissionHolder::removePermission);
} else {
repository.findGroupPermission(name).ifPresent(repository::removePermission);
permissionHolder.findGroupPermission(name).ifPresent(permissionHolder::removePermission);
}
}
HashSet<String> getPermissionsAsModifiableSet(Repository repository, String name, boolean forGroup) {
return this.getExistingPermissions(repository, name, forGroup)
HashSet<String> getPermissionsAsModifiableSet(T permissionHolder, String name, boolean forGroup) {
return getExistingPermissions(permissionHolder, name, forGroup)
.map(this::getVerbs)
.map(HashSet::new)
.orElseGet(HashSet::new);
}
private Optional<RepositoryPermission> getExistingPermissions(Repository repo, String name, boolean forGroup) {
private Optional<RepositoryPermission> getExistingPermissions(T permissionHolder, String name, boolean forGroup) {
if (!forGroup) {
return repo.findUserPermission(name);
return permissionHolder.findUserPermission(name);
} else {
return repo.findGroupPermission(name);
return permissionHolder.findGroupPermission(name);
}
}
@@ -110,7 +104,7 @@ class RepositoryPermissionBaseCommand {
templateRenderer.renderRoleNotFoundError();
}
void renderVerbNotFoundError() {
templateRenderer.renderVerbNotFoundError();
void renderVerbNotFoundError(String verb) {
templateRenderer.renderVerbNotFoundError(verb);
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.repository.RepositoryPermissionHolder;
import sonia.scm.repository.RepositoryRoleManager;
abstract class PermissionClearCommand<T extends RepositoryPermissionHolder> extends PermissionBaseCommand<T> implements Runnable {
@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;
PermissionClearCommand(RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer, PermissionBaseAdapter<T> adapter) {
super(roleManager, templateRenderer, adapter);
}
@Override
public void run() {
modify(
getIdentifier(),
ns -> {
removeExistingPermission(ns, name, forGroup);
return true;
}
);
}
abstract String getIdentifier();
@VisibleForTesting
void setName(String name) {
this.name = name;
}
@VisibleForTesting
void setForGroup(boolean forGroup) {
this.forGroup = forGroup;
}
}

View File

@@ -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.PermissionDescriptionResolver;
import sonia.scm.repository.RepositoryPermission;
import sonia.scm.repository.RepositoryPermissionHolder;
import sonia.scm.repository.RepositoryRoleManager;
import java.util.Arrays;
import java.util.Set;
import static java.util.Arrays.asList;
abstract class PermissionsAddCommand<T extends RepositoryPermissionHolder> extends PermissionBaseCommand<T> implements Runnable {
private final PermissionDescriptionResolver permissionDescriptionResolver;
@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;
PermissionsAddCommand(RepositoryRoleManager roleManager, PermissionDescriptionResolver permissionDescriptionResolver, RepositoryTemplateRenderer templateRenderer, PermissionBaseAdapter<T> adapter) {
super(roleManager, templateRenderer, adapter);
this.permissionDescriptionResolver = permissionDescriptionResolver;
}
abstract String getIdentifier();
@Override
public void run() {
modify(
getIdentifier(),
ns -> {
if (!Arrays.stream(verbs).allMatch(this::verifyVerbExists)) {
return false;
}
Set<String> resultingVerbs =
getPermissionsAsModifiableSet(ns, name, forGroup);
if (resultingVerbs.containsAll(asList(this.verbs))) {
return false;
}
resultingVerbs.addAll(asList(this.verbs));
replacePermission(ns, new RepositoryPermission(name, resultingVerbs, forGroup));
return true;
}
);
}
private boolean verifyVerbExists(String verb) {
if (permissionDescriptionResolver.getDescription(verb).isEmpty()) {
renderVerbNotFoundError(verb);
return false;
}
return true;
}
@VisibleForTesting
void setName(String name) {
this.name = name;
}
@VisibleForTesting
void setVerbs(String... verbs) {
this.verbs = verbs;
}
@VisibleForTesting
void setForGroup(boolean forGroup) {
this.forGroup = forGroup;
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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.PermissionDescriptionResolver;
import sonia.scm.repository.RepositoryRole;
import sonia.scm.repository.RepositoryRoleManager;
import sonia.scm.security.RepositoryPermissionProvider;
import java.util.List;
import static java.util.stream.Collectors.toList;
class PermissionsAvailableCommand 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;
public PermissionsAvailableCommand(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());
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.repository.RepositoryPermissionHolder;
import java.util.Collection;
import java.util.Optional;
abstract class PermissionsListCommand<T extends RepositoryPermissionHolder> implements Runnable {
@CommandLine.Mixin
private final RepositoryTemplateRenderer templateRenderer;
@CommandLine.Mixin
private final CommandValidator validator;
private final PermissionBaseAdapter<T> adapter;
private final RepositoryPermissionBeanMapper beanMapper;
@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;
PermissionsListCommand(RepositoryTemplateRenderer templateRenderer, CommandValidator validator, PermissionBaseAdapter<T> adapter, RepositoryPermissionBeanMapper beanMapper) {
this.templateRenderer = templateRenderer;
this.validator = validator;
this.adapter = adapter;
this.beanMapper = beanMapper;
}
@VisibleForTesting
void setVerbose(boolean verbose) {
this.verbose = verbose;
}
public void setKeys(boolean keys) {
this.keys = keys;
}
@Override
public void run() {
validator.validate();
Optional<T> object = adapter.get(getIdentifier());
if (object.isPresent()) {
Collection<RepositoryPermissionBean> permissions = beanMapper.createPermissionBeans(object.get().getPermissions(), keys);
if (verbose) {
templateRenderer.renderVerbose(permissions);
} else {
templateRenderer.render(permissions);
}
}
}
abstract String getIdentifier();
}

View File

@@ -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.repository.cli;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.repository.RepositoryPermission;
import sonia.scm.repository.RepositoryPermissionHolder;
import sonia.scm.repository.RepositoryRoleManager;
import java.util.Set;
import static java.util.Arrays.asList;
abstract class PermissionsRemoveCommand<T extends RepositoryPermissionHolder> extends PermissionBaseCommand<T> implements Runnable {
@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;
PermissionsRemoveCommand(RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer, PermissionBaseAdapter<T> adapter) {
super(roleManager, templateRenderer, adapter);
}
@Override
public void run() {
modify(
getIdentifier(),
ns -> {
Set<String> resultingVerbs =
getPermissionsAsModifiableSet(ns, name, forGroup);
if (resultingVerbs.stream().noneMatch(verb -> asList(verbs).contains(verb))) {
return false;
}
resultingVerbs.removeAll(asList(this.verbs));
replacePermission(ns, new RepositoryPermission(name, resultingVerbs, forGroup));
return true;
}
);
}
abstract String getIdentifier();
@VisibleForTesting
void setName(String name) {
this.name = name;
}
@VisibleForTesting
void setVerbs(String... verbs) {
this.verbs = verbs;
}
@VisibleForTesting
void setForGroup(boolean forGroup) {
this.forGroup = forGroup;
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.repository.RepositoryPermission;
import sonia.scm.repository.RepositoryPermissionHolder;
import sonia.scm.repository.RepositoryRoleManager;
abstract class PermissionsSetRoleCommand<T extends RepositoryPermissionHolder> extends PermissionBaseCommand<T> implements Runnable {
private final RepositoryRoleManager roleManager;
@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;
PermissionsSetRoleCommand(RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer, PermissionBaseAdapter<T> adapter) {
super(roleManager, templateRenderer, adapter);
this.roleManager = roleManager;
}
@Override
public void run() {
modify(
getIdentifier(),
ns -> {
if (roleManager.get(role) == null) {
renderRoleNotFoundError();
return false;
}
replacePermission(ns, new RepositoryPermission(name, role, forGroup));
return true;
}
);
}
protected abstract String getIdentifier();
@VisibleForTesting
void setRole(String role) {
this.role = role;
}
@VisibleForTesting
void setName(String name) {
this.name = name;
}
@VisibleForTesting
void setForGroup(boolean forGroup) {
this.forGroup = forGroup;
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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 sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryManager;
import java.util.Optional;
import static java.util.Optional.empty;
import static java.util.Optional.of;
class RepositoryPermissionBaseAdapter implements PermissionBaseAdapter<Repository> {
private final RepositoryManager repositoryManager;
private final RepositoryTemplateRenderer templateRenderer;
RepositoryPermissionBaseAdapter(RepositoryManager repositoryManager, RepositoryTemplateRenderer templateRenderer) {
this.repositoryManager = repositoryManager;
this.templateRenderer = templateRenderer;
}
@Override
public Optional<Repository> get(String repositoryNamespaceAndName) {
NamespaceAndName namespaceAndName;
try {
namespaceAndName = NamespaceAndName.fromString(repositoryNamespaceAndName);
} catch (IllegalArgumentException e) {
templateRenderer.renderInvalidInputError();
return empty();
}
Repository repository = repositoryManager.get(namespaceAndName);
if (repository == null) {
templateRenderer.renderNotFoundError();
return empty();
} else {
return of(repository);
}
}
@Override
public void modify(Repository repository) {
repositoryManager.modify(repository);
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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 sonia.scm.cli.PermissionDescriptionResolver;
import sonia.scm.repository.RepositoryPermission;
import sonia.scm.repository.RepositoryRoleManager;
import javax.inject.Inject;
import java.util.Collection;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toList;
class RepositoryPermissionBeanMapper {
private final RepositoryRoleManager roleManager;
private final PermissionDescriptionResolver permissionDescriptionResolver;
@Inject
RepositoryPermissionBeanMapper(RepositoryRoleManager roleManager, PermissionDescriptionResolver permissionDescriptionResolver) {
this.roleManager = roleManager;
this.permissionDescriptionResolver = permissionDescriptionResolver;
}
Collection<RepositoryPermissionBean> createPermissionBeans(Collection<RepositoryPermission> permissions, boolean keys) {
return permissions
.stream()
.map(permission -> createPermissionBean(permission, keys))
.collect(toList());
}
private RepositoryPermissionBean createPermissionBean(RepositoryPermission permission, boolean keys) {
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);
}
}

View File

@@ -28,82 +28,31 @@ import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.cli.PermissionDescriptionResolver;
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.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;
class RepositoryPermissionsAddCommand extends PermissionsAddCommand<Repository> implements Runnable {
@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;
private String repositoryNamespaceAndName;
@Inject
RepositoryPermissionsAddCommand(RepositoryManager repositoryManager, RepositoryPermissionBaseCommand permissionCommandManager, RepositoryRoleManager roleManager, PermissionDescriptionResolver permissionDescriptionResolver, RepositoryTemplateRenderer templateRenderer) {
super(repositoryManager, roleManager, templateRenderer);
this.permissionDescriptionResolver = permissionDescriptionResolver;
RepositoryPermissionsAddCommand(RepositoryManager repositoryManager, RepositoryRoleManager roleManager, PermissionDescriptionResolver permissionDescriptionResolver, RepositoryTemplateRenderer templateRenderer) {
super(roleManager, permissionDescriptionResolver, templateRenderer, new RepositoryPermissionBaseAdapter(repositoryManager, templateRenderer));
}
@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;
String getIdentifier() {
return repositoryNamespaceAndName;
}
@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;
void setRepositoryNamespaceAndName(String repositoryNamespaceAndName) {
this.repositoryNamespaceAndName = repositoryNamespaceAndName;
}
}

View File

@@ -24,76 +24,20 @@
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;
class RepositoryPermissionsAvailableCommand extends PermissionsAvailableCommand {
@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());
super(templateRenderer, repositoryPermissionProvider, permissionDescriptionResolver, repositoryRoleManager);
}
}

View File

@@ -27,6 +27,7 @@ package sonia.scm.repository.cli;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.repository.RepositoryRoleManager;
@@ -34,43 +35,23 @@ import javax.inject.Inject;
@CommandLine.Command(name = "clear-permissions")
@ParentCommand(value = RepositoryCommand.class)
class RepositoryPermissionsClearCommand extends RepositoryPermissionBaseCommand implements Runnable {
class RepositoryPermissionsClearCommand extends PermissionClearCommand<Repository> {
@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;
private String repositoryNamespaceAndName;
@Inject
public RepositoryPermissionsClearCommand(RepositoryManager repositoryManager, RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer) {
super(repositoryManager, roleManager, templateRenderer);
super(roleManager, templateRenderer, new RepositoryPermissionBaseAdapter(repositoryManager, templateRenderer));
}
@Override
public void run() {
modifyRepository(
repositoryName,
repo -> {
removeExistingPermission(repo, name, forGroup);
return true;
}
);
String getIdentifier() {
return repositoryNamespaceAndName;
}
@VisibleForTesting
void setRepositoryName(String repositoryName) {
this.repositoryName = repositoryName;
}
@VisibleForTesting
void setName(String name) {
this.name = name;
}
public void setForGroup(boolean forGroup) {
this.forGroup = forGroup;
void setRepositoryNamespaceAndName(String repositoryNamespaceAndName) {
this.repositoryNamespaceAndName = repositoryNamespaceAndName;
}
}

View File

@@ -28,102 +28,30 @@ 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;
class RepositoryPermissionsListCommand extends PermissionsListCommand<Repository> {
@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;
public RepositoryPermissionsListCommand(RepositoryTemplateRenderer templateRenderer, CommandValidator validator, RepositoryManager manager, RepositoryPermissionBeanMapper beanMapper) {
super(templateRenderer, validator, new RepositoryPermissionBaseAdapter(manager, templateRenderer), beanMapper);
}
@Override
String getIdentifier() {
return repository;
}
@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);
}
}

View File

@@ -27,66 +27,31 @@ package sonia.scm.repository.cli;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
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.Set;
import static java.util.Arrays.asList;
@CommandLine.Command(name = "remove-permissions")
@ParentCommand(value = RepositoryCommand.class)
class RepositoryPermissionsRemoveCommand extends RepositoryPermissionBaseCommand implements Runnable {
class RepositoryPermissionsRemoveCommand extends PermissionsRemoveCommand<Repository> {
@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;
private String repositoryNamespaceAndName;
@Inject
public RepositoryPermissionsRemoveCommand(RepositoryManager repositoryManager, RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer) {
super(repositoryManager, roleManager, templateRenderer);
super(roleManager, templateRenderer, new RepositoryPermissionBaseAdapter(repositoryManager, 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;
}
);
String getIdentifier() {
return repositoryNamespaceAndName;
}
@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;
void setRepositoryNamespaceAndName(String repositoryNamespaceAndName) {
this.repositoryNamespaceAndName = repositoryNamespaceAndName;
}
}

View File

@@ -27,65 +27,31 @@ package sonia.scm.repository.cli;
import com.google.common.annotations.VisibleForTesting;
import picocli.CommandLine;
import sonia.scm.cli.ParentCommand;
import sonia.scm.repository.Repository;
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;
class RepositoryPermissionsSetRoleCommand extends PermissionsSetRoleCommand<Repository> {
@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;
private String repositoryNamespaceAndName;
@Inject
public RepositoryPermissionsSetRoleCommand(RepositoryManager repositoryManager, RepositoryRoleManager roleManager, RepositoryTemplateRenderer templateRenderer) {
super(repositoryManager, roleManager, templateRenderer);
this.roleManager = roleManager;
super(roleManager, templateRenderer, new RepositoryPermissionBaseAdapter(repositoryManager, templateRenderer));
}
@Override
public void run() {
modifyRepository(
repositoryName,
repository -> {
if (roleManager.get(role) == null) {
renderRoleNotFoundError();
return false;
}
replacePermission(repository, new RepositoryPermission(name, role, forGroup));
return true;
}
);
protected String getIdentifier() {
return repositoryNamespaceAndName;
}
@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;
void setRepositoryNamespaceAndName(String repositoryNamespaceAndName) {
this.repositoryNamespaceAndName = repositoryNamespaceAndName;
}
}

View File

@@ -131,8 +131,8 @@ class RepositoryTemplateRenderer extends TemplateRenderer {
context.exit(ExitCode.NOT_FOUND);
}
void renderVerbNotFoundError() {
renderToStderr("{{i18n.verbNotFound}}", emptyMap());
void renderVerbNotFoundError(String verb) {
renderToStderr("{{i18n.verbNotFound}}: {{verb}}", Map.of("verb", verb));
context.getStderr().println();
context.exit(ExitCode.NOT_FOUND);
}