package com.denimgroup.threadfix.webapp.controller;
import java.net.MalformedURLException;
import java.net.URL;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import com.denimgroup.threadfix.data.entities.Application;
import com.denimgroup.threadfix.data.entities.ApplicationCriticality;
import com.denimgroup.threadfix.data.entities.Organization;
import com.denimgroup.threadfix.data.entities.Scan;
import com.denimgroup.threadfix.data.entities.Waf;
import com.denimgroup.threadfix.service.APIKeyService;
import com.denimgroup.threadfix.service.ApplicationCriticalityService;
import com.denimgroup.threadfix.service.ApplicationService;
import com.denimgroup.threadfix.service.OrganizationService;
import com.denimgroup.threadfix.service.ScanMergeService;
import com.denimgroup.threadfix.service.ScanService;
import com.denimgroup.threadfix.service.ScanTypeCalculationService;
import com.denimgroup.threadfix.service.WafService;
import com.denimgroup.threadfix.service.channel.ScanImportStatus;
@Controller
@RequestMapping("/rest/teams/{teamId}/applications")
public class ApplicationRestController extends RestController {
public static final String CREATION_FAILED = "New Application creation failed.";
public static final String LOOKUP_FAILED = "Application lookup failed.";
public static final String ADD_CHANNEL_FAILED = "Adding an Application Channel failed.";
public static final String SET_WAF_FAILED = "Call to setWaf failed.";
public static final String CHANNEL_LOOKUP_FAILED = "Application Channel lookup failed.";
private OrganizationService organizationService;
private ApplicationService applicationService;
private ScanService scanService;
private ScanTypeCalculationService scanTypeCalculationService;
private ScanMergeService scanMergeService;
private WafService wafService;
private ApplicationCriticalityService applicationCriticalityService;
private final static String DETAIL = "applicationDetail",
LOOKUP = "applicationLookup",
NEW = "newApplication",
SET_WAF = "setWaf",
UPLOAD = "uploadScan";
// TODO finalize which methods need to be restricted
static {
restrictedMethods.add(NEW);
restrictedMethods.add(SET_WAF);
}
@Autowired
public ApplicationRestController(OrganizationService organizationService,
APIKeyService apiKeyService, ApplicationService applicationService,
ScanService scanService, ScanMergeService scanMergeService,
ScanTypeCalculationService scanTypeCalculationService,
WafService wafService,
ApplicationCriticalityService applicationCriticalityService) {
this.organizationService = organizationService;
this.apiKeyService = apiKeyService;
this.applicationService = applicationService;
this.scanTypeCalculationService = scanTypeCalculationService;
this.scanService = scanService;
this.scanMergeService = scanMergeService;
this.wafService = wafService;
this.applicationCriticalityService = applicationCriticalityService;
}
/**
* Create a new application with the supplied name and URL.
* The rest of the configuration is done through other methods.
* @param request
* @param teamId
* @return
*/
@RequestMapping(headers="Accept=application/json", value="/new", method=RequestMethod.POST)
public @ResponseBody Object newApplication(HttpServletRequest request,
@PathVariable("teamId") int teamId) {
log.info("Received REST request for a new Application.");
String result = checkKey(request, NEW);
if (!result.equals(API_KEY_SUCCESS)) {
return result;
}
// By not using @RequestParam notations, we can catch the error in the code
// and provide better error messages.
String name = request.getParameter("name");
String url = request.getParameter("url");
if (name == null || url == null) {
log.warn("Call to New Application was missing either the name or URL parameter.");
return CREATION_FAILED;
}
// test URL format
try {
new URL(url);
} catch (MalformedURLException e) {
log.warn("The supplied URL was not formatted correctly.");
return CREATION_FAILED;
}
Organization organization = organizationService.loadOrganization(teamId);
if (organization == null) {
log.warn("Invalid Team ID.");
return CREATION_FAILED;
}
Application application = new Application();
if (name != null && url != null) {
application.setOrganization(organization);
application.setName(name.trim());
application.setUrl(url.trim());
// TODO include this as a parameter
application.setApplicationCriticality(
applicationCriticalityService.loadApplicationCriticality(
ApplicationCriticality.LOW));
}
if (applicationService.checkApplication(application)) {
applicationService.storeApplication(application);
return application;
} else {
return CREATION_FAILED;
}
}
/**
* Return details about a specific application.
* @param request
* @param appId
* @return
*/
@RequestMapping(headers="Accept=application/json", value="/{appId}", method=RequestMethod.GET)
public @ResponseBody Object applicationDetail(HttpServletRequest request,
@PathVariable("appId") int appId) {
log.info("Received REST request for Applications with teamId = " + appId + ".");
String result = checkKey(request, DETAIL);
if (!result.equals(API_KEY_SUCCESS)) {
return result;
}
Application application = applicationService.loadApplication(appId);
if (application == null) {
log.warn(LOOKUP_FAILED);
return LOOKUP_FAILED;
}
return application;
}
/**
* Return details about a specific application.
* @param request
* @param appName
* @return
*/
@RequestMapping(headers="Accept=application/json", value="/lookup", method=RequestMethod.GET)
public @ResponseBody Object applicationLookup(HttpServletRequest request) {
String appName = request.getParameter("name");
String result = checkKey(request, LOOKUP);
if (!result.equals(API_KEY_SUCCESS)) {
return result;
}
if (appName == null) {
return LOOKUP_FAILED;
}
log.info("Received REST request for Applications with teamId = " + appName + ".");
Application application = applicationService.loadApplication(appName);
if (application == null) {
log.warn(LOOKUP_FAILED);
return LOOKUP_FAILED;
}
return application;
}
/**
* Allows the user to upload a scan to an existing application channel.
* @param appId
* @param request
* @param applicationId
* @param file
* @return Status response. We may change this to make it more useful.
*/
@RequestMapping(headers="Accept=application/json", value="/{appId}/upload", method=RequestMethod.POST)
public @ResponseBody Object uploadScan(@PathVariable("appId") int appId,
@PathVariable("teamId") int teamId, HttpServletRequest request,
@RequestParam("file") MultipartFile file) {
log.info("Received REST request to upload a scan to application " + appId + ".");
String result = checkKey(request, UPLOAD);
if (!result.equals(API_KEY_SUCCESS)) {
return result;
}
Integer myChannelId = scanTypeCalculationService.calculateScanType(appId, file, request.getParameter("channelId"));
String fileName = scanTypeCalculationService.saveFile(myChannelId, file);
ScanCheckResultBean returnValue = scanService.checkFile(myChannelId, fileName);
if (ScanImportStatus.SUCCESSFUL_SCAN == returnValue.getScanCheckResult()) {
Scan scan = scanMergeService.saveRemoteScanAndRun(myChannelId, fileName);
return scan;
} else if (ScanImportStatus.EMPTY_SCAN_ERROR == returnValue.getScanCheckResult()) {
return "You attempted to upload an empty scan.";
} else {
return "The scan upload attempt returned this message: " + returnValue.getScanCheckResult();
}
}
/**
* Overwrites the WAF for the application.
* @param request
* @param appId
* @param wafId
* @return
*/
@RequestMapping(headers="Accept=application/json", value="/{appId}/setWaf", method=RequestMethod.POST)
public @ResponseBody Object setWaf(HttpServletRequest request,
@PathVariable("appId") int appId) {
String idString = request.getParameter("wafId");
Integer wafId = null;
if (idString != null) {
try {
wafId = Integer.valueOf(idString);
} catch (NumberFormatException e) {
log.warn("Non-integer parameter was submitted to setWaf.");
}
if (wafId != null) {
log.info("Received REST request to add WAF " + wafId + " to Application " + appId + ".");
}
}
if (wafId == null) {
log.warn("Received incomplete REST request to add a WAF");
return SET_WAF_FAILED;
}
String result = checkKey(request, SET_WAF);
if (!result.equals(API_KEY_SUCCESS)) {
return result;
}
Application application = applicationService.loadApplication(appId);
Waf waf = wafService.loadWaf(wafId);
if (application == null) {
log.warn("Invalid Application ID.");
return SET_WAF_FAILED;
} else if (waf == null) {
log.warn("Invalid WAF ID");
return SET_WAF_FAILED;
} else {
// Delete WAF rules if the WAF has changed
Integer oldWafId = null;
if (application.getWaf() != null && application.getWaf().getId() != null) {
oldWafId = application.getWaf().getId();
}
application.setWaf(waf);
applicationService.updateWafRules(application, oldWafId);
applicationService.storeApplication(application);
return application;
}
}
}