/* * Copyright (C) 2015 Stefano Fornari. * All Rights Reserved. No use, copying or distribution of this * work may be made except in accordance with a valid license * agreement from Stefano Fornari. This notice must be * included on all copies, modifications and derivatives of this * work. * * STEFANO FORNARI MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO * THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, OR NON-INFRINGEMENT. STEFANO FORNARI SHALL NOT BE LIABLE FOR ANY * DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING * THIS SOFTWARE OR ITS DERIVATIVES. */ package ste.web.http.handlers; import java.io.IOException; import java.security.Principal; import java.util.HashSet; import java.util.Set; import java.util.UUID; import javax.ws.rs.core.HttpHeaders; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.StatusLine; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpRequestHandler; import static org.assertj.core.api.Assertions.fail; import static org.assertj.core.api.BDDAssertions.then; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import ste.web.http.HttpSessionContext; import ste.web.http.HttpUtils; import ste.web.acl.AccessControlList; import ste.web.acl.HashMapAuthenticator; import ste.web.acl.User; import ste.web.http.HttpSession; public class BugFreeRestrictedResourceHandler { @Rule public final TemporaryFolder DOCROOT = new TemporaryFolder(); private static final String[] RESTRICTED_URIS = { "/api/collections", "/item?id=10", "/private/news.html" }; private final User[] USERS = { new User("one", "111"), new User("two", "222"), new User("three", "333"), new User("four", "444") }; private final AccessControlList ACL = new AccessControlList(); private final HttpRequestHandler DUMMY_HANDLER = new HttpRequestHandler() { @Override public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException { // // just to be able to make sure the handler was executed // response.addHeader(HttpHeaders.CONTENT_LOCATION, request.getRequestLine().getUri()); } }; private final HashMapAuthenticator AUTHENTICATOR = new HashMapAuthenticator(USERS); @Before public void before() { ACL.add("ste.web.acl.permissions.star"); } @Test public void constructors() { try { new RestrictedResourceHandler(null, ACL, AUTHENTICATOR); fail("missing arguments check"); } catch (IllegalArgumentException x) { then(x).hasMessage("handler can not be null"); } RestrictedResourceHandler h = new RestrictedResourceHandler(DUMMY_HANDLER, null, null); then(h.getHandler()).isSameAs(DUMMY_HANDLER); then(h.getAcl()).isNull(); then(h.getAuthenticator()).isNull(); h = new RestrictedResourceHandler(DUMMY_HANDLER, ACL, AUTHENTICATOR); then(h.getHandler()).isSameAs(DUMMY_HANDLER); then(h.getAcl()).isSameAs(ACL); then(h.getAuthenticator()).isSameAs(AUTHENTICATOR); } // // @TODO: we shall do it for all methods // @Test public void handle_the_request_in_authenticated_session() throws Exception { RestrictedResourceHandler h = new RestrictedResourceHandler(DUMMY_HANDLER, ACL, AUTHENTICATOR); Set<String> permissions = new HashSet<>(); permissions.add("ste.web.acl.permissions.star"); for (String URI: RESTRICTED_URIS) { HttpSessionContext context = new HttpSessionContext(); HttpRequest rq = HttpUtils.getSimpleGet(URI); HttpResponse rs = HttpUtils.getBasicResponse(); context.setSession(new HttpSession()); USERS[0].setPermissions(permissions); context.setPrincipal(USERS[0]); h.handle(rq, rs, context); StatusLine sl = rs.getStatusLine(); then(sl.getStatusCode()).isEqualTo(HttpStatus.SC_OK); then(rs.getFirstHeader(HttpHeaders.CONTENT_LOCATION).getValue()) .isEqualTo(rq.getRequestLine().getUri()); } } @Test public void returns_401_with_wwwauthenticate_on_get_if_unauthenticated_session() throws Exception { RestrictedResourceHandler h = new RestrictedResourceHandler(DUMMY_HANDLER, ACL, AUTHENTICATOR); AUTHENTICATOR.message = UUID.randomUUID().toString(); for (String URI: RESTRICTED_URIS) { HttpSessionContext context = new HttpSessionContext(); HttpRequest rq = HttpUtils.getSimpleGet(URI); HttpResponse rs = HttpUtils.getBasicResponse(); context.setSession(new HttpSession()); h.handle(rq, rs, context); StatusLine sl = rs.getStatusLine(); then(sl.getStatusCode()).isEqualTo(HttpStatus.SC_UNAUTHORIZED); then(sl.getReasonPhrase()).isEqualTo("resource " + URI + " requires authentication"); then(rs.getFirstHeader(HttpHeaders.WWW_AUTHENTICATE).getValue()).isEqualTo(AUTHENTICATOR.getMessage()); } } @Test public void returns_403_on_get_if_wrong_permissions() throws Exception { AccessControlList acl = new AccessControlList(); acl.add("ste.web.acl.permissions.private"); RestrictedResourceHandler h = new RestrictedResourceHandler(DUMMY_HANDLER, acl, AUTHENTICATOR); for (Principal user: USERS) { for (String URI: RESTRICTED_URIS) { HttpSessionContext session = new HttpSessionContext(); HttpRequest rq = HttpUtils.getSimpleGet(URI); HttpResponse rs = HttpUtils.getBasicResponse(); session.setPrincipal(user); h.handle(rq, rs, session); StatusLine sl = rs.getStatusLine(); then(sl.getStatusCode()).isEqualTo(HttpStatus.SC_FORBIDDEN); then(sl.getReasonPhrase()).isEqualTo("resource " + URI + " requires authorization"); } } } @Test public void returns_401_with_wwwauthenticate_on_get_if_unmached_credentials() throws Exception { HashMapAuthenticator a = new HashMapAuthenticator(); a.message = UUID.randomUUID().toString(); RestrictedResourceHandler h = new RestrictedResourceHandler(DUMMY_HANDLER, null, a); for (Principal user: USERS) { HttpSessionContext session = new HttpSessionContext(); HttpRequest rq = HttpUtils.getSimpleGet(RESTRICTED_URIS[0]); HttpResponse rs = HttpUtils.getBasicResponse(); session.setPrincipal(user); h.handle(rq, rs, session); StatusLine sl = rs.getStatusLine(); then(sl.getStatusCode()).isEqualTo(HttpStatus.SC_UNAUTHORIZED); then(sl.getReasonPhrase()).isEqualTo("invalid credentials"); then(rs.getFirstHeader(HttpHeaders.WWW_AUTHENTICATE).getValue()).isEqualTo(a.getMessage()); } } @Test public void returns_200_on_get_if_mached_credentials() throws Exception { RestrictedResourceHandler h = new RestrictedResourceHandler(DUMMY_HANDLER, null, AUTHENTICATOR); for (Principal user: USERS) { HttpSessionContext context = new HttpSessionContext(); HttpRequest rq = HttpUtils.getSimpleGet(RESTRICTED_URIS[0]); HttpResponse rs = HttpUtils.getBasicResponse(); context.setSession(new HttpSession()); context.setPrincipal(user); h.handle(rq, rs, context); StatusLine sl = rs.getStatusLine(); then(sl.getStatusCode()).isEqualTo(HttpStatus.SC_OK); // // plus, session shall have the principal set // then(context.getSession().getPrincipal()).isSameAs(context.getPrincipal()); } } @Test public void skip_authentication_if_session_is_already_authenticated() throws Exception { RestrictedResourceHandler h = new RestrictedResourceHandler(DUMMY_HANDLER, null, AUTHENTICATOR); final User user = new User("one", "111"); HttpSessionContext context = new HttpSessionContext(); HttpRequest rq = HttpUtils.getSimpleGet(RESTRICTED_URIS[0]); HttpResponse rs = HttpUtils.getBasicResponse(); context.setSession(new HttpSession()); context.setPrincipal(user); h.handle(rq, rs, context); StatusLine sl = rs.getStatusLine(); then(sl.getStatusCode()).isEqualTo(HttpStatus.SC_OK); // // now the session is authenticated // user.setSecret("none"); h.handle(rq, rs, context); then(sl.getStatusCode()).isEqualTo(HttpStatus.SC_OK); } @Test public void get_acl() { then(new RestrictedResourceHandler(DUMMY_HANDLER, ACL, AUTHENTICATOR).getAcl()).isSameAs(ACL); AccessControlList acl = new AccessControlList(); then(new RestrictedResourceHandler(DUMMY_HANDLER, acl, AUTHENTICATOR).getAcl()).isSameAs(acl); } @Test public void no_auth_check_if_acl_is_null() throws Exception { RestrictedResourceHandler h = new RestrictedResourceHandler(DUMMY_HANDLER, null, AUTHENTICATOR); HttpSessionContext context = new HttpSessionContext(); HttpRequest rq = HttpUtils.getSimpleGet(RESTRICTED_URIS[0]); HttpResponse rs = HttpUtils.getBasicResponse(); context.setSession(new HttpSession()); context.setPrincipal(USERS[0]); h.handle(rq, rs, context); StatusLine sl = rs.getStatusLine(); then(sl.getStatusCode()).isEqualTo(HttpStatus.SC_OK); then(rs.getFirstHeader(HttpHeaders.CONTENT_LOCATION).getValue()) .isEqualTo(rq.getRequestLine().getUri()); } @Test public void no_auth_check_if_authenticator_is_null() throws Exception { RestrictedResourceHandler h = new RestrictedResourceHandler(DUMMY_HANDLER, ACL, null); HttpSessionContext context = new HttpSessionContext(); HttpRequest rq = HttpUtils.getSimpleGet(RESTRICTED_URIS[0]); HttpResponse rs = HttpUtils.getBasicResponse(); Set<String> permissions = new HashSet<>(); permissions.add("ste.web.acl.permissions.star"); context.setSession(new HttpSession()); USERS[0].setPermissions(permissions); context.setPrincipal(USERS[0]); h.handle(rq, rs, context); StatusLine sl = rs.getStatusLine(); then(sl.getStatusCode()).isEqualTo(HttpStatus.SC_OK); then(rs.getFirstHeader(HttpHeaders.CONTENT_LOCATION).getValue()) .isEqualTo(rq.getRequestLine().getUri()); } }