From 96c2114e5326985aed2ce5fa651ed1a20f7854c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Fri, 9 Nov 2018 16:06:31 +0100 Subject: [PATCH] Reduce SecurityFilter to user injection and enable SecurityInterceptor Remove all the unnecessary stuff and all endpoints that would be no longer secure. --- .../main/java/sonia/scm/filter/Filters.java | 31 -- .../api/rest/resources/GitConfigResource.java | 124 ------ .../api/rest/resources/HgConfigResource.java | 348 ----------------- .../api/rest/resources/SvnConfigResource.java | 125 ------ .../main/java/sonia/scm/ScmServletModule.java | 1 - .../rest/AuthenticationExceptionMapper.java | 13 + .../api/rest/ContextualExceptionMapper.java | 6 +- .../rest/NotAuthorizedExceptionMapper.java | 13 + .../rest/resources/ConfigurationResource.java | 148 ------- .../scm/api/rest/resources/GroupResource.java | 248 ------------ .../api/rest/resources/PluginResource.java | 362 ------------------ .../scm/api/rest/resources/UserResource.java | 319 --------------- .../v2/resources/AuthenticationResource.java | 1 + .../scm/api/v2/resources/IndexResource.java | 2 + .../api/v2/resources/UIPluginResource.java | 2 + .../sonia/scm/filter/AdminSecurityFilter.java | 90 ----- .../java/sonia/scm/filter/SecurityFilter.java | 37 +- .../scm/security/AllowAnonymousAccess.java | 11 + .../scm/security/SecurityInterceptor.java | 19 +- .../scm/filter/AdminSecurityFilterTest.java | 85 ---- .../sonia/scm/filter/SecurityFilterTest.java | 106 +---- 21 files changed, 86 insertions(+), 2005 deletions(-) delete mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/rest/resources/GitConfigResource.java delete mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java delete mode 100644 scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/rest/resources/SvnConfigResource.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/rest/AuthenticationExceptionMapper.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/rest/NotAuthorizedExceptionMapper.java delete mode 100644 scm-webapp/src/main/java/sonia/scm/api/rest/resources/ConfigurationResource.java delete mode 100644 scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java delete mode 100644 scm-webapp/src/main/java/sonia/scm/api/rest/resources/PluginResource.java delete mode 100644 scm-webapp/src/main/java/sonia/scm/api/rest/resources/UserResource.java delete mode 100644 scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java create mode 100644 scm-webapp/src/main/java/sonia/scm/security/AllowAnonymousAccess.java delete mode 100644 scm-webapp/src/test/java/sonia/scm/filter/AdminSecurityFilterTest.java diff --git a/scm-core/src/main/java/sonia/scm/filter/Filters.java b/scm-core/src/main/java/sonia/scm/filter/Filters.java index b1f5ea47cf..dbdc8f0e7e 100644 --- a/scm-core/src/main/java/sonia/scm/filter/Filters.java +++ b/scm-core/src/main/java/sonia/scm/filter/Filters.java @@ -45,28 +45,12 @@ public final class Filters /** Field description */ public static final String PATTERN_ALL = "/*"; - /** Field description */ - public static final String PATTERN_CONFIG = REST_API_PATH + "/config*"; - /** Field description */ public static final String PATTERN_DEBUG = "/debug.html"; - /** Field description */ - public static final String PATTERN_GROUPS = REST_API_PATH + "/groups*"; - - /** Field description */ - public static final String PATTERN_PLUGINS = REST_API_PATH + "/plugins*"; - - /** Field description */ - public static final String PATTERN_RESOURCE_REGEX = - "^/(?:resources|api|plugins|index)[\\./].*(?:html|\\.css|\\.js|\\.xml|\\.json|\\.txt)"; - /** Field description */ public static final String PATTERN_RESTAPI = REST_API_PATH + "/*"; - /** Field description */ - public static final String PATTERN_USERS = REST_API_PATH + "/users*"; - /** authentication priority */ public static final int PRIORITY_AUTHENTICATION = 5000; @@ -79,21 +63,6 @@ public final class Filters /** post authentication priority */ public static final int PRIORITY_POST_AUTHENTICATION = 5500; - /** pre authorization priority */ - public static final int PRIORITY_POST_AUTHORIZATION = 6500; - - /** post base url priority */ - public static final int PRIORITY_POST_BASEURL = 1500; - - /** pre authentication priority */ - public static final int PRIORITY_PRE_AUTHENTICATION = 4500; - - /** pre authorization priority */ - public static final int PRIORITY_PRE_AUTHORIZATION = 5500; - - /** pre base url priority */ - public static final int PRIORITY_PRE_BASEURL = 500; - //~--- constructors --------------------------------------------------------- /** diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/rest/resources/GitConfigResource.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/rest/resources/GitConfigResource.java deleted file mode 100644 index bace7d0b73..0000000000 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/rest/resources/GitConfigResource.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.Inject; -import com.google.inject.Singleton; - -import sonia.scm.repository.GitConfig; -import sonia.scm.repository.GitRepositoryHandler; - -//~--- JDK imports ------------------------------------------------------------ - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; - -/** - * - * @author Sebastian Sdorra - */ -@Singleton -@Path("config/repositories/git") -@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) -public class GitConfigResource -{ - - /** - * Constructs ... - * - * - * - * @param repositoryHandler - */ - @Inject - public GitConfigResource(GitRepositoryHandler repositoryHandler) - { - this.repositoryHandler = repositoryHandler; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - @GET - public GitConfig getConfig() - { - GitConfig config = repositoryHandler.getConfig(); - - if (config == null) - { - config = new GitConfig(); - repositoryHandler.setConfig(config); - } - - return config; - } - - //~--- set methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param uriInfo - * @param config - * - * @return - */ - @POST - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response setConfig(@Context UriInfo uriInfo, GitConfig config) - { - repositoryHandler.setConfig(config); - repositoryHandler.storeConfig(); - - return Response.created(uriInfo.getRequestUri()).build(); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private GitRepositoryHandler repositoryHandler; -} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java deleted file mode 100644 index 6fc4f4e01a..0000000000 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java +++ /dev/null @@ -1,348 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.Inject; -import com.google.inject.Singleton; - -import sonia.scm.SCMContext; -import sonia.scm.installer.HgInstallerFactory; -import sonia.scm.installer.HgPackage; -import sonia.scm.installer.HgPackageReader; -import sonia.scm.installer.HgPackages; -import sonia.scm.net.ahc.AdvancedHttpClient; -import sonia.scm.repository.HgConfig; -import sonia.scm.repository.HgRepositoryHandler; - -//~--- JDK imports ------------------------------------------------------------ - -import java.io.IOException; - -import java.util.List; - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; - -/** - * - * @author Sebastian Sdorra - */ -@Singleton -@Path("config/repositories/hg") -public class HgConfigResource -{ - - /** - * Constructs ... - * - * - * - * - * @param client - * @param handler - * @param pkgReader - */ - @Inject - public HgConfigResource(AdvancedHttpClient client, - HgRepositoryHandler handler, HgPackageReader pkgReader) - { - this.client = client; - this.handler = handler; - this.pkgReader = pkgReader; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param uriInfo - * - * @return - */ - @POST - @Path("auto-configuration") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public HgConfig autoConfiguration(@Context UriInfo uriInfo) - { - return autoConfiguration(uriInfo, null); - } - - /** - * Method description - * - * - * @param uriInfo - * @param config - * - * @return - */ - @POST - @Path("auto-configuration") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public HgConfig autoConfiguration(@Context UriInfo uriInfo, HgConfig config) - { - if (config == null) - { - config = new HgConfig(); - } - - handler.doAutoConfiguration(config); - - return handler.getConfig(); - } - - /** - * Method description - * - * - * - * @param id - * @return - */ - @POST - @Path("packages/{pkgId}") - public Response installPackage(@PathParam("pkgId") String id) - { - Response response = null; - HgPackage pkg = pkgReader.getPackage(id); - - if (pkg != null) - { - if (HgInstallerFactory.createInstaller().installPackage(client, handler, - SCMContext.getContext().getBaseDirectory(), pkg)) - { - response = Response.noContent().build(); - } - else - { - response = - Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); - } - } - else - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - - return response; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public HgConfig getConfig() - { - HgConfig config = handler.getConfig(); - - if (config == null) - { - config = new HgConfig(); - } - - return config; - } - - /** - * Method description - * - * - * @return - */ - @GET - @Path("installations/hg") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public InstallationsResponse getHgInstallations() - { - List installations = - HgInstallerFactory.createInstaller().getHgInstallations(); - - return new InstallationsResponse(installations); - } - - /** - * Method description - * - * - * @return - */ - @GET - @Path("packages") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public HgPackages getPackages() - { - return pkgReader.getPackages(); - } - - /** - * Method description - * - * - * @return - */ - @GET - @Path("installations/python") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public InstallationsResponse getPythonInstallations() - { - List installations = - HgInstallerFactory.createInstaller().getPythonInstallations(); - - return new InstallationsResponse(installations); - } - - //~--- set methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param uriInfo - * @param config - * - * @return - * - * @throws IOException - */ - @POST - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response setConfig(@Context UriInfo uriInfo, HgConfig config) - throws IOException - { - handler.setConfig(config); - handler.storeConfig(); - - return Response.created(uriInfo.getRequestUri()).build(); - } - - //~--- inner classes -------------------------------------------------------- - - /** - * Class description - * - * - * @version Enter version here..., 11/04/25 - * @author Enter your name here... - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlRootElement(name = "installations") - public static class InstallationsResponse - { - - /** - * Constructs ... - * - */ - public InstallationsResponse() {} - - /** - * Constructs ... - * - * - * @param paths - */ - public InstallationsResponse(List paths) - { - this.paths = paths; - } - - //~--- get methods -------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - public List getPaths() - { - return paths; - } - - //~--- set methods -------------------------------------------------------- - - /** - * Method description - * - * - * @param paths - */ - public void setPaths(List paths) - { - this.paths = paths; - } - - //~--- fields ------------------------------------------------------------- - - /** Field description */ - @XmlElement(name = "path") - private List paths; - } - - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private AdvancedHttpClient client; - - /** Field description */ - private HgRepositoryHandler handler; - - /** Field description */ - private HgPackageReader pkgReader; -} diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/rest/resources/SvnConfigResource.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/rest/resources/SvnConfigResource.java deleted file mode 100644 index a1266ab4a4..0000000000 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/rest/resources/SvnConfigResource.java +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.Inject; -import com.google.inject.Singleton; - -import sonia.scm.repository.RepositoryManager; -import sonia.scm.repository.SvnConfig; -import sonia.scm.repository.SvnRepositoryHandler; - -//~--- JDK imports ------------------------------------------------------------ - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; - -/** - * - * @author Sebastian Sdorra - */ -@Singleton -@Path("config/repositories/svn") -@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) -public class SvnConfigResource -{ - - /** - * Constructs ... - * - * - * @param repositoryManager - */ - @Inject - public SvnConfigResource(RepositoryManager repositoryManager) - { - repositoryHandler = (SvnRepositoryHandler) repositoryManager.getHandler( - SvnRepositoryHandler.TYPE_NAME); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - @GET - public SvnConfig getConfig() - { - SvnConfig config = repositoryHandler.getConfig(); - - if (config == null) - { - config = new SvnConfig(); - repositoryHandler.setConfig(config); - } - - return config; - } - - //~--- set methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param uriInfo - * @param config - * - * @return - */ - @POST - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response setConfig(@Context UriInfo uriInfo, SvnConfig config) - { - repositoryHandler.setConfig(config); - repositoryHandler.storeConfig(); - - return Response.created(uriInfo.getRequestUri()).build(); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private SvnRepositoryHandler repositoryHandler; -} diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index 4139cb60a5..0d324137ce 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -128,7 +128,6 @@ import javax.ws.rs.POST; import javax.ws.rs.PUT; import static com.google.inject.matcher.Matchers.annotatedWith; -import static com.google.inject.matcher.Matchers.any; import static com.google.inject.matcher.Matchers.not; import static com.google.inject.matcher.Matchers.subclassesOf; import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH; diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/AuthenticationExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/AuthenticationExceptionMapper.java new file mode 100644 index 0000000000..6ba9984190 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/AuthenticationExceptionMapper.java @@ -0,0 +1,13 @@ +package sonia.scm.api.rest; + +import org.apache.shiro.authc.AuthenticationException; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +@Provider +public class AuthenticationExceptionMapper extends StatusExceptionMapper { + public AuthenticationExceptionMapper() { + super(AuthenticationException.class, Response.Status.UNAUTHORIZED); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/ContextualExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/ContextualExceptionMapper.java index 32ce336f1a..9584a1ea2c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/ContextualExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/ContextualExceptionMapper.java @@ -26,7 +26,11 @@ public class ContextualExceptionMapper implement @Override public Response toResponse(E exception) { - logger.debug("map {} to status code {}", type.getSimpleName(), status.getStatusCode(), exception); + if (logger.isTraceEnabled()) { + logger.trace("map {} to status code {}", type.getSimpleName(), status.getStatusCode(), exception); + } else { + logger.debug("map {} to status code {} with message '{}'", type.getSimpleName(), status.getStatusCode(), exception.getMessage()); + } return Response.status(status) .entity(mapper.map(exception)) .type(VndMediaType.ERROR_TYPE) diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/NotAuthorizedExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/NotAuthorizedExceptionMapper.java new file mode 100644 index 0000000000..fee3803de7 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/NotAuthorizedExceptionMapper.java @@ -0,0 +1,13 @@ +package sonia.scm.api.rest; + +import javax.ws.rs.NotAuthorizedException; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +@Provider +public class NotAuthorizedExceptionMapper extends StatusExceptionMapper { + public NotAuthorizedExceptionMapper() + { + super(NotAuthorizedException.class, Response.Status.UNAUTHORIZED); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ConfigurationResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ConfigurationResource.java deleted file mode 100644 index a9beea7679..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ConfigurationResource.java +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.Inject; -import com.google.inject.Singleton; - -import org.apache.shiro.SecurityUtils; -import org.apache.shiro.subject.Subject; - -import sonia.scm.config.ScmConfiguration; -import sonia.scm.security.Role; -import sonia.scm.security.ScmSecurityException; -import sonia.scm.util.ScmConfigurationUtil; - -//~--- JDK imports ------------------------------------------------------------ - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; - -/** - * - * @author Sebastian Sdorra - */ -@Singleton -@Path("config") -public class ConfigurationResource -{ - - /** - * Constructs ... - * - * - * @param configuration - * @param securityContextProvider - */ - @Inject - public ConfigurationResource(ScmConfiguration configuration) - { - this.configuration = configuration; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getConfiguration() - { - Response response = null; - - if (SecurityUtils.getSubject().hasRole(Role.ADMIN)) - { - response = Response.ok(configuration).build(); - } - else - { - response = Response.status(Response.Status.FORBIDDEN).build(); - } - - return response; - } - - //~--- set methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param uriInfo - * @param newConfig - * - * @return - */ - @POST - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response setConfig(@Context UriInfo uriInfo, - ScmConfiguration newConfig) - { - - // TODO replace by checkRole - Subject subject = SecurityUtils.getSubject(); - - if (!subject.hasRole(Role.ADMIN)) - { - throw new ScmSecurityException("admin privileges required"); - } - - configuration.load(newConfig); - - synchronized (ScmConfiguration.class) - { - ScmConfigurationUtil.getInstance().store(configuration); - } - - return Response.created(uriInfo.getRequestUri()).build(); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - public ScmConfiguration configuration; -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java deleted file mode 100644 index a560cc278e..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java +++ /dev/null @@ -1,248 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.ResponseHeader; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; -import org.apache.shiro.SecurityUtils; -import sonia.scm.group.Group; -import sonia.scm.group.GroupManager; -import sonia.scm.security.Role; - -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.GenericEntity; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Request; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; -import java.util.Collection; - -//~--- JDK imports ------------------------------------------------------------ - -/** - * RESTful Web Service Resource to manage groups and their members. - * - * @author Sebastian Sdorra - */ -@Path("groups") -@Singleton -public class GroupResource extends AbstractManagerResource { - - /** Field description */ - public static final String PATH_PART = "groups"; - - //~--- constructors --------------------------------------------------------- - - @Inject - public GroupResource(GroupManager groupManager) - { - super(groupManager, Group.class); - } - - //~--- methods -------------------------------------------------------------- - - /** - * Creates a new group. Note: This method requires admin privileges. - * - * @param uriInfo current uri informations - * @param group the group to be created - * - * @return - */ - @POST - @StatusCodes({ - @ResponseCode(code = 201, condition = "create success", additionalHeaders = { - @ResponseHeader(name = "Location", description = "uri to the created group") - }), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response create(@Context UriInfo uriInfo, Group group) - { - return super.create(uriInfo, group); - } - - /** - * Deletes a group. Note: This method requires admin privileges. - * - * @param name the name of the group to delete. - * - * @return - */ - @DELETE - @Path("{id}") - @StatusCodes({ - @ResponseCode(code = 204, condition = "delete success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Override - public Response delete(@PathParam("id") String name) - { - return super.delete(name); - } - - /** - * Modifies the given group. Note: This method requires admin privileges. - * - * @param name name of the group to be modified - * @param group group object to modify - * - * @return - */ - @PUT - @Path("{id}") - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response update(@PathParam("id") String name, Group group) - { - return super.update(name, group); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Fetches a group by its name or id. Note: This method requires admin privileges. - * - * @param request the current request - * @param id the id/name of the group - * - * @return the {@link Group} with the specified id - */ - @GET - @Path("{id}") - @TypeHint(Group.class) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 404, condition = "not found, no group with the specified id/name available"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response get(@Context Request request, @PathParam("id") String id) - { - Response response = null; - - if (SecurityUtils.getSubject().hasRole(Role.ADMIN)) - { - response = super.get(request, id); - } - else - { - response = Response.status(Response.Status.FORBIDDEN).build(); - } - - return response; - } - - /** - * Returns all groups. Note: This method requires admin privileges. - * - * @param request the current request - * @param start the start value for paging - * @param limit the limit value for paging - * @param sortby sort parameter - * @param desc sort direction desc or aesc - * - * @return - */ - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @TypeHint(Group[].class) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Override - public Response getAll(@Context Request request, @DefaultValue("0") - @QueryParam("start") int start, @DefaultValue("-1") - @QueryParam("limit") int limit, @QueryParam("sortby") String sortby, - @DefaultValue("false") - @QueryParam("desc") boolean desc) - { - return super.getAll(request, start, limit, sortby, desc); - } - - //~--- methods -------------------------------------------------------------- - - @Override - protected GenericEntity> createGenericEntity( - Collection items) - { - return new GenericEntity>(items) {} - ; - } - - //~--- get methods ---------------------------------------------------------- - - @Override - protected String getId(Group group) - { - return group.getName(); - } - - @Override - protected String getPathPart() - { - return PATH_PART; - } -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/PluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/PluginResource.java deleted file mode 100644 index 4a770206eb..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/PluginResource.java +++ /dev/null @@ -1,362 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.common.collect.Lists; -import com.google.inject.Inject; -import com.google.inject.Singleton; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sonia.scm.api.rest.RestActionResult; -import sonia.scm.api.rest.RestActionUploadResult; -import sonia.scm.plugin.OverviewPluginPredicate; -import sonia.scm.plugin.PluginConditionFailedException; -import sonia.scm.plugin.PluginInformation; -import sonia.scm.plugin.PluginInformationComparator; -import sonia.scm.plugin.PluginManager; - -//~--- JDK imports ------------------------------------------------------------ - -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; - -import java.io.IOException; -import java.io.InputStream; - -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import javax.ws.rs.Consumes; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; - -/** - * RESTful Web Service Endpoint to manage plugins. - * - * @author Sebastian Sdorra - */ -@Singleton -@Path("plugins") -public class PluginResource -{ - - /** - * the logger for PluginResource - */ - private static final Logger logger = - LoggerFactory.getLogger(PluginResource.class); - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - * - * @param pluginManager - */ - @Inject - public PluginResource(PluginManager pluginManager) - { - this.pluginManager = pluginManager; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Installs a plugin from a package. - * - * @param uploadedInputStream - * - * @return - * - * @throws IOException - */ - @POST - @Path("install-package") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 412, condition = "precondition failed"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Consumes(MediaType.MULTIPART_FORM_DATA) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response install( - /*@FormParam("package")*/ InputStream uploadedInputStream) - throws IOException - { - Response response = null; - - try - { - pluginManager.installPackage(uploadedInputStream); - response = Response.ok(new RestActionUploadResult(true)).build(); - } - catch (PluginConditionFailedException ex) - { - logger.warn( - "could not install plugin package, because the condition failed", ex); - response = Response.status(Status.PRECONDITION_FAILED).entity( - new RestActionResult(false)).build(); - } - catch (Exception ex) - { - logger.warn("plugin installation failed", ex); - response = - Response.serverError().entity(new RestActionResult(false)).build(); - } - - return response; - } - - /** - * Installs a plugin. - * - * @param id id of the plugin to be installed - * - * @return - */ - @POST - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Path("install/{id}") - public Response install(@PathParam("id") String id) - { - pluginManager.install(id); - - // TODO should return 204 no content - return Response.ok().build(); - } - - /** - * Installs a plugin from a package. This method is a workaround for ExtJS - * file upload, which requires text/html as content-type. - * - * @param uploadedInputStream - * @return - * - * @throws IOException - */ - @POST - @Path("install-package.html") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 412, condition = "precondition failed"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Consumes(MediaType.MULTIPART_FORM_DATA) - @Produces(MediaType.TEXT_HTML) - public Response installFromUI( - /*@FormParam("package")*/ InputStream uploadedInputStream) - throws IOException - { - return install(uploadedInputStream); - } - - /** - * Uninstalls a plugin. - * - * @param id id of the plugin to be uninstalled - * - * @return - */ - @POST - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Path("uninstall/{id}") - public Response uninstall(@PathParam("id") String id) - { - pluginManager.uninstall(id); - - // TODO should return 204 content - // consider to do a uninstall with a delete - return Response.ok().build(); - } - - /** - * Updates a plugin. - * - * @param id id of the plugin to be updated - * - * @return - */ - @POST - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Path("update/{id}") - public Response update(@PathParam("id") String id) - { - pluginManager.update(id); - - // TODO should return 204 content - // consider to do an update with a put - - return Response.ok().build(); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Returns all plugins. - * - * @return all plugins - */ - @GET - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Collection getAll() - { - return pluginManager.getAll(); - } - - /** - * Returns all available plugins. - * - * @return all available plugins - */ - @GET - @Path("available") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Collection getAvailable() - { - return pluginManager.getAvailable(); - } - - /** - * Returns all plugins which are available for update. - * - * @return all plugins which are available for update - */ - @GET - @Path("updates") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Collection getAvailableUpdates() - { - return pluginManager.getAvailableUpdates(); - } - - /** - * Returns all installed plugins. - * - * @return all installed plugins - */ - @GET - @Path("installed") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) - public Collection getInstalled() - { - return pluginManager.getInstalled(); - } - - /** - * Returns all plugins for the overview. - * - * @return all plugins for the overview - */ - @GET - @Path("overview") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Collection getOverview() - { - //J- - List plugins = Lists.newArrayList( - pluginManager.get(OverviewPluginPredicate.INSTANCE) - ); - //J+ - - Collections.sort(plugins, PluginInformationComparator.INSTANCE); - - Iterator it = plugins.iterator(); - String last = null; - - while (it.hasNext()) - { - PluginInformation pi = it.next(); - String id = pi.getId(false); - - if ((last != null) && id.equals(last)) - { - it.remove(); - } - - last = id; - } - - return plugins; - } - - //~--- fields --------------------------------------------------------------- - - /** plugin manager */ - private final PluginManager pluginManager; -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/UserResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/UserResource.java deleted file mode 100644 index e054c4c32f..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/UserResource.java +++ /dev/null @@ -1,319 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.ResponseHeader; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; -import org.apache.shiro.SecurityUtils; -import org.apache.shiro.authc.credential.PasswordService; -import sonia.scm.security.Role; -import sonia.scm.user.User; -import sonia.scm.user.UserManager; -import sonia.scm.util.AssertUtil; -import sonia.scm.util.Util; - -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.GenericEntity; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Request; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; -import java.util.Collection; - -//~--- JDK imports ------------------------------------------------------------ - -/** - * RESTful Web Service Resource to manage users. - * - * @author Sebastian Sdorra - */ -@Singleton -@Path("users") -public class UserResource extends AbstractManagerResource -{ - - /** Field description */ - public static final String DUMMY_PASSWORT = "__dummypassword__"; - - /** Field description */ - public static final String PATH_PART = "users"; - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - * - * @param userManager - * @param passwordService - */ - @Inject - public UserResource(UserManager userManager, PasswordService passwordService) - { - super(userManager, User.class); - this.passwordService = passwordService; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Creates a new user. Note: This method requires admin privileges. - * - * @param uriInfo current uri informations - * @param user the user to be created - * - * @return - */ - @POST - @StatusCodes({ - @ResponseCode(code = 201, condition = "create success", additionalHeaders = { - @ResponseHeader(name = "Location", description = "uri to the created group") - }), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response create(@Context UriInfo uriInfo, User user) - { - return super.create(uriInfo, user); - } - - /** - * Deletes a user. Note: This method requires admin privileges. - * - * @param name the name of the user to delete. - * - * @return - */ - @DELETE - @Path("{id}") - @StatusCodes({ - @ResponseCode(code = 204, condition = "delete success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Override - public Response delete(@PathParam("id") String name) - { - return super.delete(name); - } - - /** - * Modifies the given user. Note: This method requires admin privileges. - * - * @param name name of the user to be modified - * @param user user object to modify - * - * @return - */ - @PUT - @Path("{id}") - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response update(@PathParam("id") String name, User user) - { - return super.update(name, user); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Returns a user. Note: This method requires admin privileges. - * - * @param request the current request - * @param id the id/name of the user - * - * @return the {@link User} with the specified id - */ - @GET - @Path("{id}") - @TypeHint(User.class) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 404, condition = "not found, no group with the specified id/name available"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response get(@Context Request request, @PathParam("id") String id) - { - Response response = null; - - if (SecurityUtils.getSubject().hasRole(Role.ADMIN)) - { - response = super.get(request, id); - } - else - { - response = Response.status(Response.Status.FORBIDDEN).build(); - } - - return response; - } - - /** - * Returns all users. Note: This method requires admin privileges. - * - * @param request the current request - * @param start the start value for paging - * @param limit the limit value for paging - * @param sortby sort parameter - * @param desc sort direction desc or aesc - * - * @return - */ - @GET - @TypeHint(User[].class) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response getAll(@Context Request request, @DefaultValue("0") - @QueryParam("start") int start, @DefaultValue("-1") - @QueryParam("limit") int limit, @QueryParam("sortby") String sortby, - @DefaultValue("false") - @QueryParam("desc") boolean desc) - { - return super.getAll(request, start, limit, sortby, desc); - } - - //~--- methods -------------------------------------------------------------- - - @Override - protected GenericEntity> createGenericEntity( - Collection items) - { - return new GenericEntity>(items) {} - ; - } - - @Override - protected void preCreate(User user) - { - encryptPassword(user); - } - - @Override - protected void preUpdate(User user) - { - if (DUMMY_PASSWORT.equals(user.getPassword())) - { - User o = manager.get(user.getName()); - - AssertUtil.assertIsNotNull(o); - user.setPassword(o.getPassword()); - } - else - { - encryptPassword(user); - } - } - - @Override - protected Collection prepareForReturn(Collection users) - { - if (Util.isNotEmpty(users)) - { - for (User u : users) - { - u.setPassword(DUMMY_PASSWORT); - } - } - - return users; - } - - @Override - protected User prepareForReturn(User user) - { - user.setPassword(DUMMY_PASSWORT); - - return user; - } - - @Override - protected String getId(User user) - { - return user.getName(); - } - - @Override - protected String getPathPart() - { - return PATH_PART; - } - - private void encryptPassword(User user) - { - String password = user.getPassword(); - - if (Util.isNotEmpty(password)) - { - user.setPassword(passwordService.encryptPassword(password)); - } - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private PasswordService passwordService; -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AuthenticationResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AuthenticationResource.java index ffe0ce51d0..47918080ab 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AuthenticationResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AuthenticationResource.java @@ -18,6 +18,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @Path(AuthenticationResource.PATH) +@AllowAnonymousAccess public class AuthenticationResource { private static final Logger LOG = LoggerFactory.getLogger(AuthenticationResource.class); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java index 088558c7dc..1eec99ea96 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java @@ -1,6 +1,7 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.TypeHint; +import sonia.scm.security.AllowAnonymousAccess; import sonia.scm.web.VndMediaType; import javax.inject.Inject; @@ -9,6 +10,7 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; @Path(IndexResource.INDEX_PATH_V2) +@AllowAnonymousAccess public class IndexResource { public static final String INDEX_PATH_V2 = "v2/"; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java index a76c39dd69..b83f5310e3 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java @@ -5,6 +5,7 @@ import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginWrapper; +import sonia.scm.security.AllowAnonymousAccess; import sonia.scm.web.VndMediaType; import javax.inject.Inject; @@ -17,6 +18,7 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +@AllowAnonymousAccess public class UIPluginResource { private final PluginLoader pluginLoader; diff --git a/scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java deleted file mode 100644 index a5455c6d21..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.filter; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.Inject; - -import org.apache.shiro.subject.Subject; - -import sonia.scm.Priority; -import sonia.scm.config.ScmConfiguration; -import sonia.scm.security.Role; - -/** - * Security filter which allow only administrators to access the underlying - * resources. - * - * @author Sebastian Sdorra - */ -// TODO before releasing v2, delete this filter (we use Permission objects now) -@WebElement( - value = Filters.PATTERN_CONFIG, - morePatterns = { - Filters.PATTERN_USERS, - Filters.PATTERN_GROUPS, - Filters.PATTERN_PLUGINS - } -) -@Priority(Filters.PRIORITY_AUTHORIZATION + 1) -public class AdminSecurityFilter extends SecurityFilter -{ - - /** - * Constructs a new instance. - * - * @param configuration scm-manager main configuration - */ - @Inject - public AdminSecurityFilter(ScmConfiguration configuration) - { - super(configuration); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Returns {@code true} if the subject has the admin role. - * - * @param subject subject - * - * @return {@code true} if the subject has the admin role - */ - @Override - protected boolean hasPermission(Subject subject) - { - return subject.hasRole(Role.ADMIN); - } -} diff --git a/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java index d97a5b050e..ea647a4b04 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java @@ -62,8 +62,7 @@ import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH; */ @Priority(Filters.PRIORITY_AUTHORIZATION) // TODO find a better way for unprotected resources -@WebElement(value = REST_API_PATH + "" + - "/(?!v2/ui).*", regex = true) +@WebElement(value = REST_API_PATH + "/(?!v2/ui).*", regex = true) public class SecurityFilter extends HttpFilter { @@ -84,31 +83,16 @@ public class SecurityFilter extends HttpFilter HttpServletResponse response, FilterChain chain) throws IOException, ServletException { - if (!SecurityRequests.isAuthenticationRequest(request) && !SecurityRequests.isIndexRequest(request)) + Subject subject = SecurityUtils.getSubject(); + if (hasPermission(subject)) { - Subject subject = SecurityUtils.getSubject(); - if (hasPermission(subject)) - { - // add primary principal as request attribute - // see https://goo.gl/JRjNmf - String username = getUsername(subject); - request.setAttribute(ATTRIBUTE_REMOTE_USER, username); + // add primary principal as request attribute + // see https://goo.gl/JRjNmf + String username = getUsername(subject); + request.setAttribute(ATTRIBUTE_REMOTE_USER, username); - // wrap servlet request to provide authentication informations - chain.doFilter(new SecurityHttpServletRequestWrapper(request, username), response); - } - else if (subject.isAuthenticated() || subject.isRemembered()) - { - response.sendError(HttpServletResponse.SC_FORBIDDEN); - } - else if (configuration.isAnonymousAccessEnabled()) - { - response.sendError(HttpServletResponse.SC_FORBIDDEN); - } - else - { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - } + // wrap servlet request to provide authentication information + chain.doFilter(new SecurityHttpServletRequestWrapper(request, username), response); } else { @@ -116,7 +100,7 @@ public class SecurityFilter extends HttpFilter } } - protected boolean hasPermission(Subject subject) + private boolean hasPermission(Subject subject) { return ((configuration != null) && configuration.isAnonymousAccessEnabled()) || subject.isAuthenticated() @@ -139,5 +123,4 @@ public class SecurityFilter extends HttpFilter return username; } - } diff --git a/scm-webapp/src/main/java/sonia/scm/security/AllowAnonymousAccess.java b/scm-webapp/src/main/java/sonia/scm/security/AllowAnonymousAccess.java new file mode 100644 index 0000000000..86d9bf92ad --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/security/AllowAnonymousAccess.java @@ -0,0 +1,11 @@ +package sonia.scm.security; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AllowAnonymousAccess { +} diff --git a/scm-webapp/src/main/java/sonia/scm/security/SecurityInterceptor.java b/scm-webapp/src/main/java/sonia/scm/security/SecurityInterceptor.java index 56e8ff5fc4..e055590bba 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/SecurityInterceptor.java +++ b/scm-webapp/src/main/java/sonia/scm/security/SecurityInterceptor.java @@ -2,11 +2,28 @@ package sonia.scm.security; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.subject.Subject; public class SecurityInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { - return methodInvocation.proceed(); + if (hasPermission() || anonymousAccessIsAllowed(methodInvocation)) { + return methodInvocation.proceed(); + } else { + throw new AuthenticationException(); + } + } + + private boolean anonymousAccessIsAllowed(MethodInvocation methodInvocation) { + return methodInvocation.getMethod().isAnnotationPresent(AllowAnonymousAccess.class) + || methodInvocation.getMethod().getDeclaringClass().isAnnotationPresent(AllowAnonymousAccess.class); + } + + private boolean hasPermission() { + Subject subject = SecurityUtils.getSubject(); + return subject.isAuthenticated() || subject.isRemembered(); } } diff --git a/scm-webapp/src/test/java/sonia/scm/filter/AdminSecurityFilterTest.java b/scm-webapp/src/test/java/sonia/scm/filter/AdminSecurityFilterTest.java deleted file mode 100644 index b1d7f54ec9..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/filter/AdminSecurityFilterTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (c) 2014, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - -package sonia.scm.filter; - -import com.github.sdorra.shiro.ShiroRule; -import com.github.sdorra.shiro.SubjectAware; -import org.apache.shiro.SecurityUtils; -import org.junit.Test; -import static org.junit.Assert.*; -import org.junit.Before; -import org.junit.Rule; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; -import sonia.scm.config.ScmConfiguration; - -/** - * Unit tests for {@link AdminSecurityFilter}. - * - * @author Sebastian Sdorra - */ -@RunWith(MockitoJUnitRunner.class) -@SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini") -public class AdminSecurityFilterTest { - - private AdminSecurityFilter securityFilter; - - @Rule - public ShiroRule shiro = new ShiroRule(); - - /** - * Prepare object under test and mocks. - */ - @Before - public void setUp(){ - this.securityFilter = new AdminSecurityFilter(new ScmConfiguration()); - } - - /** - * Tests {@link AdminSecurityFilter#hasPermission(org.apache.shiro.subject.Subject)} as administrator. - */ - @Test - @SubjectAware(username = "dent", password = "secret") - public void testHasPermissionAsAdministrator() { - assertTrue(securityFilter.hasPermission(SecurityUtils.getSubject())); - } - - /** - * Tests {@link AdminSecurityFilter#hasPermission(org.apache.shiro.subject.Subject)} as user. - */ - @Test - @SubjectAware(username = "trillian", password = "secret") - public void testHasPermissionAsUser() { - assertFalse(securityFilter.hasPermission(SecurityUtils.getSubject())); - } - -} diff --git a/scm-webapp/src/test/java/sonia/scm/filter/SecurityFilterTest.java b/scm-webapp/src/test/java/sonia/scm/filter/SecurityFilterTest.java index 50aa6980f6..6badc0b7b0 100644 --- a/scm-webapp/src/test/java/sonia/scm/filter/SecurityFilterTest.java +++ b/scm-webapp/src/test/java/sonia/scm/filter/SecurityFilterTest.java @@ -33,30 +33,30 @@ package sonia.scm.filter; import com.github.sdorra.shiro.ShiroRule; import com.github.sdorra.shiro.SubjectAware; -import java.io.IOException; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.apache.shiro.subject.SimplePrincipalCollection; import org.apache.shiro.subject.Subject; -import org.junit.Test; -import static org.junit.Assert.*; -import static org.hamcrest.Matchers.*; import org.junit.Before; import org.junit.Rule; +import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; -import org.mockito.Mockito; -import static org.mockito.Mockito.*; import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.SCMContext; import sonia.scm.config.ScmConfiguration; import sonia.scm.user.User; import sonia.scm.user.UserTestData; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.verify; + /** * Unit tests for {@link SecurityFilter}. * @@ -95,37 +95,6 @@ public class SecurityFilterTest { public void setUp(){ this.configuration = new ScmConfiguration(); this.securityFilter = new SecurityFilter(configuration); - - when(request.getContextPath()).thenReturn("/scm"); - } - - /** - * Tests filter on authentication endpoint v1. - * - * @throws IOException - * @throws ServletException - */ - @Test - public void testDoOnAuthenticationUrlV1() throws IOException, ServletException { - checkIfAuthenticationUrlIsPassedThrough("/scm/api/auth/access_token"); - } - - /** - * Tests filter on authentication endpoint v2. - * - * @throws IOException - * @throws ServletException - */ - @Test - public void testDoOnAuthenticationUrlV2() throws IOException, ServletException { - checkIfAuthenticationUrlIsPassedThrough("/scm/api/v2/auth/access_token"); - } - - private void checkIfAuthenticationUrlIsPassedThrough(String uri) throws IOException, ServletException { - when(request.getRequestURI()).thenReturn(uri); - securityFilter.doFilter(request, response, chain); - verify(request, never()).setAttribute(Mockito.anyString(), Mockito.any()); - verify(chain).doFilter(request, response); } /** @@ -136,7 +105,6 @@ public class SecurityFilterTest { */ @Test public void testAnonymous() throws IOException, ServletException { - when(request.getRequestURI()).thenReturn("/scm/api"); securityFilter.doFilter(request, response, chain); response.sendError(HttpServletResponse.SC_FORBIDDEN); } @@ -149,7 +117,6 @@ public class SecurityFilterTest { */ @Test public void testAnonymousWithAccessEnabled() throws IOException, ServletException { - when(request.getRequestURI()).thenReturn("/scm/api"); configuration.setAnonymousAccessEnabled(true); // execute @@ -173,8 +140,7 @@ public class SecurityFilterTest { @Test public void testAuthenticated() throws IOException, ServletException { authenticateUser(UserTestData.createTrillian()); - when(request.getRequestURI()).thenReturn("/scm/api"); - + // execute securityFilter.doFilter(request, response, chain); @@ -187,42 +153,6 @@ public class SecurityFilterTest { assertEquals("trillian", captured.getRemoteUser()); } - /** - * Tests filter without permissions. - * - * @throws IOException - * @throws ServletException - */ - @Test - public void testForbidden() throws IOException, ServletException { - authenticateUser(UserTestData.createTrillian()); - when(request.getRequestURI()).thenReturn("/scm/api"); - - // execute - securityFilter = new AccessForbiddenSecurityFilter(configuration); - securityFilter.doFilter(request, response, chain); - - // assert - verify(response).sendError(HttpServletResponse.SC_FORBIDDEN); - } - - /** - * Tests filter unauthenticated and without permissions. - * - * @throws IOException - * @throws ServletException - */ - @Test - public void testUnauthorized() throws IOException, ServletException { - when(request.getRequestURI()).thenReturn("/scm/api"); - - // execute - securityFilter = new AccessForbiddenSecurityFilter(configuration); - securityFilter.doFilter(request, response, chain); - - // assert - verify(response).sendError(HttpServletResponse.SC_UNAUTHORIZED); - } private void authenticateUser(User user) { SimplePrincipalCollection spc = new SimplePrincipalCollection(); @@ -236,18 +166,4 @@ public class SecurityFilterTest { shiro.setSubject(subject); } - - private static class AccessForbiddenSecurityFilter extends SecurityFilter { - - private AccessForbiddenSecurityFilter(ScmConfiguration configuration) { - super(configuration); - } - - @Override - protected boolean hasPermission(Subject subject) { - return false; - } - - } - }