mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-02-08 15:46:54 +01:00
Reduce SecurityFilter to user injection and enable SecurityInterceptor
Remove all the unnecessary stuff and all endpoints that would be no longer secure.
This commit is contained in:
@@ -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 ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<String> 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<String> 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<String> paths)
|
||||
{
|
||||
this.paths = paths;
|
||||
}
|
||||
|
||||
//~--- get methods --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<String> getPaths()
|
||||
{
|
||||
return paths;
|
||||
}
|
||||
|
||||
//~--- set methods --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param paths
|
||||
*/
|
||||
public void setPaths(List<String> paths)
|
||||
{
|
||||
this.paths = paths;
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
@XmlElement(name = "path")
|
||||
private List<String> paths;
|
||||
}
|
||||
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private AdvancedHttpClient client;
|
||||
|
||||
/** Field description */
|
||||
private HgRepositoryHandler handler;
|
||||
|
||||
/** Field description */
|
||||
private HgPackageReader pkgReader;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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<AuthenticationException> {
|
||||
public AuthenticationExceptionMapper() {
|
||||
super(AuthenticationException.class, Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,11 @@ public class ContextualExceptionMapper<E extends ExceptionWithContext> 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)
|
||||
|
||||
@@ -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<NotAuthorizedException> {
|
||||
public NotAuthorizedExceptionMapper()
|
||||
{
|
||||
super(NotAuthorizedException.class, Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<Group> {
|
||||
|
||||
/** Field description */
|
||||
public static final String PATH_PART = "groups";
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
@Inject
|
||||
public GroupResource(GroupManager groupManager)
|
||||
{
|
||||
super(groupManager, Group.class);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Creates a new group. <strong>Note:</strong> 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. <strong>Note:</strong> 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. <strong>Note:</strong> 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. <strong>Note:</strong> 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. <strong>Note:</strong> 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<Collection<Group>> createGenericEntity(
|
||||
Collection<Group> items)
|
||||
{
|
||||
return new GenericEntity<Collection<Group>>(items) {}
|
||||
;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
@Override
|
||||
protected String getId(Group group)
|
||||
{
|
||||
return group.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPathPart()
|
||||
{
|
||||
return PATH_PART;
|
||||
}
|
||||
}
|
||||
@@ -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<PluginInformation> 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<PluginInformation> 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<PluginInformation> 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<PluginInformation> 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<PluginInformation> getOverview()
|
||||
{
|
||||
//J-
|
||||
List<PluginInformation> plugins = Lists.newArrayList(
|
||||
pluginManager.get(OverviewPluginPredicate.INSTANCE)
|
||||
);
|
||||
//J+
|
||||
|
||||
Collections.sort(plugins, PluginInformationComparator.INSTANCE);
|
||||
|
||||
Iterator<PluginInformation> 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;
|
||||
}
|
||||
@@ -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<User>
|
||||
{
|
||||
|
||||
/** 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. <strong>Note:</strong> 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. <strong>Note:</strong> 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. <strong>Note:</strong> 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. <strong>Note:</strong> 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. <strong>Note:</strong> 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<Collection<User>> createGenericEntity(
|
||||
Collection<User> items)
|
||||
{
|
||||
return new GenericEntity<Collection<User>>(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<User> prepareForReturn(Collection<User> 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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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/";
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user