/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.service.security; import com.foundationdb.http.HttpConductor; import com.foundationdb.rest.RestService; import com.foundationdb.server.service.servicemanager.GuicedServiceManager; import com.foundationdb.server.test.it.ITBase; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import com.fasterxml.jackson.databind.JsonNode; import org.junit.After; import org.junit.Before; import org.junit.Test; import static com.foundationdb.util.JsonUtils.readTree; import static org.junit.Assert.*; import java.net.URI; import java.util.Arrays; import java.util.HashMap; import java.util.Map; public class RestSecurityIT extends SecurityServiceITBase { @Override protected GuicedServiceManager.BindingsConfigurationProvider serviceBindingsProvider() { return super.serviceBindingsProvider() .require(RestService.class); } @Override protected Map<String, String> startupConfigProperties() { Map<String, String> properties = super.startupConfigProperties(); properties.put("fdbsql.http.login", "basic"); // "digest" properties.put("fdbsql.http.csrf_protection.type", "none"); return properties; } private int openRestURL(String request, String query, String userInfo, boolean post) throws Exception { CloseableHttpClient client = HttpClientBuilder.create().build(); HttpRequestBase httpRequest; if (post) { httpRequest = new HttpPost(getRestURL(request, "", userInfo)); ((HttpPost)httpRequest).setEntity(new ByteArrayEntity(query.getBytes("UTF-8"))); } else { httpRequest = new HttpGet(getRestURL(request, query, userInfo)); } HttpResponse response = client.execute(httpRequest); int code = response.getStatusLine().getStatusCode(); EntityUtils.consume(response.getEntity()); client.close(); return code; } private URI getRestURL(String request, String query, String userInfo) throws Exception { int port = serviceManager().getServiceByClass(HttpConductor.class).getPort(); String context = serviceManager().getServiceByClass(RestService.class).getContextPath(); return new URI("http", userInfo, "localhost", port, context + request, query, null); } @Test public void restUnauthenticated() throws Exception { assertEquals(HttpStatus.SC_UNAUTHORIZED, openRestURL("/user1.utable/1", null, null, false)); } @Test public void restAuthenticated() throws Exception { assertEquals(HttpStatus.SC_OK, openRestURL("/entity/user1.utable/1", null, "user1:password", false)); } @Test public void restAuthenticateBadUser() throws Exception { assertEquals(HttpStatus.SC_UNAUTHORIZED, openRestURL("/user1.utable/1", null, "user2:none", false)); } @Test public void restAuthenticateBadPassword() throws Exception { assertEquals(HttpStatus.SC_UNAUTHORIZED, openRestURL("/user1.utable/1", null, "user1:wrong", false)); } @Test public void restAuthenticateWrongSchema() throws Exception { assertEquals(HttpStatus.SC_FORBIDDEN, openRestURL("/entity/user2.utable/1", null, "user1:password", false)); } @Test public void restQueryAuthenticated() throws Exception { assertEquals(HttpStatus.SC_OK, openRestURL("/sql/query", "{\"q\": \"SELECT * FROM utable\"}", "user1:password", true)); } @Test public void restQueryWrongSchema() throws Exception { assertEquals(HttpStatus.SC_NOT_FOUND, openRestURL("/sql/query", "{\"q\": \"SELECT * FROM user2.utable\"}", "user1:password", true)); } static final String ADD_USER = "{\"user\":\"user3\", \"password\":\"pass\", \"roles\": [\"rest-user\"]}"; @Test public void restAddDropUser() throws Exception { SecurityService securityService = securityService(); assertNull(securityService.getUser("user3")); CloseableHttpClient client = HttpClientBuilder.create().build(); HttpPost post = new HttpPost(getRestURL("/security/users", null, "akiban:topsecret")); post.setEntity(new StringEntity(ADD_USER, ContentType.APPLICATION_JSON)); HttpResponse response = client.execute(post); int code = response.getStatusLine().getStatusCode(); String content = EntityUtils.toString(response.getEntity()); assertEquals(HttpStatus.SC_OK, code); assertNotNull(securityService.getUser("user3")); // Check returned id JsonNode idNode = readTree(content).get("id"); assertNotNull("Has id field", idNode); assertEquals("id is integer", true, idNode.isInt()); HttpDelete delete = new HttpDelete(getRestURL("/security/users/user3", null, "akiban:topsecret")); response = client.execute(delete); code = response.getStatusLine().getStatusCode(); EntityUtils.consume(response.getEntity()); client.close(); assertEquals(HttpStatus.SC_OK, code); assertNull(securityService.getUser("user3")); } }