/**
* Copyright (c) Codice Foundation
* <p>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
**/
package org.codice.ddf.ui;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.codice.ddf.platform.util.XMLUtils;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.opengis.filter.Filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ddf.catalog.CatalogFramework;
import ddf.catalog.data.BinaryContent;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.Result;
import ddf.catalog.federation.FederationException;
import ddf.catalog.filter.FilterBuilder;
import ddf.catalog.operation.QueryRequest;
import ddf.catalog.operation.QueryResponse;
import ddf.catalog.operation.impl.QueryImpl;
import ddf.catalog.operation.impl.QueryRequestImpl;
import ddf.catalog.source.SourceUnavailableException;
import ddf.catalog.source.UnsupportedQueryException;
import ddf.catalog.transform.CatalogTransformerException;
import ddf.security.SecurityConstants;
import ddf.security.Subject;
import ddf.security.service.SecurityManager;
import ddf.security.service.SecurityServiceException;
import ddf.security.service.impl.cas.CasAuthenticationToken;
/**
* This a a very simple example of a servlet protected by CAS that can be used to query for
* metacards using metacard ids.
* <p>
* The query page that displays a metacard in xml format.
*/
public class Query extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LoggerFactory.getLogger(Query.class);
private static final String QUERY_FORM_SERVLET = "/ddf/query/QueryForm";
private static final String QUERY_REQUEST_PARAM = "query";
private static final String PROXY_TICKET_REQUEST_PARAM = "proxyticket";
private static final String STS_SERVICE_URL =
"https://localhost:8993/services/SecurityTokenService";
private transient CatalogFramework catalogFramework;
private transient SecurityManager securityManager;
private transient FilterBuilder filterBuilder;
public void setCatalogFramework(CatalogFramework catalogFramework) {
this.catalogFramework = catalogFramework;
}
public void setSecurityManager(SecurityManager securityManager) {
LOGGER.debug("Got a security manager");
this.securityManager = securityManager;
}
public void setFilterBuilder(FilterBuilder filterBuilder) {
this.filterBuilder = filterBuilder;
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
LOGGER.debug("serviceticket request parameter: {}",
request.getParameter(PROXY_TICKET_REQUEST_PARAM));
LOGGER.debug("query request parameter: {}", request.getParameter(QUERY_REQUEST_PARAM));
String html = createPage(request);
writer.println(html);
}
/**
* @param request The Http servlet request.
* @return Returns the html representation of the query page which includes the xml
* representation of the metacard.
*/
private String createPage(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
sb.append(
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">");
sb.append("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">");
sb.append("<head>");
sb.append("<title>");
sb.append("Query Result");
sb.append("</title>");
sb.append("</head>");
sb.append("<body>");
sb.append("<h1>");
sb.append("Query Request:");
sb.append("</h1>");
sb.append("<p>");
String metacardId = request.getParameter(QUERY_REQUEST_PARAM);
sb.append(metacardId);
sb.append("</p>");
sb.append("<h1>");
sb.append("Query Result:");
sb.append("</h1>");
sb.append("<p>");
sb.append("<textarea name=\"queryresult\" id=\"queryresult\" rows=\"40\" cols=\"80\">");
sb.append(getMetacardForId(metacardId, getProxyTicket(request)));
sb.append("</textarea>");
sb.append("</p>");
sb.append("<p>");
sb.append("<form method=\"post\" action=\"" + QUERY_FORM_SERVLET + "\">");
sb.append("<input type=\"submit\" value=\"New Query\" />");
sb.append("</form>");
sb.append("</p>");
sb.append("</body>");
sb.append("</html>");
StringBuilder message = new StringBuilder();
message.append(
"\n########################################################################\n");
message.append(" Query result html:\n");
message.append(sb.toString());
message.append(
"\n########################################################################\n");
LOGGER.debug(message.toString());
return sb.toString();
}
/**
* @param searchPhrase The search phrase used to query for the metacard.
* @param proxyTicket The CAS proxy ticket that will be used by the STS to get a SAML assertion.
* @return
*/
private String getMetacardForId(String searchPhrase, String proxyTicket) {
Filter filter = filterBuilder.attribute(Metacard.ANY_TEXT)
.is()
.like()
.text(searchPhrase);
LOGGER.info("Query filter: {}", filter.toString());
String queryError = "Unable to perform query " + filter.toString() + ".";
QueryRequest request = new QueryRequestImpl(new QueryImpl(filter), true);
StringBuilder responseString = new StringBuilder();
try {
Subject subject = securityManager.getSubject(new CasAuthenticationToken(proxyTicket));
LOGGER.info("Adding {} property with value {} to request",
SecurityConstants.SECURITY_SUBJECT,
subject);
request.getProperties()
.put(SecurityConstants.SECURITY_SUBJECT, subject);
} catch (SecurityServiceException se) {
LOGGER.error("Could not retrieve subject from securitymanager.", se);
return queryError;
}
try {
LOGGER.debug("About to query the catalog framework with query {}", filter.toString());
QueryResponse queryResponse = catalogFramework.query(request, null);
LOGGER.debug("Got query response from catalog framework for query {}",
filter.toString());
List<Result> results = queryResponse.getResults();
if (results != null) {
String message =
"The query for " + filter.toString() + " returned " + results.size()
+ " results.";
responseString.append(message);
LOGGER.debug(message);
for (Result curResult : results) {
Metacard metacard = curResult.getMetacard();
LOGGER.debug("Transforming the metacard with id [{}] to xml.",
metacard.getId());
BinaryContent content = catalogFramework.transform(metacard, "xml", null);
StringWriter writer = new StringWriter();
IOUtils.copy(content.getInputStream(), writer, "UTF8");
LOGGER.debug("Formatting xml for metacard with id [{}].", metacard.getId());
responseString.append(format(writer.toString()));
}
} else {
String message = "The query for " + filter.toString() + " returned a null result.";
responseString.append(message);
LOGGER.warn(message);
}
} catch (SourceUnavailableException e) {
LOGGER.error(queryError, e);
} catch (UnsupportedQueryException e) {
LOGGER.error(queryError, e);
} catch (FederationException e) {
LOGGER.error(queryError, e);
} catch (CatalogTransformerException e) {
LOGGER.error(queryError, e);
} catch (IOException e) {
LOGGER.error(queryError, e);
}
return responseString.toString();
}
/**
* Gets the CAS proxy ticket that will be used by the STS to get a SAML assertion.
*
* @param request The Http servlet request.
* @return Returns the CAS proxy ticket that will be used by the STS to get a SAML assertion.
*/
private String getProxyTicket(HttpServletRequest request) {
AttributePrincipal attributePrincipal = (AttributePrincipal) request.getUserPrincipal();
String proxyTicket = null;
if (attributePrincipal != null) {
// proxyTicket = attributePrincipal.getProxyTicketFor(
// "https://server:8993/ddf/query/sts" );
LOGGER.debug("Getting proxy ticket for {}", STS_SERVICE_URL);
proxyTicket = attributePrincipal.getProxyTicketFor(STS_SERVICE_URL);
LOGGER.info("proxy ticket: {}", proxyTicket);
} else {
LOGGER.error("attribute principal is null!");
}
return proxyTicket;
}
/**
* @param unformattedXml Unformatted xml.
* @return Returns formatted xml.
*/
private String format(String unformattedXml) {
Source xmlInput = new StreamSource(new StringReader(unformattedXml));
String formattedXml;
formattedXml = XMLUtils.prettyFormat(xmlInput);
LOGGER.debug("Formatted xml:\n{}", formattedXml);
if (StringUtils.isBlank(formattedXml)) {
// Did not format so return unformatted xml
formattedXml = unformattedXml;
}
return formattedXml;
}
}