/*
* JBoss, Home of Professional Open Source
* Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
*/
package org.searchisko.ftest.rest;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.junit.InSequence;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.searchisko.api.ContentObjectFields;
import org.searchisko.api.security.Role;
import org.searchisko.api.service.ConfigService;
import org.searchisko.ftest.DeploymentHelpers;
import org.searchisko.ftest.ProviderModel;
import org.searchisko.ftest.filter.ESProxyFilterTest;
import com.jayway.restassured.http.ContentType;
import static com.jayway.restassured.RestAssured.given;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isEmptyOrNullString;
/**
* Integration test for /search REST API.
*
* @author Libor Krzyzanek
* @author Vlastimil Elias (velias at redhat dot com)
* @see org.searchisko.api.rest.SearchRestService
*/
@RunWith(Arquillian.class)
public class SearchRestServiceTest {
public static final String SEARCH_REST_API = DeploymentHelpers.CURRENT_REST_VERSION + "search";
@Deployment(testable = false)
public static WebArchive createDeployment() throws IOException {
return DeploymentHelpers.createDeployment();
}
@ArquillianResource
URL context;
@Test
@InSequence(10)
public void assertSearchNoAnyProviderConfigured() throws MalformedURLException {
given().contentType(ContentType.JSON).expect().log().ifValidationFails().statusCode(500).when()
.get(new URL(context, SEARCH_REST_API).toExternalForm());
}
public static final String TYPE1 = "provider1_blog";
public static final String TYPE2 = "provider1_issue";
static ProviderModel provider1 = new ProviderModel("provider1", "password");
static final String contentId = "test-id";
static final String contentId2 = "test-id2";
static final String contentId3 = "test-id3";
static final String contentId4 = "test-id4";
@Test
@InSequence(30)
public void setupCreateProviderAndDocumentTypes() throws MalformedURLException {
String idx1 = provider1.addContentType(TYPE1, "blogpost", true);
String idx2 = provider1.addContentType(TYPE2, "issue", true, "", Role.PROVIDER);
ProviderRestServiceTest.createNewProvider(context, provider1);
ESProxyFilterTest.createSearchESIndex(context, idx1, "{}");
ESProxyFilterTest.createSearchESIndex(context, idx2, "{}");
ESProxyFilterTest.createSearchESIndexMapping(context, idx1, TYPE1, "{\"" + TYPE1 + "\":{}}");
ESProxyFilterTest.createSearchESIndexMapping(context, idx2, TYPE2, "{\"" + TYPE2 + "\":{}}");
}
@Test
@InSequence(31)
public void assertSearchAllNoResult() throws MalformedURLException {
given().contentType(ContentType.JSON).expect().log().ifValidationFails().statusCode(200)
.contentType(ContentType.JSON).body("hits.total", is(0)).when()
.get(new URL(context, SEARCH_REST_API).toExternalForm());
}
@Test
@InSequence(32)
public void assertNoStarAllowedForFiled() throws MalformedURLException {
// Bad Request must be returned
given().contentType(ContentType.JSON).expect().log().ifValidationFails().statusCode(400)
.contentType(ContentType.JSON).when().get(new URL(context, SEARCH_REST_API + "?field=*").toExternalForm());
}
@Test
@InSequence(39)
public void setupPushContent() throws MalformedURLException {
{
Map<String, Object> content = new HashMap<>();
content.put("data", "test");
content.put("data2", "test2");
content.put("data3", "test3");
content.put("data4", "test4");
ContentRestServiceTest.createOrUpdateContent(context, provider1, TYPE1, contentId, content);
}
{
Map<String, Object> content = new HashMap<>();
content.put("data", "test");
content.put("data2", "test2");
content.put("data3", "test3");
content.put("data4", "test4");
ContentRestServiceTest.createOrUpdateContent(context, provider1, TYPE1, contentId2, content);
}
{
Map<String, Object> content = new HashMap<>();
content.put("data", "test");
content.put("data2", "test2");
content.put("data3", "test3");
content.put("data4", "test4");
ContentRestServiceTest.createOrUpdateContent(context, provider1, TYPE2, contentId3, content);
}
{
Map<String, Object> content = new HashMap<>();
content.put("data", "test");
content.put("data2", "test2");
content.put("data3", "test3");
content.put("data4", "test4");
ContentRestServiceTest.createOrUpdateContent(context, provider1, TYPE2, contentId4, content);
}
}
@Test
@InSequence(40)
public void setupRefreshES() throws MalformedURLException {
DeploymentHelpers.refreshES();
}
protected static String uuid;
// /////////////////////////////// content type level security - #142 ////////////////////////////////
@Test
@InSequence(41)
public void assertDtlsSearchAllInsertedContent_noPermissionForAnonym() throws MalformedURLException {
// unauthenticated user has permission only to type 1 data
uuid = given().contentType(ContentType.JSON).expect().log().ifValidationFails().statusCode(200)
.contentType(ContentType.JSON).body("hits.total", is(2)).body("hits.hits[0]._type", is(TYPE1))
.body("hits.hits[1]._type", is(TYPE1)).when().get(new URL(context, SEARCH_REST_API).toExternalForm())
.andReturn().getBody().jsonPath().getString("uuid");
}
@Test
@InSequence(42)
public void assertDtlsSearchAllInsertedContent_providerHasPermission() throws MalformedURLException {
// authenticated provider has right to type 2 as it allows role "provider"
given().contentType(ContentType.JSON).auth().preemptive().basic(provider1.name, provider1.password).expect().log()
.ifValidationFails().statusCode(200).contentType(ContentType.JSON).body("hits.total", is(4)).when()
.get(new URL(context, SEARCH_REST_API).toExternalForm());
}
@Test
@InSequence(43)
public void setupDtlsChangeRoleInProvider1() throws MalformedURLException {
provider1.addContentType(TYPE2, "issue", true, "", "otherrole");
ProviderRestServiceTest.updateProvider(context, provider1);
}
@Test
@InSequence(44)
public void assertDtlsSearchAllInsertedContent_providerHasNoPermission() throws MalformedURLException {
// authenticated provider has no right to type 2 as it allows another role now
given().contentType(ContentType.JSON).auth().preemptive().basic(provider1.name, provider1.password).expect().log()
.ifValidationFails().statusCode(200).contentType(ContentType.JSON).body("hits.total", is(2))
.body("hits.hits[0]._type", is(TYPE1)).body("hits.hits[1]._type", is(TYPE1)).when()
.get(new URL(context, SEARCH_REST_API).toExternalForm());
}
@Test
@InSequence(45)
public void assertDtlsSearchAllInsertedContent_adminHasPermission() throws MalformedURLException {
// default provider has right to type 2 as he is admin
given().contentType(ContentType.JSON).auth().preemptive()
.basic(DeploymentHelpers.DEFAULT_PROVIDER_NAME, DeploymentHelpers.DEFAULT_PROVIDER_PASSWORD).expect().log()
.ifValidationFails().statusCode(200).contentType(ContentType.JSON).body("hits.total", is(4)).when()
.get(new URL(context, SEARCH_REST_API).toExternalForm());
}
// /////////////////////////////// field level security - #150 ////////////////////////////////
@Test
@InSequence(50)
public void setupFlsChangeRoleInProvider1() throws MalformedURLException {
provider1.addContentType(TYPE2, "issue", true, "");
ProviderRestServiceTest.updateProvider(context, provider1);
}
@Test
@InSequence(51)
public void setupFlsUploadConfigFields() throws MalformedURLException {
String data = "{\"field_visible_for_roles\" : {\n" + " \"data2\" : [\"provider2\",\"provider\"],\n"
+ " \"data3\" : [\"anotherrole\"] \n" + " }}";
ConfigRestServiceTest.uploadConfigFile(context, ConfigService.CFGNAME_SEARCH_RESPONSE_FIELDS, data);
}
@Test
@InSequence(52)
public void assertFlsSearchAllInsertedContent_anonymHasNoPermissionToField() throws MalformedURLException {
// anonymous can get 'data' but not 'data2' nor 'data3' field
given().expect().log().ifValidationFails().statusCode(200).contentType(ContentType.JSON)
.body("hits.hits[0].fields.data[0]", is("test")).body("hits.hits[0].fields.data2", isEmptyOrNullString())
.body("hits.hits[0].fields.data3", isEmptyOrNullString()).when()
.get(new URL(context, SEARCH_REST_API + "?field=data&field=data2&field=data3").toExternalForm());
// non authenticated user has no right to data2 and data3 field so we get 403 Unauthorized
given().expect().log().ifValidationFails().statusCode(403).when()
.get(new URL(context, SEARCH_REST_API + "?field=data2").toExternalForm());
given().expect().log().ifValidationFails().statusCode(403).when()
.get(new URL(context, SEARCH_REST_API + "?field=data3").toExternalForm());
}
@Test
@InSequence(53)
public void assertFlsSearchAllInsertedContent_providerHasNoPermissionToSomeField() throws MalformedURLException {
// authenticated provider can get 'data' and 'data2' but not 'data3' field
given().auth().preemptive().basic(provider1.name, provider1.password).expect().log().ifValidationFails()
.statusCode(200).contentType(ContentType.JSON).body("hits.hits[0].fields.data[0]", is("test"))
.body("hits.hits[0].fields.data2[0]", is("test2")).body("hits.hits[0].fields.data3", isEmptyOrNullString())
.when().get(new URL(context, SEARCH_REST_API + "?field=data&field=data2&field=data3").toExternalForm());
// authenticated provider has no right to data3 field so we get 403 Unauthorized
given().auth().preemptive().basic(provider1.name, provider1.password).expect().log().ifValidationFails()
.statusCode(403).when().get(new URL(context, SEARCH_REST_API + "?field=data3").toExternalForm());
}
@Test
@InSequence(55)
public void assertFlsSearchAllInsertedContent_adminHasPermissionToAllFields() throws MalformedURLException {
given().auth().preemptive()
.basic(DeploymentHelpers.DEFAULT_PROVIDER_NAME, DeploymentHelpers.DEFAULT_PROVIDER_PASSWORD).expect().log()
.ifValidationFails().statusCode(200).contentType(ContentType.JSON)
.body("hits.hits[0].fields.data[0]", is("test")).body("hits.hits[0].fields.data2[0]", is("test2"))
.body("hits.hits[0].fields.data3[0]", is("test3")).when()
.get(new URL(context, SEARCH_REST_API + "?field=data&field=data2&field=data3").toExternalForm());
}
// /////////////////////////////// Document level security - #143 ////////////////////////////////
@Test
@InSequence(60)
public void setupDlsTests() throws MalformedURLException {
ConfigRestServiceTest.removeConfigFile(context, ConfigService.CFGNAME_SEARCH_RESPONSE_FIELDS);
// this one will be visible only for admin in our case
{
Map<String, Object> content = new HashMap<>();
content.put(ContentObjectFields.SYS_VISIBLE_FOR_ROLES, "role1");
ContentRestServiceTest.createOrUpdateContent(context, provider1, TYPE1, contentId3, content);
}
// this one will be visible for admin and auth provider with role in our case
{
Map<String, Object> content = new HashMap<>();
content.put(ContentObjectFields.SYS_VISIBLE_FOR_ROLES, "provider");
ContentRestServiceTest.createOrUpdateContent(context, provider1, TYPE1, contentId4, content);
}
DeploymentHelpers.refreshES();
}
@Test
@InSequence(61)
public void assertDls_adminHasPermissionToAllDocuments() throws MalformedURLException {
given().auth().preemptive()
.basic(DeploymentHelpers.DEFAULT_PROVIDER_NAME, DeploymentHelpers.DEFAULT_PROVIDER_PASSWORD).expect().log()
.ifValidationFails().statusCode(200).contentType(ContentType.JSON).body("hits.total", is(6)).when()
.get(new URL(context, SEARCH_REST_API).toExternalForm());
}
@Test
@InSequence(62)
public void assertDls_anonym() throws MalformedURLException {
given().expect().log().ifValidationFails().statusCode(200).contentType(ContentType.JSON).body("hits.total", is(4))
.when().get(new URL(context, SEARCH_REST_API).toExternalForm());
}
@Test
@InSequence(63)
public void assertDls_userwithrole() throws MalformedURLException {
given().auth().preemptive().basic(provider1.name, provider1.password).expect().log().ifValidationFails()
.statusCode(200).contentType(ContentType.JSON).body("hits.total", is(5)).when()
.get(new URL(context, SEARCH_REST_API).toExternalForm());
}
// /////////////////////////////// _source field filtering security - #184 ////////////////////////////////
@Test
@InSequence(70)
public void setupSffsPrepareDocuments() throws MalformedURLException {
ContentRestServiceTest.deleteContent(context, provider1, TYPE1, contentId);
ContentRestServiceTest.deleteContent(context, provider1, TYPE1, contentId2);
ContentRestServiceTest.deleteContent(context, provider1, TYPE1, contentId3);
ContentRestServiceTest.deleteContent(context, provider1, TYPE1, contentId4);
ContentRestServiceTest.deleteContent(context, provider1, TYPE2, contentId3);
ContentRestServiceTest.deleteContent(context, provider1, TYPE2, contentId4);
Map<String, Object> content = new HashMap<>();
content.put("field1", "fv1");
content.put("field2", "fv2");
content.put("field4", "fv4");
Map<String, Object> field3map = new HashMap<>();
field3map.put("field1", "fv3_1");
field3map.put("field2", "fv3_2");
field3map.put("field4", "fv3_4");
content.put("field3", field3map);
ContentRestServiceTest.createOrUpdateContent(context, provider1, TYPE1, contentId, content);
DeploymentHelpers.refreshES();
}
@Test
@InSequence(71)
public void setupSffsUploadConfigFieldsEmpty() throws MalformedURLException {
String data = "{}";
ConfigRestServiceTest.uploadConfigFile(context, ConfigService.CFGNAME_SEARCH_RESPONSE_FIELDS, data);
}
@Test
@InSequence(72)
public void assertSffs_noRestrictions_Anonym_sourceUnfiltered() throws MalformedURLException {
// check source is returned untouched for anonymous user if no restriction exist
given().expect().log().ifValidationFails().statusCode(200).contentType(ContentType.JSON)
.body("hits.hits[0]._source.field1", is("fv1")).body("hits.hits[0]._source.field2", is("fv2"))
.body("hits.hits[0]._source.field4", is("fv4")).body("hits.hits[0]._source.field3.field1", is("fv3_1"))
.body("hits.hits[0]._source.field3.field2", is("fv3_2"))
.body("hits.hits[0]._source.field3.field4", is("fv3_4")).when()
.get(new URL(context, SEARCH_REST_API + "?field=_source").toExternalForm());
}
@Test
@InSequence(75)
public void setupSffsUploadConfigFieldsRestricted() throws MalformedURLException {
String data = "{\"source_filtering_for_roles\" : {" + "\"field1\" : [\"role_other\"],"
+ "\"*.field1\" : [\"role_other\"]," + "\"field2\" : [\"provider\"]," + "\"field3.*\" : [\"provider\"]" + "}}";
ConfigRestServiceTest.uploadConfigFile(context, ConfigService.CFGNAME_SEARCH_RESPONSE_FIELDS, data);
}
@Test
@InSequence(76)
public void assertSffs_restrictions_admin_sourceUnfiltered() throws MalformedURLException {
// check source is returned untouched for admin
given().auth().preemptive()
.basic(DeploymentHelpers.DEFAULT_PROVIDER_NAME, DeploymentHelpers.DEFAULT_PROVIDER_PASSWORD).expect().log()
.ifValidationFails().statusCode(200).contentType(ContentType.JSON)
.body("hits.hits[0]._source.field1", is("fv1")).body("hits.hits[0]._source.field2", is("fv2"))
.body("hits.hits[0]._source.field4", is("fv4")).body("hits.hits[0]._source.field3.field1", is("fv3_1"))
.body("hits.hits[0]._source.field3.field2", is("fv3_2"))
.body("hits.hits[0]._source.field3.field4", is("fv3_4")).when()
.get(new URL(context, SEARCH_REST_API + "?field=_source").toExternalForm());
}
@Test
@InSequence(77)
public void assertSffs_restrictions_Anonym_sourceFiltered() throws MalformedURLException {
// check source is returned filtered for anonymous user
given().expect().log().ifValidationFails().statusCode(200).contentType(ContentType.JSON)
.body("hits.hits[0]._source.field1", isEmptyOrNullString())
.body("hits.hits[0]._source.field2", isEmptyOrNullString()).body("hits.hits[0]._source.field4", is("fv4"))
.body("hits.hits[0]._source.field3.field1", isEmptyOrNullString())
.body("hits.hits[0]._source.field3.field2", isEmptyOrNullString())
.body("hits.hits[0]._source.field3.field4", isEmptyOrNullString()).when()
.get(new URL(context, SEARCH_REST_API + "?field=_source").toExternalForm());
}
@Test
@InSequence(78)
public void assertSffs_restrictions_role_sourceFiltered() throws MalformedURLException {
// check source is returned filtered for user with provider
given().auth().preemptive().basic(provider1.name, provider1.password).expect().log().ifValidationFails()
.statusCode(200).contentType(ContentType.JSON).body("hits.hits[0]._source.field1", isEmptyOrNullString())
.body("hits.hits[0]._source.field2", is("fv2")).body("hits.hits[0]._source.field4", is("fv4"))
.body("hits.hits[0]._source.field3.field1", isEmptyOrNullString())
.body("hits.hits[0]._source.field3.field2", is("fv3_2"))
.body("hits.hits[0]._source.field3.field4", is("fv3_4")).when()
.get(new URL(context, SEARCH_REST_API + "?field=_source").toExternalForm());
}
// /////////////////////////////// put search result use info ////////////////////////////////
@Test
@InSequence(100)
public void assertPutSearchInvalid() throws MalformedURLException {
given().contentType(ContentType.JSON).pathParam("search_result_uuid", "invalid-uuid")
.pathParam("hit_id", "invalid-hit-id").expect().statusCode(200).log().ifError()
.body(is("statistics record ignored")).when()
.put(new URL(context, SEARCH_REST_API + "/{search_result_uuid}/{hit_id}").toExternalForm());
}
@Test
@InSequence(101)
@Ignore("REST Search API behaves differently")
public void assertPutSearch() throws MalformedURLException {
given().contentType(ContentType.JSON).pathParam("search_result_uuid", uuid)
.pathParam("hit_id", TYPE1 + "-" + contentId).log().all().expect().log().all().statusCode(200)
.body(is("statistics record accepted")).when()
.put(new URL(context, SEARCH_REST_API + "/{search_result_uuid}/{hit_id}").toExternalForm());
}
}