Introduce index resource with first links

This commit is contained in:
René Pfeuffer
2018-09-26 17:00:13 +02:00
parent b645901704
commit 357ccc7ddb
7 changed files with 148 additions and 7 deletions

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}
} }

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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;
} }