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