mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 08:25:44 +01:00
Introduce index resource with first links
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import de.otto.edison.hal.HalRepresentation;
|
||||||
|
import de.otto.edison.hal.Links;
|
||||||
|
|
||||||
|
public class IndexDto extends HalRepresentation {
|
||||||
|
|
||||||
|
IndexDto(Links links) {
|
||||||
|
super(links);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import de.otto.edison.hal.Link;
|
||||||
|
import de.otto.edison.hal.Links;
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class IndexDtoGenerator {
|
||||||
|
|
||||||
|
private final ResourceLinks resourceLinks;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public IndexDtoGenerator(ResourceLinks resourceLinks) {
|
||||||
|
this.resourceLinks = resourceLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IndexDto generate() {
|
||||||
|
Links.Builder builder = Links.linkingTo();
|
||||||
|
if (SecurityUtils.getSubject().isAuthenticated()) {
|
||||||
|
builder.single(
|
||||||
|
Link.link("me", resourceLinks.me().self()),
|
||||||
|
Link.link("logout", resourceLinks.authentication().logout())
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
builder.single(
|
||||||
|
Link.link("formLogin", resourceLinks.authentication().formLogin()),
|
||||||
|
Link.link("jsonLogin", resourceLinks.authentication().jsonLogin())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new IndexDto(builder.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
|
||||||
|
@Path(IndexResource.INDEX_PATH_V2)
|
||||||
|
public class IndexResource {
|
||||||
|
public static final String INDEX_PATH_V2 = "v2/";
|
||||||
|
|
||||||
|
private final IndexDtoGenerator indexDtoGenerator;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public IndexResource(IndexDtoGenerator indexDtoGenerator) {
|
||||||
|
this.indexDtoGenerator = indexDtoGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public IndexDto getIndex() {
|
||||||
|
return indexDtoGenerator.generate();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ package sonia.scm.api.v2.resources;
|
|||||||
import sonia.scm.repository.NamespaceAndName;
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.ws.rs.core.UriInfo;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
class ResourceLinks {
|
class ResourceLinks {
|
||||||
@@ -473,4 +472,28 @@ class ResourceLinks {
|
|||||||
return uiPluginCollectionLinkBuilder.method("plugins").parameters().method("getInstalledPlugins").parameters().href();
|
return uiPluginCollectionLinkBuilder.method("plugins").parameters().method("getInstalledPlugins").parameters().href();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AuthenticationLinks authentication() {
|
||||||
|
return new AuthenticationLinks(scmPathInfoStore.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AuthenticationLinks {
|
||||||
|
private final LinkBuilder loginLinkBuilder;
|
||||||
|
|
||||||
|
AuthenticationLinks(ScmPathInfo pathInfo) {
|
||||||
|
this.loginLinkBuilder = new LinkBuilder(pathInfo, AuthenticationResource.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
String formLogin() {
|
||||||
|
return loginLinkBuilder.method("authenticateViaForm").parameters().href();
|
||||||
|
}
|
||||||
|
|
||||||
|
String jsonLogin() {
|
||||||
|
return loginLinkBuilder.method("authenticateViaJSONBody").parameters().href();
|
||||||
|
}
|
||||||
|
|
||||||
|
String logout() {
|
||||||
|
return loginLinkBuilder.method("logout").parameters().href();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ import sonia.scm.plugin.Extension;
|
|||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.RepositoryDAO;
|
import sonia.scm.repository.RepositoryDAO;
|
||||||
import sonia.scm.user.User;
|
import sonia.scm.user.User;
|
||||||
|
import sonia.scm.user.UserPermissions;
|
||||||
import sonia.scm.util.Util;
|
import sonia.scm.util.Util;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -74,7 +75,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
|||||||
|
|
||||||
// TODO move to util class
|
// TODO move to util class
|
||||||
private static final String SEPARATOR = System.getProperty("line.separator", "\n");
|
private static final String SEPARATOR = System.getProperty("line.separator", "\n");
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
private static final String ADMIN_PERMISSION = "*";
|
private static final String ADMIN_PERMISSION = "*";
|
||||||
|
|
||||||
@@ -88,7 +89,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
|||||||
LoggerFactory.getLogger(DefaultAuthorizationCollector.class);
|
LoggerFactory.getLogger(DefaultAuthorizationCollector.class);
|
||||||
|
|
||||||
//~--- constructors ---------------------------------------------------------
|
//~--- constructors ---------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs ...
|
* Constructs ...
|
||||||
*
|
*
|
||||||
@@ -209,7 +210,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
|||||||
String perm = permission.getType().getPermissionPrefix().concat(repository.getId());
|
String perm = permission.getType().getPermissionPrefix().concat(repository.getId());
|
||||||
if (logger.isTraceEnabled())
|
if (logger.isTraceEnabled())
|
||||||
{
|
{
|
||||||
logger.trace("add repository permission {} for user {} at repository {}",
|
logger.trace("add repository permission {} for user {} at repository {}",
|
||||||
perm, user.getName(), repository.getName());
|
perm, user.getName(), repository.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,6 +255,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
|||||||
|
|
||||||
collectGlobalPermissions(builder, user, groups);
|
collectGlobalPermissions(builder, user, groups);
|
||||||
collectRepositoryPermissions(builder, user, groups);
|
collectRepositoryPermissions(builder, user, groups);
|
||||||
|
builder.add(canReadOwnUser(user));
|
||||||
permissions = builder.build();
|
permissions = builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,6 +264,10 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String canReadOwnUser(User user) {
|
||||||
|
return "user:" + UserPermissions.ACTION_READ + ":" + user.getName();
|
||||||
|
}
|
||||||
|
|
||||||
//~--- get methods ----------------------------------------------------------
|
//~--- get methods ----------------------------------------------------------
|
||||||
|
|
||||||
private boolean isUserPermitted(User user, GroupNames groups,
|
private boolean isUserPermitted(User user, GroupNames groups,
|
||||||
@@ -272,7 +278,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
|||||||
|| ((!perm.isGroupPermission()) && user.getName().equals(perm.getName()));
|
|| ((!perm.isGroupPermission()) && user.getName().equals(perm.getName()));
|
||||||
//J+
|
//J+
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void invalidateCache(AuthorizationChangedEvent event) {
|
public void invalidateCache(AuthorizationChangedEvent event) {
|
||||||
if (event.isEveryUserAffected()) {
|
if (event.isEveryUserAffected()) {
|
||||||
@@ -281,12 +287,12 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
|||||||
invalidateCache();
|
invalidateCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invalidateUserCache(final String username) {
|
private void invalidateUserCache(final String username) {
|
||||||
logger.info("invalidate cache for user {}, because of a received authorization event", username);
|
logger.info("invalidate cache for user {}, because of a received authorization event", username);
|
||||||
cache.removeAll((CacheKey item) -> username.equalsIgnoreCase(item.username));
|
cache.removeAll((CacheKey item) -> username.equalsIgnoreCase(item.username));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void invalidateCache() {
|
private void invalidateCache() {
|
||||||
logger.info("invalidate cache, because of a received authorization event");
|
logger.info("invalidate cache, because of a received authorization event");
|
||||||
cache.clear();
|
cache.clear();
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import com.github.sdorra.shiro.ShiroRule;
|
||||||
|
import com.github.sdorra.shiro.SubjectAware;
|
||||||
|
import org.assertj.core.api.Assertions;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini")
|
||||||
|
public class IndexResourceTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final ShiroRule shiroRule = new ShiroRule();
|
||||||
|
|
||||||
|
private final IndexDtoGenerator indexDtoGenerator = new IndexDtoGenerator(ResourceLinksMock.createMock(URI.create("/")));
|
||||||
|
private final IndexResource indexResource = new IndexResource(indexDtoGenerator);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldRenderLoginUrlsForUnauthenticatedRequest() {
|
||||||
|
IndexDto index = indexResource.getIndex();
|
||||||
|
|
||||||
|
Assertions.assertThat(index.getLinks().getLinkBy("formLogin")).matches(Optional::isPresent);
|
||||||
|
Assertions.assertThat(index.getLinks().getLinkBy("jsonLogin")).matches(Optional::isPresent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SubjectAware(username = "trillian", password = "secret")
|
||||||
|
public void shouldRenderMeUrlForAuthenticatedRequest() {
|
||||||
|
IndexDto index = indexResource.getIndex();
|
||||||
|
|
||||||
|
Assertions.assertThat(index.getLinks().getLinkBy("me")).matches(Optional::isPresent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SubjectAware(username = "trillian", password = "secret")
|
||||||
|
public void shouldRenderLogoutUrlForAuthenticatedRequest() {
|
||||||
|
IndexDto index = indexResource.getIndex();
|
||||||
|
|
||||||
|
Assertions.assertThat(index.getLinks().getLinkBy("logout")).matches(Optional::isPresent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ public class ResourceLinksMock {
|
|||||||
when(resourceLinks.repositoryTypeCollection()).thenReturn(new ResourceLinks.RepositoryTypeCollectionLinks(uriInfo));
|
when(resourceLinks.repositoryTypeCollection()).thenReturn(new ResourceLinks.RepositoryTypeCollectionLinks(uriInfo));
|
||||||
when(resourceLinks.uiPluginCollection()).thenReturn(new ResourceLinks.UIPluginCollectionLinks(uriInfo));
|
when(resourceLinks.uiPluginCollection()).thenReturn(new ResourceLinks.UIPluginCollectionLinks(uriInfo));
|
||||||
when(resourceLinks.uiPlugin()).thenReturn(new ResourceLinks.UIPluginLinks(uriInfo));
|
when(resourceLinks.uiPlugin()).thenReturn(new ResourceLinks.UIPluginLinks(uriInfo));
|
||||||
|
when(resourceLinks.authentication()).thenReturn(new ResourceLinks.AuthenticationLinks(uriInfo));
|
||||||
|
|
||||||
return resourceLinks;
|
return resourceLinks;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user