001package org.nuxeo.elasticsearch.http.readonly; 002 003/* 004 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and contributors. 005 * 006 * All rights reserved. This program and the accompanying materials 007 * are made available under the terms of the GNU Lesser General Public License 008 * (LGPL) version 2.1 which accompanies this distribution, and is available at 009 * http://www.gnu.org/licenses/lgpl-2.1.html 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * Contributors: 017 * bdelbosc 018 */ 019 020import java.io.IOException; 021import java.security.Principal; 022 023import javax.validation.constraints.NotNull; 024import javax.ws.rs.Consumes; 025import javax.ws.rs.GET; 026import javax.ws.rs.POST; 027import javax.ws.rs.Path; 028import javax.ws.rs.PathParam; 029import javax.ws.rs.Produces; 030import javax.ws.rs.core.Context; 031import javax.ws.rs.core.MediaType; 032import javax.ws.rs.core.MultivaluedMap; 033import javax.ws.rs.core.UriInfo; 034 035import org.apache.commons.logging.Log; 036import org.apache.commons.logging.LogFactory; 037import org.json.JSONException; 038import org.nuxeo.ecm.core.api.NuxeoPrincipal; 039import org.nuxeo.ecm.webengine.model.WebObject; 040import org.nuxeo.ecm.webengine.model.impl.ModuleRoot; 041import org.nuxeo.elasticsearch.http.readonly.filter.RequestValidator; 042import org.nuxeo.elasticsearch.http.readonly.filter.SearchRequestFilter; 043import org.nuxeo.elasticsearch.http.readonly.service.RequestFilterService; 044import org.nuxeo.elasticsearch.http.readonly.filter.DefaultSearchRequestFilter; 045import org.nuxeo.runtime.api.Framework; 046 047/** 048 * Exposes a limited set of Read Only Elasticsearch REST API. 049 * 050 * @since 7.3 051 */ 052@Path("/es") 053@WebObject(type = "es") 054public class Main extends ModuleRoot { 055 private static final Log log = LogFactory.getLog(Main.class); 056 private static final String DEFAULT_ES_BASE_URL = "http://localhost:9200/"; 057 private static final java.lang.String ES_BASE_URL_PROPERTY = "elasticsearch.httpReadOnly.baseUrl"; 058 private String esBaseUrl; 059 060 public Main() { 061 super(); 062 if (getContext() == null) { 063 } 064 if (log.isDebugEnabled()) { 065 log.debug("New instance of ES module"); 066 } 067 } 068 069 @GET 070 @Path("_search") 071 @Consumes(MediaType.APPLICATION_FORM_URLENCODED) 072 @Produces(MediaType.APPLICATION_JSON) 073 public String searchWithPayload(@Context UriInfo uriInf, MultivaluedMap<String, String> formParams) 074 throws IOException, JSONException { 075 return doSearchWithPayload("_all", "_all", uriInf.getRequestUri().getRawQuery(), 076 formParams.keySet().iterator().next()); 077 } 078 079 @POST 080 @Path("_search") 081 @Consumes(MediaType.APPLICATION_JSON) 082 @Produces(MediaType.APPLICATION_JSON) 083 public String searchWithPost(@Context UriInfo uriInf, String payload) throws IOException, JSONException { 084 return doSearchWithPayload("_all", "_all", uriInf.getRequestUri().getRawQuery(), payload); 085 } 086 087 @GET 088 @Path("{indices}/_search") 089 @Consumes(MediaType.APPLICATION_FORM_URLENCODED) 090 @Produces(MediaType.APPLICATION_JSON) 091 public String searchWithPayload(@PathParam("indices") String indices, @Context UriInfo uriInf, 092 MultivaluedMap<String, String> formParams) throws IOException, JSONException { 093 return doSearchWithPayload(indices, "_all", uriInf.getRequestUri().getRawQuery(), 094 formParams.keySet().iterator().next()); 095 } 096 097 @POST 098 @Path("{indices}/_search") 099 @Consumes(MediaType.APPLICATION_JSON) 100 @Produces(MediaType.APPLICATION_JSON) 101 public String searchWithPost(@PathParam("indices") String indices, @Context UriInfo uriInf, String payload) 102 throws IOException, JSONException { 103 return doSearchWithPayload(indices, "_all", uriInf.getRequestUri().getRawQuery(), payload); 104 } 105 106 @GET 107 @Path("{indices}/{types}/_search") 108 @Consumes(MediaType.APPLICATION_FORM_URLENCODED) 109 @Produces(MediaType.APPLICATION_JSON) 110 public String searchWithPayload(@PathParam("indices") String indices, @PathParam("types") String types, 111 @Context UriInfo uriInf, MultivaluedMap<String, String> formParams) throws IOException, JSONException { 112 return doSearchWithPayload(indices, types, uriInf.getRequestUri().getRawQuery(), 113 formParams.keySet().iterator().next()); 114 } 115 116 @POST 117 @Path("{indices}/{types}/_search") 118 @Consumes(MediaType.APPLICATION_JSON) 119 @Produces(MediaType.APPLICATION_JSON) 120 public String searchWithPost(@PathParam("indices") String indices, @PathParam("types") String types, 121 @Context UriInfo uriInf, String payload) throws IOException, JSONException { 122 return doSearchWithPayload(indices, types, uriInf.getRequestUri().getRawQuery(), payload); 123 } 124 125 protected String doSearchWithPayload(String indices, String types, String rawQuery, String payload) 126 throws IOException, JSONException { 127 RequestFilterService requestFilterService = Framework.getService(RequestFilterService.class); 128 try { 129 SearchRequestFilter req = requestFilterService.getRequestFilters(indices); 130 if (req == null) { 131 req = new DefaultSearchRequestFilter(); 132 } 133 req.init(getContext().getCoreSession(), indices, types, rawQuery, payload); 134 log.debug(req); 135 return HttpClient.get(getElasticsearchBaseUrl() + req.getUrl(), req.getPayload()); 136 } catch (InstantiationException | IllegalAccessException e) { 137 log.error("Error when trying to get Search Request Filter for indice " + indices, e); 138 return null; 139 } 140 } 141 142 @GET 143 @Path("{indices}/{types}/_search") 144 @Produces(MediaType.APPLICATION_JSON) 145 public String searchWithUri(@PathParam("indices") String indices, @PathParam("types") String types, @Context UriInfo uriInf) 146 throws IOException, JSONException { 147 DefaultSearchRequestFilter req = new DefaultSearchRequestFilter(); 148 req.init(getContext().getCoreSession(), indices, types, 149 uriInf.getRequestUri().getRawQuery(), null); 150 log.debug(req); 151 return HttpClient.get(getElasticsearchBaseUrl() + req.getUrl(), req.getPayload()); 152 } 153 154 @GET 155 @Path("{indices}/{types}/{documentId: [a-zA-Z0-9\\-]+}") 156 @Produces(MediaType.APPLICATION_JSON) 157 public String getDocument(@PathParam("indices") String indices, @PathParam("types") String types, 158 @PathParam("documentId") String documentId, @Context UriInfo uriInf) throws IOException, JSONException { 159 NuxeoPrincipal principal = getPrincipal(); 160 RequestValidator validator = new RequestValidator(); 161 indices = validator.getIndices(indices); 162 types = validator.getTypes(indices, types); 163 validator.checkValidDocumentId(documentId); 164 DocRequestFilter req = new DocRequestFilter(principal, indices, types, documentId, 165 uriInf.getRequestUri().getRawQuery()); 166 log.debug(req); 167 if (!principal.isAdministrator()) { 168 String docAcl = HttpClient.get(getElasticsearchBaseUrl() + req.getCheckAccessUrl()); 169 validator.checkAccess(principal, docAcl); 170 } 171 return HttpClient.get(getElasticsearchBaseUrl() + req.getUrl()); 172 } 173 174 protected String getElasticsearchBaseUrl() { 175 if (esBaseUrl == null) { 176 esBaseUrl = Framework.getProperty(ES_BASE_URL_PROPERTY, DEFAULT_ES_BASE_URL); 177 } 178 return esBaseUrl; 179 } 180 181 public @NotNull NuxeoPrincipal getPrincipal() { 182 Principal principal = ctx.getPrincipal(); 183 if (principal == null) { 184 throw new IllegalArgumentException("No principal found"); 185 } 186 return (NuxeoPrincipal) principal; 187 } 188 189}