/*
* (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* bdelbosc
*/
package org.nuxeo.elasticsearch.http.readonly;
import java.io.IOException;
import java.security.Principal;
import javax.validation.constraints.NotNull;
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.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.nuxeo.ecm.core.api.NuxeoPrincipal;
import org.nuxeo.ecm.webengine.model.WebObject;
import org.nuxeo.ecm.webengine.model.impl.ModuleRoot;
import org.nuxeo.elasticsearch.http.readonly.filter.RequestValidator;
import org.nuxeo.elasticsearch.http.readonly.filter.SearchRequestFilter;
import org.nuxeo.elasticsearch.http.readonly.service.RequestFilterService;
import org.nuxeo.elasticsearch.http.readonly.filter.DefaultSearchRequestFilter;
import org.nuxeo.runtime.api.Framework;
/**
* Exposes a limited set of Read Only Elasticsearch REST API.
*
* @since 7.3
*/
@Path("/es")
@WebObject(type = "es")
public class Main extends ModuleRoot {
private static final Log log = LogFactory.getLog(Main.class);
private static final String DEFAULT_ES_BASE_URL = "http://localhost:9200/";
private static final java.lang.String ES_BASE_URL_PROPERTY = "elasticsearch.httpReadOnly.baseUrl";
private String esBaseUrl;
public Main() {
super();
if (getContext() == null) {
}
if (log.isDebugEnabled()) {
log.debug("New instance of ES module");
}
}
@GET
@Path("_search")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
public String searchWithPayload(@Context UriInfo uriInf, MultivaluedMap<String, String> formParams)
throws IOException, JSONException {
return doSearchWithPayload("_all", "_all", uriInf.getRequestUri().getRawQuery(),
formParams.keySet().iterator().next());
}
@POST
@Path("_search")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public String searchWithPost(@Context UriInfo uriInf, String payload) throws IOException, JSONException {
return doSearchWithPayload("_all", "_all", uriInf.getRequestUri().getRawQuery(), payload);
}
@GET
@Path("{indices}/_search")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
public String searchWithPayload(@PathParam("indices") String indices, @Context UriInfo uriInf,
MultivaluedMap<String, String> formParams) throws IOException, JSONException {
return doSearchWithPayload(indices, "_all", uriInf.getRequestUri().getRawQuery(),
formParams.keySet().iterator().next());
}
@POST
@Path("{indices}/_search")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public String searchWithPost(@PathParam("indices") String indices, @Context UriInfo uriInf, String payload)
throws IOException, JSONException {
return doSearchWithPayload(indices, "_all", uriInf.getRequestUri().getRawQuery(), payload);
}
@GET
@Path("{indices}/{types}/_search")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
public String searchWithPayload(@PathParam("indices") String indices, @PathParam("types") String types,
@Context UriInfo uriInf, MultivaluedMap<String, String> formParams) throws IOException, JSONException {
return doSearchWithPayload(indices, types, uriInf.getRequestUri().getRawQuery(),
formParams.keySet().iterator().next());
}
@POST
@Path("{indices}/{types}/_search")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public String searchWithPost(@PathParam("indices") String indices, @PathParam("types") String types,
@Context UriInfo uriInf, String payload) throws IOException, JSONException {
return doSearchWithPayload(indices, types, uriInf.getRequestUri().getRawQuery(), payload);
}
protected String doSearchWithPayload(String indices, String types, String rawQuery, String payload)
throws IOException, JSONException {
RequestFilterService requestFilterService = Framework.getService(RequestFilterService.class);
try {
SearchRequestFilter req = requestFilterService.getRequestFilters(indices);
if (req == null) {
req = new DefaultSearchRequestFilter();
}
req.init(getContext().getCoreSession(), indices, types, rawQuery, payload);
log.debug(req);
return HttpClient.get(getElasticsearchBaseUrl() + req.getUrl(), req.getPayload());
} catch (InstantiationException | IllegalAccessException e) {
log.error("Error when trying to get Search Request Filter for indice " + indices, e);
return null;
}
}
@GET
@Path("{indices}/{types}/_search")
@Produces(MediaType.APPLICATION_JSON)
public String searchWithUri(@PathParam("indices") String indices, @PathParam("types") String types, @Context UriInfo uriInf)
throws IOException, JSONException {
DefaultSearchRequestFilter req = new DefaultSearchRequestFilter();
req.init(getContext().getCoreSession(), indices, types,
uriInf.getRequestUri().getRawQuery(), null);
log.debug(req);
return HttpClient.get(getElasticsearchBaseUrl() + req.getUrl(), req.getPayload());
}
@GET
@Path("{indices}/{types}/{documentId: [a-zA-Z0-9\\-]+}")
@Produces(MediaType.APPLICATION_JSON)
public String getDocument(@PathParam("indices") String indices, @PathParam("types") String types,
@PathParam("documentId") String documentId, @Context UriInfo uriInf) throws IOException, JSONException {
NuxeoPrincipal principal = getPrincipal();
RequestValidator validator = new RequestValidator();
indices = validator.getIndices(indices);
types = validator.getTypes(indices, types);
validator.checkValidDocumentId(documentId);
DocRequestFilter req = new DocRequestFilter(principal, indices, types, documentId,
uriInf.getRequestUri().getRawQuery());
log.debug(req);
if (!principal.isAdministrator()) {
String docAcl = HttpClient.get(getElasticsearchBaseUrl() + req.getCheckAccessUrl());
validator.checkAccess(principal, docAcl);
}
return HttpClient.get(getElasticsearchBaseUrl() + req.getUrl());
}
protected String getElasticsearchBaseUrl() {
if (esBaseUrl == null) {
esBaseUrl = Framework.getProperty(ES_BASE_URL_PROPERTY, DEFAULT_ES_BASE_URL);
}
return esBaseUrl;
}
public @NotNull NuxeoPrincipal getPrincipal() {
Principal principal = ctx.getPrincipal();
if (principal == null) {
throw new IllegalArgumentException("No principal found");
}
return (NuxeoPrincipal) principal;
}
}