mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-14 09:25:43 +01:00
update rest endpoint annotations
This commit is contained in:
@@ -1,22 +1,38 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
|
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
|
||||||
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
|
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
import io.swagger.v3.oas.annotations.security.SecurityScheme;
|
import io.swagger.v3.oas.annotations.security.SecurityScheme;
|
||||||
import io.swagger.v3.oas.annotations.security.SecuritySchemes;
|
import io.swagger.v3.oas.annotations.security.SecuritySchemes;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.apache.shiro.SecurityUtils;
|
import org.apache.shiro.SecurityUtils;
|
||||||
import org.apache.shiro.authc.AuthenticationException;
|
import org.apache.shiro.authc.AuthenticationException;
|
||||||
import org.apache.shiro.subject.Subject;
|
import org.apache.shiro.subject.Subject;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.security.*;
|
import sonia.scm.security.AccessToken;
|
||||||
|
import sonia.scm.security.AccessTokenBuilder;
|
||||||
|
import sonia.scm.security.AccessTokenBuilderFactory;
|
||||||
|
import sonia.scm.security.AccessTokenCookieIssuer;
|
||||||
|
import sonia.scm.security.AllowAnonymousAccess;
|
||||||
|
import sonia.scm.security.Scope;
|
||||||
|
import sonia.scm.security.Tokens;
|
||||||
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.ws.rs.*;
|
import javax.ws.rs.BeanParam;
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
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.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
@@ -39,6 +55,9 @@ import java.util.Optional;
|
|||||||
type = SecuritySchemeType.APIKEY
|
type = SecuritySchemeType.APIKEY
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@OpenAPIDefinition(tags = {
|
||||||
|
@Tag(name = "Authentication", description = "Authentication related endpoints")
|
||||||
|
})
|
||||||
@Path(AuthenticationResource.PATH)
|
@Path(AuthenticationResource.PATH)
|
||||||
@AllowAnonymousAccess
|
@AllowAnonymousAccess
|
||||||
public class AuthenticationResource {
|
public class AuthenticationResource {
|
||||||
@@ -54,8 +73,7 @@ public class AuthenticationResource {
|
|||||||
private LogoutRedirection logoutRedirection;
|
private LogoutRedirection logoutRedirection;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public AuthenticationResource(AccessTokenBuilderFactory tokenBuilderFactory, AccessTokenCookieIssuer cookieIssuer)
|
public AuthenticationResource(AccessTokenBuilderFactory tokenBuilderFactory, AccessTokenCookieIssuer cookieIssuer) {
|
||||||
{
|
|
||||||
this.tokenBuilderFactory = tokenBuilderFactory;
|
this.tokenBuilderFactory = tokenBuilderFactory;
|
||||||
this.cookieIssuer = cookieIssuer;
|
this.cookieIssuer = cookieIssuer;
|
||||||
}
|
}
|
||||||
@@ -63,12 +81,18 @@ public class AuthenticationResource {
|
|||||||
@POST
|
@POST
|
||||||
@Path("access_token")
|
@Path("access_token")
|
||||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||||
@StatusCodes({
|
@Operation(summary = "Login via Form", description = "Form-based authentication", tags = "Authentication")
|
||||||
@ResponseCode(code = 200, condition = "success"),
|
@ApiResponse(responseCode = "200", description = "success")
|
||||||
@ResponseCode(code = 400, condition = "bad request, required parameter is missing"),
|
@ApiResponse(responseCode = "400", description = "bad request, required parameter is missing")
|
||||||
@ResponseCode(code = 401, condition = "unauthorized, the specified username or password is wrong"),
|
@ApiResponse(responseCode = "401", description = "unauthorized, the specified username or password is wrong")
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
@ApiResponse(
|
||||||
})
|
responseCode = "500",
|
||||||
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
public Response authenticateViaForm(
|
public Response authenticateViaForm(
|
||||||
@Context HttpServletRequest request,
|
@Context HttpServletRequest request,
|
||||||
@Context HttpServletResponse response,
|
@Context HttpServletResponse response,
|
||||||
@@ -80,17 +104,23 @@ public class AuthenticationResource {
|
|||||||
@POST
|
@POST
|
||||||
@Path("access_token")
|
@Path("access_token")
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
@StatusCodes({
|
@Operation(summary = "Login via JSON", description = "JSON-based authentication", tags = "Authentication")
|
||||||
@ResponseCode(code = 200, condition = "success"),
|
@ApiResponse(responseCode = "200", description = "success")
|
||||||
@ResponseCode(code = 400, condition = "bad request, required parameter is missing"),
|
@ApiResponse(responseCode = "400", description = "bad request, required parameter is missing")
|
||||||
@ResponseCode(code = 401, condition = "unauthorized, the specified username or password is wrong"),
|
@ApiResponse(responseCode = "401", description = "unauthorized, the specified username or password is wrong")
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
@ApiResponse(
|
||||||
})
|
responseCode = "500",
|
||||||
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
public Response authenticateViaJSONBody(
|
public Response authenticateViaJSONBody(
|
||||||
@Context HttpServletRequest request,
|
@Context HttpServletRequest request,
|
||||||
@Context HttpServletResponse response,
|
@Context HttpServletResponse response,
|
||||||
AuthenticationRequestDto authentication
|
AuthenticationRequestDto authentication
|
||||||
) {
|
) {
|
||||||
return authenticate(request, response, authentication);
|
return authenticate(request, response, authentication);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,12 +136,11 @@ public class AuthenticationResource {
|
|||||||
Response res;
|
Response res;
|
||||||
Subject subject = SecurityUtils.getSubject();
|
Subject subject = SecurityUtils.getSubject();
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
subject.login(Tokens.createAuthenticationToken(request, authentication.getUsername(), authentication.getPassword()));
|
subject.login(Tokens.createAuthenticationToken(request, authentication.getUsername(), authentication.getPassword()));
|
||||||
|
|
||||||
AccessTokenBuilder tokenBuilder = tokenBuilderFactory.create();
|
AccessTokenBuilder tokenBuilder = tokenBuilderFactory.create();
|
||||||
if ( authentication.getScope() != null ) {
|
if (authentication.getScope() != null) {
|
||||||
tokenBuilder.scope(Scope.valueOf(authentication.getScope()));
|
tokenBuilder.scope(Scope.valueOf(authentication.getScope()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,17 +150,12 @@ public class AuthenticationResource {
|
|||||||
cookieIssuer.authenticate(request, response, token);
|
cookieIssuer.authenticate(request, response, token);
|
||||||
res = Response.noContent().build();
|
res = Response.noContent().build();
|
||||||
} else {
|
} else {
|
||||||
res = Response.ok( token.compact() ).build();
|
res = Response.ok(token.compact()).build();
|
||||||
}
|
}
|
||||||
}
|
} catch (AuthenticationException ex) {
|
||||||
catch (AuthenticationException ex)
|
if (LOG.isTraceEnabled()) {
|
||||||
{
|
|
||||||
if (LOG.isTraceEnabled())
|
|
||||||
{
|
|
||||||
LOG.trace("authentication failed for user ".concat(authentication.getUsername()), ex);
|
LOG.trace("authentication failed for user ".concat(authentication.getUsername()), ex);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG.warn("authentication failed for user {}", authentication.getUsername());
|
LOG.warn("authentication failed for user {}", authentication.getUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,12 +170,10 @@ public class AuthenticationResource {
|
|||||||
@DELETE
|
@DELETE
|
||||||
@Path("access_token")
|
@Path("access_token")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@StatusCodes({
|
@Operation(summary = "Logout", description = "Removes the access token.", tags = "Authentication")
|
||||||
@ResponseCode(code = 204, condition = "success"),
|
@ApiResponse(responseCode = "204", description = "success")
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
@ApiResponse(responseCode = "500", description = "internal server error")
|
||||||
})
|
public Response logout(@Context HttpServletRequest request, @Context HttpServletResponse response) {
|
||||||
public Response logout(@Context HttpServletRequest request, @Context HttpServletResponse response)
|
|
||||||
{
|
|
||||||
Subject subject = SecurityUtils.getSubject();
|
Subject subject = SecurityUtils.getSubject();
|
||||||
|
|
||||||
subject.logout();
|
subject.logout();
|
||||||
@@ -159,7 +181,6 @@ public class AuthenticationResource {
|
|||||||
// remove authentication cookie
|
// remove authentication cookie
|
||||||
cookieIssuer.invalidate(request, response);
|
cookieIssuer.invalidate(request, response);
|
||||||
|
|
||||||
// TODO anonymous access ??
|
|
||||||
if (logoutRedirection == null) {
|
if (logoutRedirection == null) {
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import sonia.scm.ReducedModelObject;
|
import sonia.scm.ReducedModelObject;
|
||||||
import sonia.scm.group.GroupDisplayManager;
|
import sonia.scm.group.GroupDisplayManager;
|
||||||
import sonia.scm.user.UserDisplayManager;
|
import sonia.scm.user.UserDisplayManager;
|
||||||
import sonia.scm.web.VndMediaType;
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.Size;
|
import javax.validation.constraints.Size;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
@@ -18,7 +22,9 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@OpenAPIDefinition(tags = {
|
||||||
|
@Tag(name = "Autocomplete", description = "Autocomplete related endpoints")
|
||||||
|
})
|
||||||
@Path(AutoCompleteResource.PATH)
|
@Path(AutoCompleteResource.PATH)
|
||||||
public class AutoCompleteResource {
|
public class AutoCompleteResource {
|
||||||
public static final String PATH = "v2/autocomplete/";
|
public static final String PATH = "v2/autocomplete/";
|
||||||
@@ -43,13 +49,26 @@ public class AutoCompleteResource {
|
|||||||
@GET
|
@GET
|
||||||
@Path("users")
|
@Path("users")
|
||||||
@Produces(VndMediaType.AUTOCOMPLETE)
|
@Produces(VndMediaType.AUTOCOMPLETE)
|
||||||
@StatusCodes({
|
@Operation(summary = "Search user", description = "Returns matching users.", tags = "Autocomplete")
|
||||||
@ResponseCode(code = 200, condition = "success"),
|
@ApiResponse(
|
||||||
@ResponseCode(code = 400, condition = "if the searched string contains less than 2 characters"),
|
responseCode = "200",
|
||||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
description = "success",
|
||||||
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"user:autocomplete\" privilege"),
|
content = @Content(
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
mediaType = VndMediaType.AUTOCOMPLETE,
|
||||||
})
|
schema = @Schema(implementation = ReducedObjectModelDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ApiResponse(responseCode = "400", description = "if the searched string contains less than 2 characters")
|
||||||
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"user:autocomplete\" privilege")
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "500",
|
||||||
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
public List<ReducedObjectModelDto> searchUser(@NotEmpty(message = PARAMETER_IS_REQUIRED) @Size(min = MIN_SEARCHED_CHARS, message = INVALID_PARAMETER_LENGTH) @QueryParam("q") String filter) {
|
public List<ReducedObjectModelDto> searchUser(@NotEmpty(message = PARAMETER_IS_REQUIRED) @Size(min = MIN_SEARCHED_CHARS, message = INVALID_PARAMETER_LENGTH) @QueryParam("q") String filter) {
|
||||||
return map(userDisplayManager.autocomplete(filter));
|
return map(userDisplayManager.autocomplete(filter));
|
||||||
}
|
}
|
||||||
@@ -57,13 +76,25 @@ public class AutoCompleteResource {
|
|||||||
@GET
|
@GET
|
||||||
@Path("groups")
|
@Path("groups")
|
||||||
@Produces(VndMediaType.AUTOCOMPLETE)
|
@Produces(VndMediaType.AUTOCOMPLETE)
|
||||||
@StatusCodes({
|
@Operation(summary = "Search groups", description = "Returns matching groups.", tags = "Autocomplete")
|
||||||
@ResponseCode(code = 200, condition = "success"),
|
@ApiResponse(
|
||||||
@ResponseCode(code = 400, condition = "if the searched string contains less than 2 characters"),
|
responseCode = "200",
|
||||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
description = "success",
|
||||||
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"group:autocomplete\" privilege"),
|
content = @Content(
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
mediaType = VndMediaType.AUTOCOMPLETE,
|
||||||
})
|
schema = @Schema(implementation = ReducedObjectModelDto.class)
|
||||||
|
))
|
||||||
|
@ApiResponse(responseCode = "400", description = "if the searched string contains less than 2 characters")
|
||||||
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"group:autocomplete\" privilege")
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "500",
|
||||||
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
public List<ReducedObjectModelDto> searchGroup(@NotEmpty(message = PARAMETER_IS_REQUIRED) @Size(min = MIN_SEARCHED_CHARS, message = INVALID_PARAMETER_LENGTH) @QueryParam("q") String filter) {
|
public List<ReducedObjectModelDto> searchGroup(@NotEmpty(message = PARAMETER_IS_REQUIRED) @Size(min = MIN_SEARCHED_CHARS, message = INVALID_PARAMETER_LENGTH) @QueryParam("q") String filter) {
|
||||||
return map(groupDisplayManager.autocomplete(filter));
|
return map(groupDisplayManager.autocomplete(filter));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
import de.otto.edison.hal.HalRepresentation;
|
||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import sonia.scm.plugin.AvailablePlugin;
|
import sonia.scm.plugin.AvailablePlugin;
|
||||||
import sonia.scm.plugin.InstalledPlugin;
|
import sonia.scm.plugin.InstalledPlugin;
|
||||||
import sonia.scm.plugin.PluginManager;
|
import sonia.scm.plugin.PluginManager;
|
||||||
@@ -24,6 +28,9 @@ import java.util.stream.Collectors;
|
|||||||
import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
||||||
import static sonia.scm.NotFoundException.notFound;
|
import static sonia.scm.NotFoundException.notFound;
|
||||||
|
|
||||||
|
@OpenAPIDefinition(tags = {
|
||||||
|
@Tag(name = "Available plugin", description = "Available plugin related endpoints")
|
||||||
|
})
|
||||||
public class AvailablePluginResource {
|
public class AvailablePluginResource {
|
||||||
|
|
||||||
private final PluginDtoCollectionMapper collectionMapper;
|
private final PluginDtoCollectionMapper collectionMapper;
|
||||||
@@ -44,11 +51,25 @@ public class AvailablePluginResource {
|
|||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Path("")
|
@Path("")
|
||||||
@StatusCodes({
|
@Operation(summary = "Find all available plugins", description = "Returns a collection of available plugins.", tags = "Available plugin")
|
||||||
@ResponseCode(code = 200, condition = "success"),
|
@ApiResponse(
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
responseCode = "200",
|
||||||
})
|
description = "success",
|
||||||
@TypeHint(CollectionDto.class)
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.PLUGIN_COLLECTION,
|
||||||
|
schema = @Schema(implementation = HalRepresentation.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"plugin:read\" privilege")
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "500",
|
||||||
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
@Produces(VndMediaType.PLUGIN_COLLECTION)
|
@Produces(VndMediaType.PLUGIN_COLLECTION)
|
||||||
public Response getAvailablePlugins() {
|
public Response getAvailablePlugins() {
|
||||||
PluginPermissions.read().check();
|
PluginPermissions.read().check();
|
||||||
@@ -68,12 +89,26 @@ public class AvailablePluginResource {
|
|||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Path("/{name}")
|
@Path("/{name}")
|
||||||
@StatusCodes({
|
@Operation(summary = "Find single available plugin", description = "Returns an available plugins.", tags = "Available plugin")
|
||||||
@ResponseCode(code = 200, condition = "success"),
|
@ApiResponse(
|
||||||
@ResponseCode(code = 404, condition = "not found"),
|
responseCode = "200",
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
description = "success",
|
||||||
})
|
content = @Content(
|
||||||
@TypeHint(PluginDto.class)
|
mediaType = VndMediaType.PLUGIN,
|
||||||
|
schema = @Schema(implementation = PluginDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"plugin:read\" privilege")
|
||||||
|
@ApiResponse(responseCode = "404", description = "not found")
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "500",
|
||||||
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
@Produces(VndMediaType.PLUGIN)
|
@Produces(VndMediaType.PLUGIN)
|
||||||
public Response getAvailablePlugin(@PathParam("name") String name) {
|
public Response getAvailablePlugin(@PathParam("name") String name) {
|
||||||
PluginPermissions.read().check();
|
PluginPermissions.read().check();
|
||||||
@@ -87,15 +122,24 @@ public class AvailablePluginResource {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers plugin installation.
|
* Triggers plugin installation.
|
||||||
|
*
|
||||||
* @param name plugin name
|
* @param name plugin name
|
||||||
* @return HTTP Status.
|
* @return HTTP Status.
|
||||||
*/
|
*/
|
||||||
@POST
|
@POST
|
||||||
@Path("/{name}/install")
|
@Path("/{name}/install")
|
||||||
@StatusCodes({
|
@Operation(summary = "Triggers plugin installation", description = "Put single plugin in installation queue. Plugin will be installed after restart.", tags = "Available plugin")
|
||||||
@ResponseCode(code = 200, condition = "success"),
|
@ApiResponse(responseCode = "200", description = "success")
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
})
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"plugin:manage\" privilege")
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "500",
|
||||||
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
public Response installPlugin(@PathParam("name") String name, @QueryParam("restart") boolean restartAfterInstallation) {
|
public Response installPlugin(@PathParam("name") String name, @QueryParam("restart") boolean restartAfterInstallation) {
|
||||||
PluginPermissions.manage().check();
|
PluginPermissions.manage().check();
|
||||||
pluginManager.install(name, restartAfterInstallation);
|
pluginManager.install(name, restartAfterInstallation);
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.media.Content;
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import sonia.scm.PageResult;
|
import sonia.scm.PageResult;
|
||||||
import sonia.scm.repository.Changeset;
|
import sonia.scm.repository.Changeset;
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import sonia.scm.config.ConfigurationPermissions;
|
import sonia.scm.config.ConfigurationPermissions;
|
||||||
import sonia.scm.config.ScmConfiguration;
|
import sonia.scm.config.ScmConfiguration;
|
||||||
import sonia.scm.repository.NamespaceStrategyValidator;
|
import sonia.scm.repository.NamespaceStrategyValidator;
|
||||||
@@ -21,6 +25,9 @@ import javax.ws.rs.core.Response;
|
|||||||
/**
|
/**
|
||||||
* RESTful Web Service Resource to manage the configuration.
|
* RESTful Web Service Resource to manage the configuration.
|
||||||
*/
|
*/
|
||||||
|
@OpenAPIDefinition(tags = {
|
||||||
|
@Tag(name = "Instance configuration", description = "Global SCM-Manager instance configuration")
|
||||||
|
})
|
||||||
@Path(ConfigResource.CONFIG_PATH_V2)
|
@Path(ConfigResource.CONFIG_PATH_V2)
|
||||||
public class ConfigResource {
|
public class ConfigResource {
|
||||||
|
|
||||||
@@ -46,13 +53,25 @@ public class ConfigResource {
|
|||||||
@GET
|
@GET
|
||||||
@Path("")
|
@Path("")
|
||||||
@Produces(VndMediaType.CONFIG)
|
@Produces(VndMediaType.CONFIG)
|
||||||
@TypeHint(UserDto.class)
|
@Operation(summary = "Instance configuration", description = "Returns the instance configuration.", tags = "Instance configuration")
|
||||||
@StatusCodes({
|
@ApiResponse(
|
||||||
@ResponseCode(code = 200, condition = "success"),
|
responseCode = "200",
|
||||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
description = "success",
|
||||||
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"configuration:read:global\" privilege"),
|
content = @Content(
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
mediaType = VndMediaType.CONFIG,
|
||||||
})
|
schema = @Schema(implementation = ConfigDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:read\" privilege")
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "500",
|
||||||
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
public Response get() {
|
public Response get() {
|
||||||
|
|
||||||
// We do this permission check in Resource and not in ScmConfiguration, because it must be available for reading
|
// We do this permission check in Resource and not in ScmConfiguration, because it must be available for reading
|
||||||
@@ -70,13 +89,18 @@ public class ConfigResource {
|
|||||||
@PUT
|
@PUT
|
||||||
@Path("")
|
@Path("")
|
||||||
@Consumes(VndMediaType.CONFIG)
|
@Consumes(VndMediaType.CONFIG)
|
||||||
@StatusCodes({
|
@Operation(summary = "Update instance configuration", description = "Modifies the instance configuration.", tags = "Instance configuration")
|
||||||
@ResponseCode(code = 204, condition = "update success"),
|
@ApiResponse(responseCode = "204", description = "update success")
|
||||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"configuration:write:global\" privilege"),
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:write\" privilege")
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
@ApiResponse(
|
||||||
})
|
responseCode = "500",
|
||||||
@TypeHint(TypeHint.NO_CONTENT.class)
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
public Response update(@Valid ConfigDto configDto) {
|
public Response update(@Valid ConfigDto configDto) {
|
||||||
|
|
||||||
// This *could* be moved to ScmConfiguration or ScmConfigurationUtil classes.
|
// This *could* be moved to ScmConfiguration or ScmConfigurationUtil classes.
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package sonia.scm.api.v2.resources;
|
|||||||
|
|
||||||
import com.github.sdorra.spotter.ContentType;
|
import com.github.sdorra.spotter.ContentType;
|
||||||
import com.github.sdorra.spotter.ContentTypes;
|
import com.github.sdorra.spotter.ContentTypes;
|
||||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.NotFoundException;
|
import sonia.scm.NotFoundException;
|
||||||
@@ -11,6 +13,7 @@ import sonia.scm.repository.NamespaceAndName;
|
|||||||
import sonia.scm.repository.api.RepositoryService;
|
import sonia.scm.repository.api.RepositoryService;
|
||||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||||
import sonia.scm.util.IOUtil;
|
import sonia.scm.util.IOUtil;
|
||||||
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
@@ -43,20 +46,30 @@ public class ContentResource {
|
|||||||
* recognized, this will be given in the header <code>Language</code>.
|
* recognized, this will be given in the header <code>Language</code>.
|
||||||
*
|
*
|
||||||
* @param namespace the namespace of the repository
|
* @param namespace the namespace of the repository
|
||||||
* @param name the name of the repository
|
* @param name the name of the repository
|
||||||
* @param revision the revision
|
* @param revision the revision
|
||||||
* @param path The path of the file
|
* @param path The path of the file
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Path("{revision}/{path: .*}")
|
@Path("{revision}/{path: .*}")
|
||||||
@StatusCodes({
|
@Operation(summary = "File content by revision", description = "Returns the content of a file for the given revision in the repository.", tags = "Repository")
|
||||||
@ResponseCode(code = 200, condition = "success"),
|
@ApiResponse(responseCode = "200", description = "success")
|
||||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
@ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the repository"),
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the repository")
|
||||||
@ResponseCode(code = 404, condition = "not found, no repository with the specified name available in the namespace"),
|
@ApiResponse(
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
responseCode = "404",
|
||||||
})
|
description = "not found, no repository with the specified name available in the namespace",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
))
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "500",
|
||||||
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
))
|
||||||
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) {
|
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) {
|
||||||
StreamingOutput stream = createStreamingOutput(namespace, name, revision, path);
|
StreamingOutput stream = createStreamingOutput(namespace, name, revision, path);
|
||||||
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||||
@@ -85,20 +98,34 @@ public class ContentResource {
|
|||||||
* the repository. The programming language will be given in the header <code>Language</code>.
|
* the repository. The programming language will be given in the header <code>Language</code>.
|
||||||
*
|
*
|
||||||
* @param namespace the namespace of the repository
|
* @param namespace the namespace of the repository
|
||||||
* @param name the name of the repository
|
* @param name the name of the repository
|
||||||
* @param revision the revision
|
* @param revision the revision
|
||||||
* @param path The path of the file
|
* @param path The path of the file
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@HEAD
|
@HEAD
|
||||||
@Path("{revision}/{path: .*}")
|
@Path("{revision}/{path: .*}")
|
||||||
@StatusCodes({
|
@Operation(
|
||||||
@ResponseCode(code = 200, condition = "success"),
|
summary = "File metadata by revision",
|
||||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
description = "Returns the content type and the programming language (if it can be detected) of a file for the given revision in the repository.",
|
||||||
@ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the repository"),
|
tags = "Repository"
|
||||||
@ResponseCode(code = 404, condition = "not found, no repository with the specified name available in the namespace"),
|
)
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
@ApiResponse(responseCode = "200", description = "success")
|
||||||
})
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the repository")
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "404",
|
||||||
|
description = "not found, no repository with the specified name available in the namespace",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
))
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "500",
|
||||||
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
))
|
||||||
public Response metadata(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) {
|
public Response metadata(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @PathParam("path") String path) {
|
||||||
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||||
Response.ResponseBuilder responseBuilder = Response.ok();
|
Response.ResponseBuilder responseBuilder = Response.ok();
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
import sonia.scm.NotFoundException;
|
import sonia.scm.NotFoundException;
|
||||||
import sonia.scm.repository.NamespaceAndName;
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
import sonia.scm.repository.api.DiffCommandBuilder;
|
import sonia.scm.repository.api.DiffCommandBuilder;
|
||||||
@@ -44,23 +46,29 @@ public class DiffRootResource {
|
|||||||
* Get the repository diff of a revision
|
* Get the repository diff of a revision
|
||||||
*
|
*
|
||||||
* @param namespace repository namespace
|
* @param namespace repository namespace
|
||||||
* @param name repository name
|
* @param name repository name
|
||||||
* @param revision the revision
|
* @param revision the revision
|
||||||
* @return the dif of the revision
|
* @return the dif of the revision
|
||||||
* @throws NotFoundException if the repository is not found
|
* @throws NotFoundException if the repository is not found
|
||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@Path("{revision}")
|
@Path("{revision}")
|
||||||
@Produces(VndMediaType.DIFF)
|
@Produces(VndMediaType.DIFF)
|
||||||
@StatusCodes({
|
@Operation(summary = "Diff by revision", description = "Get the repository diff of a revision.", tags = "Repository")
|
||||||
@ResponseCode(code = 200, condition = "success"),
|
@ApiResponse(responseCode = "200", description = "success")
|
||||||
@ResponseCode(code = 400, condition = "Bad Request"),
|
@ApiResponse(responseCode = "400", description = "Bad Request")
|
||||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
@ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the diff"),
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the diff")
|
||||||
@ResponseCode(code = 404, condition = "not found, no revision with the specified param for the repository available or repository not found"),
|
@ApiResponse(responseCode = "404", description = "not found, no revision with the specified param for the repository available or repository not found")
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
@ApiResponse(
|
||||||
})
|
responseCode = "500",
|
||||||
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision , @Pattern(regexp = DIFF_FORMAT_VALUES_REGEX) @DefaultValue("NATIVE") @QueryParam("format") String format ) throws IOException {
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision, @Pattern(regexp = DIFF_FORMAT_VALUES_REGEX) @DefaultValue("NATIVE") @QueryParam("format") String format) throws IOException {
|
||||||
HttpUtil.checkForCRLFInjection(revision);
|
HttpUtil.checkForCRLFInjection(revision);
|
||||||
DiffFormat diffFormat = DiffFormat.valueOf(format);
|
DiffFormat diffFormat = DiffFormat.valueOf(format);
|
||||||
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||||
@@ -77,14 +85,27 @@ public class DiffRootResource {
|
|||||||
@GET
|
@GET
|
||||||
@Path("{revision}/parsed")
|
@Path("{revision}/parsed")
|
||||||
@Produces(VndMediaType.DIFF_PARSED)
|
@Produces(VndMediaType.DIFF_PARSED)
|
||||||
@StatusCodes({
|
@Operation(summary = "Parsed diff by revision", description = "Get the parsed repository diff of a revision.", tags = "Repository")
|
||||||
@ResponseCode(code = 200, condition = "success"),
|
@ApiResponse(
|
||||||
@ResponseCode(code = 400, condition = "Bad Request"),
|
responseCode = "200",
|
||||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
description = "success",
|
||||||
@ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the diff"),
|
content = @Content(
|
||||||
@ResponseCode(code = 404, condition = "not found, no revision with the specified param for the repository available or repository not found"),
|
mediaType = VndMediaType.DIFF_PARSED,
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
schema = @Schema(implementation = DiffResultDto.class)
|
||||||
})
|
)
|
||||||
|
)
|
||||||
|
@ApiResponse(responseCode = "400", description = "Bad Request")
|
||||||
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the diff")
|
||||||
|
@ApiResponse(responseCode = "404", description = "not found, no revision with the specified param for the repository available or repository not found")
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "500",
|
||||||
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
public DiffResultDto getParsed(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws IOException {
|
public DiffResultDto getParsed(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws IOException {
|
||||||
HttpUtil.checkForCRLFInjection(revision);
|
HttpUtil.checkForCRLFInjection(revision);
|
||||||
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import sonia.scm.security.PermissionAssigner;
|
import sonia.scm.security.PermissionAssigner;
|
||||||
import sonia.scm.security.PermissionDescriptor;
|
import sonia.scm.security.PermissionDescriptor;
|
||||||
import sonia.scm.web.VndMediaType;
|
import sonia.scm.web.VndMediaType;
|
||||||
@@ -10,6 +16,9 @@ import javax.ws.rs.Path;
|
|||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
@OpenAPIDefinition(tags = {
|
||||||
|
@Tag(name = "Permissions", description = "Permission related endpoints")
|
||||||
|
})
|
||||||
@Path("v2/permissions")
|
@Path("v2/permissions")
|
||||||
public class GlobalPermissionResource {
|
public class GlobalPermissionResource {
|
||||||
|
|
||||||
@@ -22,6 +31,25 @@ public class GlobalPermissionResource {
|
|||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Produces(VndMediaType.PERMISSION_COLLECTION)
|
@Produces(VndMediaType.PERMISSION_COLLECTION)
|
||||||
|
@Operation(summary = "List of permissions", description = "Returns all available permissions", tags = "Permissions")
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "200",
|
||||||
|
description = "success",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.PERMISSION_COLLECTION,
|
||||||
|
schema = @Schema(implementation = PermissionListDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the permissions")
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "500",
|
||||||
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
@Path("")
|
@Path("")
|
||||||
public Response getAll() {
|
public Response getAll() {
|
||||||
String[] permissions = permissionAssigner.getAvailablePermissions().stream().map(PermissionDescriptor::getValue).toArray(String[]::new);
|
String[] permissions = permissionAssigner.getAvailablePermissions().stream().map(PermissionDescriptor::getValue).toArray(String[]::new);
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ import com.webcohesion.enunciate.metadata.rs.ResponseHeader;
|
|||||||
import com.webcohesion.enunciate.metadata.rs.ResponseHeaders;
|
import com.webcohesion.enunciate.metadata.rs.ResponseHeaders;
|
||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||||
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import sonia.scm.group.Group;
|
import sonia.scm.group.Group;
|
||||||
import sonia.scm.group.GroupManager;
|
import sonia.scm.group.GroupManager;
|
||||||
import sonia.scm.search.SearchRequest;
|
import sonia.scm.search.SearchRequest;
|
||||||
@@ -25,7 +31,9 @@ import java.util.function.Predicate;
|
|||||||
|
|
||||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||||
|
|
||||||
|
@OpenAPIDefinition(tags = {
|
||||||
|
@Tag(name = "Group", description = "Group related endpoints")
|
||||||
|
})
|
||||||
public class GroupCollectionResource {
|
public class GroupCollectionResource {
|
||||||
|
|
||||||
private static final int DEFAULT_PAGE_SIZE = 10;
|
private static final int DEFAULT_PAGE_SIZE = 10;
|
||||||
@@ -56,14 +64,26 @@ public class GroupCollectionResource {
|
|||||||
@GET
|
@GET
|
||||||
@Path("")
|
@Path("")
|
||||||
@Produces(VndMediaType.GROUP_COLLECTION)
|
@Produces(VndMediaType.GROUP_COLLECTION)
|
||||||
@TypeHint(CollectionDto.class)
|
@Operation(summary = "List of groups", description = "Returns all groups for a given page number", tags = "Group")
|
||||||
@StatusCodes({
|
@ApiResponse(
|
||||||
@ResponseCode(code = 200, condition = "success"),
|
responseCode = "200",
|
||||||
@ResponseCode(code = 400, condition = "\"sortBy\" field unknown"),
|
description = "success",
|
||||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
content = @Content(
|
||||||
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"group\" privilege"),
|
mediaType = VndMediaType.GROUP_COLLECTION,
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
schema = @Schema(implementation = CollectionDto.class)
|
||||||
})
|
)
|
||||||
|
)
|
||||||
|
@ApiResponse(responseCode = "400", description = "\"sortBy\" field unknown")
|
||||||
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"group\" privilege")
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "500",
|
||||||
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
public Response getAll(@DefaultValue("0") @QueryParam("page") int page,
|
public Response getAll(@DefaultValue("0") @QueryParam("page") int page,
|
||||||
@DefaultValue("" + DEFAULT_PAGE_SIZE) @QueryParam("pageSize") int pageSize,
|
@DefaultValue("" + DEFAULT_PAGE_SIZE) @QueryParam("pageSize") int pageSize,
|
||||||
@QueryParam("sortBy") String sortBy,
|
@QueryParam("sortBy") String sortBy,
|
||||||
@@ -83,14 +103,19 @@ public class GroupCollectionResource {
|
|||||||
@POST
|
@POST
|
||||||
@Path("")
|
@Path("")
|
||||||
@Consumes(VndMediaType.GROUP)
|
@Consumes(VndMediaType.GROUP)
|
||||||
@StatusCodes({
|
@Operation(summary = "Create group", description = "Creates a new group", tags = "Group")
|
||||||
@ResponseCode(code = 201, condition = "create success"),
|
@ApiResponse(responseCode = "201", description = "create success")
|
||||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"group\" privilege"),
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"group\" privilege")
|
||||||
@ResponseCode(code = 409, condition = "conflict, a group with this name already exists"),
|
@ApiResponse(responseCode = "409", description = "conflict, a group with this name already exists")
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
@ApiResponse(
|
||||||
})
|
responseCode = "500",
|
||||||
@TypeHint(TypeHint.NO_CONTENT.class)
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
@ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created group"))
|
@ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created group"))
|
||||||
public Response create(@Valid GroupDto group) {
|
public Response create(@Valid GroupDto group) {
|
||||||
return adapter.create(group,
|
return adapter.create(group,
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ package sonia.scm.api.v2.resources;
|
|||||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
import sonia.scm.security.PermissionAssigner;
|
import sonia.scm.security.PermissionAssigner;
|
||||||
import sonia.scm.security.PermissionDescriptor;
|
import sonia.scm.security.PermissionDescriptor;
|
||||||
import sonia.scm.security.PermissionPermissions;
|
import sonia.scm.security.PermissionPermissions;
|
||||||
@@ -39,14 +43,25 @@ public class GroupPermissionResource {
|
|||||||
@GET
|
@GET
|
||||||
@Path("")
|
@Path("")
|
||||||
@Produces(VndMediaType.PERMISSION_COLLECTION)
|
@Produces(VndMediaType.PERMISSION_COLLECTION)
|
||||||
@TypeHint(PermissionListDto.class)
|
@Operation(summary = "Group permission", description = "Returns permissions for a group.", tags = {"Group", "Permissions"})
|
||||||
@StatusCodes({
|
@ApiResponse(
|
||||||
@ResponseCode(code = 200, condition = "success"),
|
responseCode = "200",
|
||||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
description = "success",
|
||||||
@ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the group"),
|
content = @Content(
|
||||||
@ResponseCode(code = 404, condition = "not found, no group with the specified id/name available"),
|
mediaType = VndMediaType.PERMISSION_COLLECTION,
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
schema = @Schema(implementation = PermissionListDto.class)
|
||||||
})
|
))
|
||||||
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
|
@ApiResponse(responseCode = "403", description = "not authorized, the current user has no privileges to read the group")
|
||||||
|
@ApiResponse(responseCode = "404", description = "not found, no group with the specified id/name available")
|
||||||
|
@ApiResponse(
|
||||||
|
responseCode = "500",
|
||||||
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
public Response getPermissions(@PathParam("id") String id) {
|
public Response getPermissions(@PathParam("id") String id) {
|
||||||
PermissionPermissions.read().check();
|
PermissionPermissions.read().check();
|
||||||
Collection<PermissionDescriptor> permissions = permissionAssigner.readPermissionsForGroup(id);
|
Collection<PermissionDescriptor> permissions = permissionAssigner.readPermissionsForGroup(id);
|
||||||
@@ -62,15 +77,20 @@ public class GroupPermissionResource {
|
|||||||
@PUT
|
@PUT
|
||||||
@Path("")
|
@Path("")
|
||||||
@Consumes(VndMediaType.PERMISSION_COLLECTION)
|
@Consumes(VndMediaType.PERMISSION_COLLECTION)
|
||||||
@StatusCodes({
|
@Operation(summary = "Update Group permissions", description = "Sets permissions for a group. Overwrites all existing permissions.", tags = {"Group", "Permissions"})
|
||||||
@ResponseCode(code = 204, condition = "update success"),
|
@ApiResponse(responseCode = "204", description = "update success")
|
||||||
@ResponseCode(code = 400, condition = "Invalid body"),
|
@ApiResponse(responseCode = "400", description = "Invalid body")
|
||||||
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
@ResponseCode(code = 403, condition = "not authorized, the current group does not have the correct privilege"),
|
@ApiResponse(responseCode = "403", description = "not authorized, the current group does not have the correct privilege")
|
||||||
@ResponseCode(code = 404, condition = "not found, no group with the specified id/name available"),
|
@ApiResponse(responseCode = "404", description = "not found, no group with the specified id/name available")
|
||||||
@ResponseCode(code = 500, condition = "internal server error")
|
@ApiResponse(
|
||||||
})
|
responseCode = "500",
|
||||||
@TypeHint(TypeHint.NO_CONTENT.class)
|
description = "internal server error",
|
||||||
|
content = @Content(
|
||||||
|
mediaType = VndMediaType.ERROR_TYPE,
|
||||||
|
schema = @Schema(implementation = ErrorDto.class)
|
||||||
|
)
|
||||||
|
)
|
||||||
public Response overwritePermissions(@PathParam("id") String id, PermissionListDto newPermissions) {
|
public Response overwritePermissions(@PathParam("id") String id, PermissionListDto newPermissions) {
|
||||||
Collection<PermissionDescriptor> permissionDescriptors = Arrays.stream(newPermissions.getPermissions())
|
Collection<PermissionDescriptor> permissionDescriptors = Arrays.stream(newPermissions.getPermissions())
|
||||||
.map(PermissionDescriptor::new)
|
.map(PermissionDescriptor::new)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Hidden;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
|
||||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
|
||||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
|
||||||
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.media.Content;
|
import io.swagger.v3.oas.annotations.media.Content;
|
||||||
@@ -30,7 +27,7 @@ import javax.ws.rs.core.UriInfo;
|
|||||||
* RESTful Web Service Resource to get currently logged in users.
|
* RESTful Web Service Resource to get currently logged in users.
|
||||||
*/
|
*/
|
||||||
@OpenAPIDefinition(tags = {
|
@OpenAPIDefinition(tags = {
|
||||||
@Tag(name = "Me", description = "Me related endpoints")
|
@Tag(name = "Current user", description = "Current user related endpoints")
|
||||||
})
|
})
|
||||||
@Path(MeResource.ME_PATH_V2)
|
@Path(MeResource.ME_PATH_V2)
|
||||||
public class MeResource {
|
public class MeResource {
|
||||||
@@ -53,7 +50,7 @@ public class MeResource {
|
|||||||
@GET
|
@GET
|
||||||
@Path("")
|
@Path("")
|
||||||
@Produces(VndMediaType.ME)
|
@Produces(VndMediaType.ME)
|
||||||
@Operation(summary = "Current user", description = "Returns the currently logged in user or a 401 if user is not logged in.", tags = "Me")
|
@Operation(summary = "Current user", description = "Returns the currently logged in user or a 401 if user is not logged in.", tags = "Current user")
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
responseCode = "200",
|
responseCode = "200",
|
||||||
description = "success",
|
description = "success",
|
||||||
@@ -80,7 +77,7 @@ public class MeResource {
|
|||||||
@PUT
|
@PUT
|
||||||
@Path("password")
|
@Path("password")
|
||||||
@Consumes(VndMediaType.PASSWORD_CHANGE)
|
@Consumes(VndMediaType.PASSWORD_CHANGE)
|
||||||
@Operation(summary = "Change password", description = "Change password of the current user.", tags = "Me")
|
@Operation(summary = "Change password", description = "Change password of the current user.", tags = "Current user")
|
||||||
@ApiResponse(responseCode = "204", description = "update success")
|
@ApiResponse(responseCode = "204", description = "update success")
|
||||||
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
|
|||||||
Reference in New Issue
Block a user