From 4bea59fc816fba68b6ecb43050b1d53a75fae939 Mon Sep 17 00:00:00 2001 From: Miguel Reboiro-Jato Date: Mon, 18 Feb 2019 12:14:21 +0100 Subject: [PATCH] Adds security check to PeopleResourceTest The PeopleResourceTests now include security tests to verify that normal users can't access people data. --- src/main/angular/package-lock.json | 2 +- src/main/angular/package.json | 2 +- .../esei/daa/filters/AuthorizationFilter.java | 19 ++- .../esei/daa/rest/PeopleResourceTest.java | 121 ++++++++++++++---- .../esei/daa/rest/UsersResourceTest.java | 20 --- 5 files changed, 113 insertions(+), 51 deletions(-) diff --git a/src/main/angular/package-lock.json b/src/main/angular/package-lock.json index ebbcbfb..aa34786 100644 --- a/src/main/angular/package-lock.json +++ b/src/main/angular/package-lock.json @@ -1,6 +1,6 @@ { "name": "daa-example", - "version": "0.2.0-alpha.1", + "version": "0.2.0-alpha.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/main/angular/package.json b/src/main/angular/package.json index 2e12133..117e716 100644 --- a/src/main/angular/package.json +++ b/src/main/angular/package.json @@ -1,6 +1,6 @@ { "name": "daa-example", - "version": "0.2.0-alpha.1", + "version": "0.2.0-alpha.2", "scripts": { "ng": "./node_modules/.bin/ng", "start": "./node_modules/.bin/ng serve", diff --git a/src/test/java/es/uvigo/esei/daa/filters/AuthorizationFilter.java b/src/test/java/es/uvigo/esei/daa/filters/AuthorizationFilter.java index 6c058a1..b2144df 100644 --- a/src/test/java/es/uvigo/esei/daa/filters/AuthorizationFilter.java +++ b/src/test/java/es/uvigo/esei/daa/filters/AuthorizationFilter.java @@ -3,12 +3,14 @@ package es.uvigo.esei.daa.filters; import java.io.IOException; import java.security.Principal; import java.util.Base64; +import java.util.List; import javax.annotation.Priority; import javax.ws.rs.Priorities; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.PathSegment; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.SecurityContext; @@ -18,6 +20,12 @@ import es.uvigo.esei.daa.dao.DAOException; import es.uvigo.esei.daa.dao.UsersDAO; import es.uvigo.esei.daa.entities.User; +/** + * This performs the Basic HTTP authentication following (almost) the same + * rules as the defined in the web.xml file. + * + * @author Miguel Reboiro Jato + */ @Provider @Priority(Priorities.AUTHENTICATION) public class AuthorizationFilter implements ContainerRequestFilter { @@ -46,7 +54,11 @@ public class AuthorizationFilter implements ContainerRequestFilter { if (this.dao.checkLogin(userPass[0], userPass[1])) { final User user = this.dao.get(userPass[0]); - requestContext.setSecurityContext(new UserSecurityContext(user)); + if (isPeoplePath(requestContext) && !user.getRole().equals("ADMIN")) { + requestContext.abortWith(createResponse()); + } else { + requestContext.setSecurityContext(new UserSecurityContext(user)); + } } else { requestContext.abortWith(createResponse()); } @@ -59,6 +71,11 @@ public class AuthorizationFilter implements ContainerRequestFilter { } } + private static boolean isPeoplePath(ContainerRequestContext context) { + final List pathSegments = context.getUriInfo().getPathSegments(); + return !pathSegments.isEmpty() && pathSegments.get(0).getPath().equals("people"); + } + private static Response createResponse() { return Response.status(Status.UNAUTHORIZED) .header(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"DAAExample\"") diff --git a/src/test/java/es/uvigo/esei/daa/rest/PeopleResourceTest.java b/src/test/java/es/uvigo/esei/daa/rest/PeopleResourceTest.java index 3fe2f74..4cbc8be 100644 --- a/src/test/java/es/uvigo/esei/daa/rest/PeopleResourceTest.java +++ b/src/test/java/es/uvigo/esei/daa/rest/PeopleResourceTest.java @@ -7,8 +7,12 @@ import static es.uvigo.esei.daa.dataset.PeopleDataset.newPerson; import static es.uvigo.esei.daa.dataset.PeopleDataset.newSurname; import static es.uvigo.esei.daa.dataset.PeopleDataset.nonExistentId; import static es.uvigo.esei.daa.dataset.PeopleDataset.people; +import static es.uvigo.esei.daa.dataset.UsersDataset.adminLogin; +import static es.uvigo.esei.daa.dataset.UsersDataset.normalLogin; +import static es.uvigo.esei.daa.dataset.UsersDataset.userToken; import static es.uvigo.esei.daa.matchers.HasHttpStatus.hasBadRequestStatus; import static es.uvigo.esei.daa.matchers.HasHttpStatus.hasOkStatus; +import static es.uvigo.esei.daa.matchers.HasHttpStatus.hasUnauthorized; import static es.uvigo.esei.daa.matchers.IsEqualToPerson.containsPeopleInAnyOrder; import static es.uvigo.esei.daa.matchers.IsEqualToPerson.equalsToPerson; import static javax.ws.rs.client.Entity.entity; @@ -40,7 +44,7 @@ import com.github.springtestdbunit.DbUnitTestExecutionListener; import com.github.springtestdbunit.annotation.DatabaseSetup; import com.github.springtestdbunit.annotation.ExpectedDatabase; -import es.uvigo.esei.daa.DAAExampleApplication; +import es.uvigo.esei.daa.DAAExampleTestApplication; import es.uvigo.esei.daa.entities.Person; import es.uvigo.esei.daa.listeners.ApplicationContextBinding; import es.uvigo.esei.daa.listeners.ApplicationContextJndiBindingTestExecutionListener; @@ -67,7 +71,7 @@ import es.uvigo.esei.daa.listeners.DbManagementTestExecutionListener; public class PeopleResourceTest extends JerseyTest { @Override protected Application configure() { - return new DAAExampleApplication(); + return new DAAExampleTestApplication(); } @Override @@ -81,27 +85,49 @@ public class PeopleResourceTest extends JerseyTest { @Test public void testList() throws IOException { - final Response response = target("people").request().get(); + final Response response = target("people").request() + .header("Authorization", "Basic " + userToken(adminLogin())) + .get(); assertThat(response, hasOkStatus()); final List people = response.readEntity(new GenericType>(){}); assertThat(people, containsPeopleInAnyOrder(people())); } + + @Test + public void testListUnauthorized() throws IOException { + final Response response = target("people").request() + .header("Authorization", "Basic " + userToken(normalLogin())) + .get(); + assertThat(response, hasUnauthorized()); + } @Test public void testGet() throws IOException { - final Response response = target("people/" + existentId()).request().get(); + final Response response = target("people/" + existentId()).request() + .header("Authorization", "Basic " + userToken(adminLogin())) + .get(); assertThat(response, hasOkStatus()); final Person person = response.readEntity(Person.class); assertThat(person, is(equalsToPerson(existentPerson()))); } + + @Test + public void testGetUnauthorized() throws IOException { + final Response response = target("people/" + existentId()).request() + .header("Authorization", "Basic " + userToken(normalLogin())) + .get(); + assertThat(response, hasUnauthorized()); + } @Test public void testGetInvalidId() throws IOException { - final Response response = target("people/" + nonExistentId()).request().get(); + final Response response = target("people/" + nonExistentId()).request() + .header("Authorization", "Basic " + userToken(adminLogin())) + .get(); assertThat(response, hasBadRequestStatus()); } @@ -113,24 +139,37 @@ public class PeopleResourceTest extends JerseyTest { form.param("name", newName()); form.param("surname", newSurname()); - final Response response = target("people") - .request(MediaType.APPLICATION_JSON_TYPE) - .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + final Response response = target("people").request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); assertThat(response, hasOkStatus()); final Person person = response.readEntity(Person.class); assertThat(person, is(equalsToPerson(newPerson()))); } + + @Test + public void testAddUnauthorized() throws IOException { + final Form form = new Form(); + form.param("name", newName()); + form.param("surname", newSurname()); + + final Response response = target("people").request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(normalLogin())) + .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + + assertThat(response, hasUnauthorized()); + } @Test public void testAddMissingName() throws IOException { final Form form = new Form(); form.param("surname", newSurname()); - final Response response = target("people") - .request(MediaType.APPLICATION_JSON_TYPE) - .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + final Response response = target("people").request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); assertThat(response, hasBadRequestStatus()); } @@ -140,9 +179,9 @@ public class PeopleResourceTest extends JerseyTest { final Form form = new Form(); form.param("name", newName()); - final Response response = target("people") - .request(MediaType.APPLICATION_JSON_TYPE) - .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + final Response response = target("people").request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .post(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); assertThat(response, hasBadRequestStatus()); } @@ -154,9 +193,9 @@ public class PeopleResourceTest extends JerseyTest { form.param("name", newName()); form.param("surname", newSurname()); - final Response response = target("people/" + existentId()) - .request(MediaType.APPLICATION_JSON_TYPE) - .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + final Response response = target("people/" + existentId()).request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); assertThat(response, hasOkStatus()); final Person modifiedPerson = response.readEntity(Person.class); @@ -168,14 +207,27 @@ public class PeopleResourceTest extends JerseyTest { assertThat(modifiedPerson, is(equalsToPerson(person))); } + @Test + public void testModifyUnauthorized() throws IOException { + final Form form = new Form(); + form.param("name", newName()); + form.param("surname", newSurname()); + + final Response response = target("people/" + existentId()).request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(normalLogin())) + .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + + assertThat(response, hasUnauthorized()); + } + @Test public void testModifyName() throws IOException { final Form form = new Form(); form.param("name", newName()); - final Response response = target("people/" + existentId()) - .request(MediaType.APPLICATION_JSON_TYPE) - .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + final Response response = target("people/" + existentId()).request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); assertThat(response, hasBadRequestStatus()); } @@ -185,9 +237,9 @@ public class PeopleResourceTest extends JerseyTest { final Form form = new Form(); form.param("surname", newSurname()); - final Response response = target("people/" + existentId()) - .request(MediaType.APPLICATION_JSON_TYPE) - .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + final Response response = target("people/" + existentId()).request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .put(entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); assertThat(response, hasBadRequestStatus()); } @@ -198,9 +250,9 @@ public class PeopleResourceTest extends JerseyTest { form.param("name", newName()); form.param("surname", newSurname()); - final Response response = target("people/" + nonExistentId()) - .request(MediaType.APPLICATION_JSON_TYPE) - .put(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); + final Response response = target("people/" + nonExistentId()).request(MediaType.APPLICATION_JSON_TYPE) + .header("Authorization", "Basic " + userToken(adminLogin())) + .put(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE)); assertThat(response, hasBadRequestStatus()); } @@ -208,7 +260,9 @@ public class PeopleResourceTest extends JerseyTest { @Test @ExpectedDatabase("/datasets/dataset-delete.xml") public void testDelete() throws IOException { - final Response response = target("people/" + existentId()).request().delete(); + final Response response = target("people/" + existentId()).request() + .header("Authorization", "Basic " + userToken(adminLogin())) + .delete(); assertThat(response, hasOkStatus()); @@ -216,10 +270,21 @@ public class PeopleResourceTest extends JerseyTest { assertThat(deletedId, is(equalTo(existentId()))); } + + @Test + public void testDeleteUnauthorized() throws IOException { + final Response response = target("people/" + existentId()).request() + .header("Authorization", "Basic " + userToken(normalLogin())) + .delete(); + + assertThat(response, hasUnauthorized()); + } @Test public void testDeleteInvalidId() throws IOException { - final Response response = target("people/" + nonExistentId()).request().delete(); + final Response response = target("people/" + nonExistentId()).request() + .header("Authorization", "Basic " + userToken(adminLogin())) + .delete(); assertThat(response, hasBadRequestStatus()); } diff --git a/src/test/java/es/uvigo/esei/daa/rest/UsersResourceTest.java b/src/test/java/es/uvigo/esei/daa/rest/UsersResourceTest.java index ed9a674..0c74ad1 100644 --- a/src/test/java/es/uvigo/esei/daa/rest/UsersResourceTest.java +++ b/src/test/java/es/uvigo/esei/daa/rest/UsersResourceTest.java @@ -17,13 +17,7 @@ import javax.ws.rs.core.Application; import javax.ws.rs.core.Response; import org.glassfish.jersey.client.ClientConfig; -import org.glassfish.jersey.server.ResourceConfig; -import org.glassfish.jersey.servlet.ServletContainer; -import org.glassfish.jersey.test.DeploymentContext; import org.glassfish.jersey.test.JerseyTest; -import org.glassfish.jersey.test.ServletDeploymentContext; -import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory; -import org.glassfish.jersey.test.spi.TestContainerFactory; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; @@ -74,20 +68,6 @@ public class UsersResourceTest extends JerseyTest { config.property("com.sun.jersey.api.json.POJOMappingFeature", Boolean.TRUE); } - @Override - protected TestContainerFactory getTestContainerFactory() { - return new GrizzlyWebTestContainerFactory(); - } - - @Override - protected DeploymentContext configureDeployment() { - return ServletDeploymentContext.forServlet( - new ServletContainer(ResourceConfig.forApplication(configure())) - ) - .servletPath("/rest") - .build(); - } - @Test public void testGetAdminOwnUser() throws IOException { final String admin = adminLogin(); -- 2.18.1