mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-05-05 13:57:27 +02:00
Implement namespace configurations & permissions
Co-authored-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com> Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com> Reviewed-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
This commit is contained in:
@@ -27,10 +27,13 @@ package sonia.scm.api.v2.resources;
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import de.otto.edison.hal.Links;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@SuppressWarnings("java:S2160") // we don't need equals and hashcode
|
||||
public class NamespaceDto extends HalRepresentation {
|
||||
|
||||
private String namespace;
|
||||
|
||||
@@ -24,32 +24,48 @@
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import de.otto.edison.hal.Embedded;
|
||||
import de.otto.edison.hal.Link;
|
||||
import de.otto.edison.hal.Links;
|
||||
import org.mapstruct.InjectionStrategy;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.ObjectFactory;
|
||||
import sonia.scm.repository.Namespace;
|
||||
import sonia.scm.repository.NamespaceManager;
|
||||
import sonia.scm.repository.NamespacePermissions;
|
||||
import sonia.scm.search.SearchEngine;
|
||||
import sonia.scm.search.SearchableType;
|
||||
import sonia.scm.web.EdisonHalAppender;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static de.otto.edison.hal.Embedded.embeddedBuilder;
|
||||
import static de.otto.edison.hal.Link.link;
|
||||
import static de.otto.edison.hal.Link.linkBuilder;
|
||||
import static de.otto.edison.hal.Links.linkingTo;
|
||||
|
||||
class NamespaceToNamespaceDtoMapper {
|
||||
|
||||
private final ResourceLinks links;
|
||||
private final SearchEngine searchEngine;
|
||||
// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection.
|
||||
@SuppressWarnings("squid:S3306")
|
||||
@Mapper
|
||||
public abstract class NamespaceToNamespaceDtoMapper extends BaseMapper<Namespace, NamespaceDto> {
|
||||
|
||||
@Inject
|
||||
NamespaceToNamespaceDtoMapper(ResourceLinks links, SearchEngine searchEngine) {
|
||||
this.links = links;
|
||||
this.searchEngine = searchEngine;
|
||||
}
|
||||
protected ResourceLinks links;
|
||||
@Inject
|
||||
protected SearchEngine searchEngine;
|
||||
@Inject
|
||||
protected NamespaceManager namespaceManager;
|
||||
|
||||
NamespaceDto map(String namespace) {
|
||||
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
||||
public abstract NamespaceDto map(String namespace);
|
||||
|
||||
@ObjectFactory
|
||||
NamespaceDto createDto(String namespace) {
|
||||
Links.Builder linkingTo = linkingTo();
|
||||
linkingTo
|
||||
.self(links.namespace().self(namespace))
|
||||
@@ -61,9 +77,30 @@ class NamespaceToNamespaceDtoMapper {
|
||||
}
|
||||
linkingTo.array(searchLinks(namespace));
|
||||
linkingTo.single(link("searchableTypes", links.searchableTypes().searchableTypesForNamespace(namespace)));
|
||||
Optional<Namespace> optionalNamespace = namespaceManager.get(namespace);
|
||||
if (optionalNamespace.isPresent()) {
|
||||
Embedded.Builder embeddedBuilder = embeddedBuilder();
|
||||
applyEnrichers(new EdisonHalAppender(linkingTo, embeddedBuilder), optionalNamespace.get(), namespace);
|
||||
}
|
||||
|
||||
return new NamespaceDto(namespace, linkingTo.build());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setLinks(ResourceLinks links) {
|
||||
this.links = links;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setSearchEngine(SearchEngine searchEngine) {
|
||||
this.searchEngine = searchEngine;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setNamespaceManager(NamespaceManager namespaceManager) {
|
||||
this.namespaceManager = namespaceManager;
|
||||
}
|
||||
|
||||
private List<Link> searchLinks(String namespace) {
|
||||
return searchEngine.getSearchableTypes().stream()
|
||||
.filter(SearchableType::limitableToNamespace)
|
||||
|
||||
@@ -28,6 +28,7 @@ import sonia.scm.store.DataStore;
|
||||
import sonia.scm.store.DataStoreFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
public class NamespaceDao {
|
||||
@@ -50,4 +51,8 @@ public class NamespaceDao {
|
||||
public void delete(String namespace) {
|
||||
store.remove(namespace);
|
||||
}
|
||||
|
||||
public Collection<Namespace> allWithPermissions() {
|
||||
return store.getAll().values();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,35 +58,25 @@ import java.util.Set;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@Singleton
|
||||
@Extension
|
||||
public class DefaultAuthorizationCollector implements AuthorizationCollector
|
||||
{
|
||||
public class DefaultAuthorizationCollector implements AuthorizationCollector {
|
||||
|
||||
/** Field description */
|
||||
private static final String CACHE_NAME = "sonia.cache.authorizing";
|
||||
|
||||
/**
|
||||
* the logger for DefaultAuthorizationCollector
|
||||
*/
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(DefaultAuthorizationCollector.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
* @param cacheManager
|
||||
* @param repositoryDAO
|
||||
* @param securitySystem
|
||||
* @param repositoryPermissionProvider
|
||||
* @param groupCollector
|
||||
* @param namespaceDao
|
||||
*/
|
||||
/** authorization cache */
|
||||
private final Cache<CacheKey, AuthorizationInfo> cache;
|
||||
|
||||
private final RepositoryDAO repositoryDAO;
|
||||
private final NamespaceDao namespaceDao;
|
||||
private final SecuritySystem securitySystem;
|
||||
private final RepositoryPermissionProvider repositoryPermissionProvider;
|
||||
private final GroupCollector groupCollector;
|
||||
|
||||
@Inject
|
||||
public DefaultAuthorizationCollector(CacheManager cacheManager,
|
||||
RepositoryDAO repositoryDAO, SecuritySystem securitySystem, RepositoryPermissionProvider repositoryPermissionProvider, GroupCollector groupCollector, NamespaceDao namespaceDao)
|
||||
@@ -99,14 +89,6 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
||||
this.namespaceDao = namespaceDao;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@VisibleForTesting
|
||||
AuthorizationInfo collect()
|
||||
{
|
||||
@@ -125,13 +107,6 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
||||
return authorizationInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
* @param principals
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public AuthorizationInfo collect(PrincipalCollection principals)
|
||||
{
|
||||
@@ -176,6 +151,35 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
||||
}
|
||||
}
|
||||
|
||||
private void collectNamespacePermissions(Builder<String> builder, User user, Set<String> groups) {
|
||||
for (Namespace namespace : namespaceDao.allWithPermissions()) {
|
||||
collectNamespacePermissions(builder, namespace, user, groups);
|
||||
}
|
||||
}
|
||||
|
||||
private void collectNamespacePermissions(Builder<String> builder, Namespace namespace, User user, Set<String> groups) {
|
||||
for (RepositoryPermission permission : namespace.getPermissions()) {
|
||||
if (isUserPermitted(user, groups, permission)) {
|
||||
addNamespacePermission(builder, namespace, user, permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addNamespacePermission(Builder<String> builder, Namespace namespace, User user, RepositoryPermission permission) {
|
||||
Collection<String> verbs = getVerbs(permission);
|
||||
if (!verbs.isEmpty())
|
||||
{
|
||||
String perm = "namespace:" + String.join(",", verbs) + ":" + namespace.getId();
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("add namespace permission {} for user {} at namespace {}",
|
||||
perm, user.getName(), namespace.getNamespace());
|
||||
}
|
||||
|
||||
builder.add(perm);
|
||||
}
|
||||
}
|
||||
|
||||
private void collectRepositoryPermissions(Builder<String> builder, User user,
|
||||
Set<String> groups)
|
||||
{
|
||||
@@ -245,6 +249,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
||||
|
||||
collectGlobalPermissions(builder, user, groups);
|
||||
collectRepositoryPermissions(builder, user, groups);
|
||||
collectNamespacePermissions(builder, user, groups);
|
||||
builder.add(canReadOwnUser(user));
|
||||
if (!Authentications.isSubjectAnonymous(user.getName())) {
|
||||
builder.add(getUserAutocompletePermission());
|
||||
@@ -254,7 +259,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
||||
builder.add(getPublicKeyPermission(user));
|
||||
}
|
||||
|
||||
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(ImmutableSet.of(Role.USER));
|
||||
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(Set.of(Role.USER));
|
||||
info.addStringPermissions(builder.build());
|
||||
|
||||
return info;
|
||||
@@ -314,24 +319,17 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
//~--- inner classes --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Cache key.
|
||||
*/
|
||||
private static class CacheKey
|
||||
{
|
||||
private final Set<String> groupnames;
|
||||
private final String username;
|
||||
|
||||
private CacheKey(String username, Set<String> groupnames)
|
||||
{
|
||||
this.username = username;
|
||||
this.groupnames = groupnames;
|
||||
}
|
||||
|
||||
//~--- methods ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
@@ -351,36 +349,10 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
||||
&& Objects.equal(groupnames, other.groupnames);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(username, groupnames);
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** group names */
|
||||
private final Set<String> groupnames;
|
||||
|
||||
/** username */
|
||||
private final String username;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** authorization cache */
|
||||
private final Cache<CacheKey, AuthorizationInfo> cache;
|
||||
|
||||
/** repository dao */
|
||||
private final RepositoryDAO repositoryDAO;
|
||||
|
||||
/** security system */
|
||||
private final SecuritySystem securitySystem;
|
||||
|
||||
private final RepositoryPermissionProvider repositoryPermissionProvider;
|
||||
private final GroupCollector groupCollector;
|
||||
private final NamespaceDao namespaceDao;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user