/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.ssh.server;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.apache.commons.fileupload.FileItem;
import org.eclipse.che.api.core.BadRequestException;
import org.eclipse.che.api.core.ConflictException;
import org.eclipse.che.api.core.NotFoundException;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.rest.Service;
import org.eclipse.che.api.core.rest.annotations.GenerateLink;
import org.eclipse.che.api.core.rest.shared.dto.Link;
import org.eclipse.che.api.core.util.LinksHelper;
import org.eclipse.che.api.ssh.server.model.impl.SshPairImpl;
import org.eclipse.che.api.ssh.shared.Constants;
import org.eclipse.che.api.ssh.shared.dto.GenerateSshPairRequest;
import org.eclipse.che.api.ssh.shared.dto.SshPairDto;
import org.eclipse.che.api.ssh.shared.model.SshPair;
import org.eclipse.che.commons.env.EnvironmentContext;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
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.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static org.eclipse.che.api.ssh.shared.Constants.LINK_REL_GET_PAIR;
import static org.eclipse.che.api.ssh.shared.Constants.LINK_REL_REMOVE_PAIR;
import static org.eclipse.che.dto.server.DtoFactory.newDto;
/**
* Defines Ssh Rest API.
*
* @author Sergii Leschenko
*/
@Api(value = "/ssh", description = "Ssh REST API")
@Path("/ssh")
public class SshService extends Service {
private final SshManager sshManager;
@Inject
public SshService(SshManager sshManager) {
this.sshManager = sshManager;
}
@POST
@Path("generate")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@GenerateLink(rel = Constants.LINK_REL_GENERATE_PAIR)
@ApiOperation(value = "Generate and stores ssh pair based on the request",
notes = "This operation can be performed only by authorized user," +
"this user will be the owner of the created ssh pair",
response = SshPairDto.class)
@ApiResponses({@ApiResponse(code = 201, message = "The ssh pair successfully generated"),
@ApiResponse(code = 400, message = "Missed required parameters, parameters are not valid"),
@ApiResponse(code = 409, message = "Conflict error occurred during the ssh pair generation" +
"(e.g. The Ssh pair with such name and service already exists)"),
@ApiResponse(code = 500, message = "Internal server error occurred")})
public Response generatePair(@ApiParam(value = "The configuration to generate the new ssh pair", required = true)
GenerateSshPairRequest request) throws BadRequestException, ServerException, ConflictException {
requiredNotNull(request, "Generate ssh pair request required");
requiredNotNull(request.getService(), "Service name required");
requiredNotNull(request.getName(), "Name required");
final SshPairImpl generatedPair = sshManager.generatePair(getCurrentUserId(), request.getService(), request.getName());
return Response.status(Response.Status.CREATED)
.entity(asDto(injectLinks(asDto(generatedPair))))
.build();
}
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_HTML)
@GenerateLink(rel = Constants.LINK_REL_CREATE_PAIR)
public Response createPair(Iterator<FileItem> formData) throws BadRequestException, ServerException, ConflictException {
String service = null;
String name = null;
String privateKey = null;
String publicKey = null;
while (formData.hasNext()) {
FileItem item = formData.next();
String fieldName = item.getFieldName();
switch (fieldName) {
case "service":
service = item.getString();
break;
case "name":
name = item.getString();
break;
case "privateKey":
privateKey = item.getString();
break;
case "publicKey":
publicKey = item.getString();
break;
default:
//do nothing
}
}
requiredNotNull(service, "Service name required");
requiredNotNull(name, "Name required");
if (privateKey == null && publicKey == null) {
throw new BadRequestException("Key content was not provided.");
}
sshManager.createPair(new SshPairImpl(getCurrentUserId(), service, name, publicKey, privateKey));
// We should send 200 response code and body with empty line
// through specific of html form that doesn't invoke complete submit handler
return Response.ok("", MediaType.TEXT_HTML).build();
}
@POST
@Consumes(APPLICATION_JSON)
@GenerateLink(rel = Constants.LINK_REL_CREATE_PAIR)
@ApiOperation(value = "Create a new ssh pair",
notes = "This operation can be performed only by authorized user," +
"this user will be the owner of the created ssh pair")
@ApiResponses({@ApiResponse(code = 204, message = "The ssh pair successfully created"),
@ApiResponse(code = 400, message = "Missed required parameters, parameters are not valid"),
@ApiResponse(code = 409, message = "Conflict error occurred during the ssh pair creation" +
"(e.g. The Ssh pair with such name and service already exists)"),
@ApiResponse(code = 500, message = "Internal server error occurred")})
public void createPair(@ApiParam(value = "The ssh pair to create", required = true)
SshPairDto sshPair) throws BadRequestException, ServerException, ConflictException {
requiredNotNull(sshPair, "Ssh pair required");
requiredNotNull(sshPair.getService(), "Service name required");
requiredNotNull(sshPair.getName(), "Name required");
if (sshPair.getPublicKey() == null && sshPair.getPrivateKey() == null) {
throw new BadRequestException("Key content was not provided.");
}
sshManager.createPair(new SshPairImpl(getCurrentUserId(), sshPair));
}
@GET
@Path("{service}/find")
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Get the ssh pair by the name of pair and name of service owned by the current user",
notes = "This operation can be performed only by authorized user.")
@ApiResponses({@ApiResponse(code = 200, message = "The ssh pair successfully fetched"),
@ApiResponse(code = 400, message = "Missed required parameters, parameters are not valid"),
@ApiResponse(code = 404, message = "The ssh pair with specified name and service does not exist for current user"),
@ApiResponse(code = 500, message = "Internal server error occurred")})
public SshPairDto getPair(@ApiParam("Name of service")
@PathParam("service")
String service,
@ApiParam(value = "Name of ssh pair", required = true)
@QueryParam("name")
String name) throws NotFoundException, ServerException, BadRequestException {
requiredNotNull(name, "Name of ssh pair");
return injectLinks(asDto(sshManager.getPair(getCurrentUserId(), service, name)));
}
@DELETE
@Path("{service}")
@ApiOperation(value = "Remove the ssh pair by the name of pair and name of service owned by the current user")
@ApiResponses({@ApiResponse(code = 204, message = "The ssh pair successfully removed"),
@ApiResponse(code = 400, message = "Missed required parameters, parameters are not valid"),
@ApiResponse(code = 404, message = "The ssh pair doesn't exist"),
@ApiResponse(code = 500, message = "Internal server error occurred")})
public void removePair(@ApiParam("Name of service")
@PathParam("service")
String service,
@ApiParam(value = "Name of ssh pair", required = true)
@QueryParam("name")
String name) throws ServerException, NotFoundException, BadRequestException {
requiredNotNull(name, "Name of ssh pair");
sshManager.removePair(getCurrentUserId(), service, name);
}
@GET
@Path("{service}")
@Produces(APPLICATION_JSON)
@ApiOperation(value = "Get the ssh pairs by name of service owned by the current user",
notes = "This operation can be performed only by authorized user.",
response = SshPairDto.class,
responseContainer = "List")
@ApiResponses({@ApiResponse(code = 200, message = "The ssh pairs successfully fetched"),
@ApiResponse(code = 500, message = "Internal server error occurred")})
public List<SshPairDto> getPairs(@ApiParam("Name of service")
@PathParam("service")
String service) throws ServerException {
return sshManager.getPairs(getCurrentUserId(), service)
.stream()
.map(sshPair -> injectLinks(asDto(sshPair)))
.collect(Collectors.toList());
}
private static String getCurrentUserId() {
return EnvironmentContext.getCurrent().getSubject().getUserId();
}
private static SshPairDto asDto(SshPair pair) {
return newDto(SshPairDto.class).withService(pair.getService())
.withName(pair.getName())
.withPublicKey(pair.getPublicKey())
.withPrivateKey(pair.getPrivateKey());
}
private SshPairDto injectLinks(SshPairDto sshPairDto) {
final UriBuilder uriBuilder = getServiceContext().getServiceUriBuilder();
final Link getPairsLink = LinksHelper.createLink("GET",
uriBuilder.clone()
.path(getClass(), "getPairs")
.build(sshPairDto.getService())
.toString(),
APPLICATION_JSON,
LINK_REL_GET_PAIR);
final Link removePairLink = LinksHelper.createLink("DELETE",
uriBuilder.clone()
.path(getClass(), "removePair")
.build(sshPairDto.getService(), sshPairDto.getName())
.toString(),
APPLICATION_JSON,
LINK_REL_REMOVE_PAIR);
final Link getPairLink = LinksHelper.createLink("GET",
uriBuilder.clone()
.path(getClass(), "getPair")
.build(sshPairDto.getService(), sshPairDto.getName())
.toString(),
APPLICATION_JSON,
LINK_REL_GET_PAIR);
return sshPairDto.withLinks(Arrays.asList(getPairsLink, removePairLink, getPairLink));
}
/**
* Checks object reference is not {@code null}
*
* @param object
* object reference to check
* @param subject
* used as subject of exception message "{subject} required"
* @throws BadRequestException
* when object reference is {@code null}
*/
private void requiredNotNull(Object object, String subject) throws BadRequestException {
if (object == null) {
throw new BadRequestException(subject + " required");
}
}
}