shiro-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bdem...@apache.org
Subject shiro git commit: SHIRO-200: Added ability to configure basic authentication for specific HTTP methods
Date Mon, 11 Jul 2016 22:49:23 GMT
Repository: shiro
Updated Branches:
  refs/heads/master 3432f6ade -> ff84432da


SHIRO-200: Added ability to configure basic authentication for specific HTTP methods

The follow example, would allow anonymous access to /basic/** for HEAD and GET requests, but
require
basicAuth for modifying actions (POST, PUT, DELTE)
```ini
[urls]
/basic/** = authcBasic[POST,PUT,DELETE]
```


Project: http://git-wip-us.apache.org/repos/asf/shiro/repo
Commit: http://git-wip-us.apache.org/repos/asf/shiro/commit/ff84432d
Tree: http://git-wip-us.apache.org/repos/asf/shiro/tree/ff84432d
Diff: http://git-wip-us.apache.org/repos/asf/shiro/diff/ff84432d

Branch: refs/heads/master
Commit: ff84432dabf5698fd4ce83f41c41f70b665bfc07
Parents: 3432f6a
Author: Brian Demers <bdemers@apache.org>
Authored: Mon Jul 11 18:36:12 2016 -0400
Committer: Brian Demers <bdemers@apache.org>
Committed: Mon Jul 11 18:48:36 2016 -0400

----------------------------------------------------------------------
 .../authc/BasicHttpAuthenticationFilter.java    | 37 ++++++++
 .../BasicHttpFilterAuthenticationTest.java      | 98 +++++++++++++++++++-
 2 files changed, 134 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/shiro/blob/ff84432d/web/src/main/java/org/apache/shiro/web/filter/authc/BasicHttpAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/web/src/main/java/org/apache/shiro/web/filter/authc/BasicHttpAuthenticationFilter.java
b/web/src/main/java/org/apache/shiro/web/filter/authc/BasicHttpAuthenticationFilter.java
index 75f15c5..9f9a7e3 100644
--- a/web/src/main/java/org/apache/shiro/web/filter/authc/BasicHttpAuthenticationFilter.java
+++ b/web/src/main/java/org/apache/shiro/web/filter/authc/BasicHttpAuthenticationFilter.java
@@ -176,6 +176,43 @@ public class BasicHttpAuthenticationFilter extends AuthenticatingFilter
{
     public void setAuthcScheme(String authcScheme) {
         this.authcScheme = authcScheme;
     }
+    
+    /**
+     * The Basic authentication filter can be configured with a list of HTTP methods to which
it should apply. This
+     * method ensures that authentication is <em>only</em> required for those
HTTP methods specified. For example,
+     * if you had the configuration:
+     * <pre>
+     *    [urls]
+     *    /basic/** = authcBasic[POST,PUT,DELETE]
+     * </pre>
+     * then a GET request would not required authentication but a POST would.
+     * @param request The current HTTP servlet request.
+     * @param response The current HTTP servlet response.
+     * @param mappedValue The array of configured HTTP methods as strings. This is empty
if no methods are configured.
+     */
+    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object
mappedValue) {
+        HttpServletRequest httpRequest = WebUtils.toHttp(request);
+        String httpMethod = httpRequest.getMethod();
+        
+        // Check whether the current request's method requires authentication.
+        // If no methods have been configured, then all of them require auth,
+        // otherwise only the declared ones need authentication.
+        String[] methods = (String[]) (mappedValue == null ? new String[0] : mappedValue);
+        boolean authcRequired = methods.length == 0;
+        for (String m : methods) {
+            if (httpMethod.equalsIgnoreCase(m)) {
+                authcRequired = true;
+                break;
+            }
+        }
+        
+        if (authcRequired) {
+            return super.isAccessAllowed(request, response, mappedValue);
+        }
+        else {
+            return true;
+        }
+    }
 
     /**
      * Processes unauthenticated requests. It handles the two-stage request/challenge authentication
protocol.

http://git-wip-us.apache.org/repos/asf/shiro/blob/ff84432d/web/src/test/java/org/apache/shiro/web/filter/authc/BasicHttpFilterAuthenticationTest.java
----------------------------------------------------------------------
diff --git a/web/src/test/java/org/apache/shiro/web/filter/authc/BasicHttpFilterAuthenticationTest.java
b/web/src/test/java/org/apache/shiro/web/filter/authc/BasicHttpFilterAuthenticationTest.java
index 72a42cd..3c32002 100644
--- a/web/src/test/java/org/apache/shiro/web/filter/authc/BasicHttpFilterAuthenticationTest.java
+++ b/web/src/test/java/org/apache/shiro/web/filter/authc/BasicHttpFilterAuthenticationTest.java
@@ -32,6 +32,7 @@ import javax.servlet.http.HttpServletResponse;
 import org.apache.shiro.authc.AuthenticationToken;
 import org.apache.shiro.authc.UsernamePasswordToken;
 import org.apache.shiro.codec.Base64;
+import org.apache.shiro.test.SecurityManagerTestSupport;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -40,7 +41,7 @@ import org.junit.Test;
  * Test case for {@link BasicHttpAuthenticationFilter}.
  * @since 1.0
  */
-public class BasicHttpFilterAuthenticationTest {
+public class BasicHttpFilterAuthenticationTest extends SecurityManagerTestSupport {
 
     BasicHttpAuthenticationFilter testFilter;
 
@@ -137,6 +138,101 @@ public class BasicHttpFilterAuthenticationTest {
 		verify(request);
 		verify(response);
     }
+    
+    @Test
+    public void httpMethodDoesNotRequireAuthentication() throws Exception {
+        testFilter = new BasicHttpAuthenticationFilter();
+        
+        HttpServletRequest request = createMock(HttpServletRequest.class);
+        expect(request.getMethod()).andReturn("GET");
+        replay(request);
+        
+        HttpServletResponse response = createMock(HttpServletResponse.class);
+        replay(response);
+        
+        boolean accessAllowed = testFilter.isAccessAllowed(request, response, new String[]
{ "POST", "PUT", "DELETE" });
+        assertTrue("Access not allowed for GET", accessAllowed);
+    }
+    
+    @Test
+    public void httpMethodRequiresAuthentication() throws Exception {
+        testFilter = new BasicHttpAuthenticationFilter();
+        
+        HttpServletRequest request = createMock(HttpServletRequest.class);
+        expect(request.getHeader("Authorization")).andReturn(createAuthorizationHeader("pedro",
""));
+        expect(request.getRemoteHost()).andReturn("localhost");
+        expect(request.getMethod()).andReturn("POST");
+        replay(request);
+        
+        HttpServletResponse response = createMock(HttpServletResponse.class);
+        replay(response);
+        
+        boolean accessAllowed = testFilter.isAccessAllowed(request, response, new String[]
{ "POST", "PUT", "DELETE" });
+        assertTrue("Access allowed for POST", !accessAllowed);
+    }
+    
+    @Test
+    public void httpMethodsAreCaseInsensitive() throws Exception {
+        testFilter = new BasicHttpAuthenticationFilter();
+        
+        HttpServletRequest request = createMock(HttpServletRequest.class);
+        expect(request.getMethod()).andReturn("GET");
+        expect(request.getMethod()).andReturn("POST");
+        expect(request.getHeader("Authorization")).andReturn(createAuthorizationHeader("pedro",
"")).anyTimes();
+        expect(request.getRemoteHost()).andReturn("localhost").anyTimes();
+        replay(request);
+        
+        HttpServletResponse response = createMock(HttpServletResponse.class);
+        replay(response);
+        
+        boolean accessAllowed = testFilter.isAccessAllowed(request, response, new String[]
{ "post", "put", "delete" });
+        assertTrue("Access not allowed for GET", accessAllowed);
+        
+        accessAllowed = testFilter.isAccessAllowed(request, response, new String[] { "post",
"put", "delete" });
+        assertTrue("Access allowed for POST", !accessAllowed);
+    }
+    
+    @Test
+    public void allHttpMethodsRequireAuthenticationIfNoneConfigured() throws Exception {
+        testFilter = new BasicHttpAuthenticationFilter();
+        
+        HttpServletRequest request = createMock(HttpServletRequest.class);
+        expect(request.getHeader("Authorization")).andReturn(createAuthorizationHeader("pedro",
"")).anyTimes();
+        expect(request.getRemoteHost()).andReturn("localhost").anyTimes();
+        expect(request.getMethod()).andReturn("GET");
+        expect(request.getMethod()).andReturn("POST");
+        replay(request);
+        
+        HttpServletResponse response = createMock(HttpServletResponse.class);
+        replay(response);
+        
+        boolean accessAllowed = testFilter.isAccessAllowed(request, response, new String[0]);
+        assertTrue("Access allowed for GET", !accessAllowed);
+        
+        accessAllowed = testFilter.isAccessAllowed(request, response, new String[0]);
+        assertTrue("Access allowed for POST", !accessAllowed);
+    }
+    
+    @Test
+    public void allHttpMethodsRequireAuthenticationIfNullConfig() throws Exception {
+        testFilter = new BasicHttpAuthenticationFilter();
+        
+        HttpServletRequest request = createMock(HttpServletRequest.class);
+        expect(request.getHeader("Authorization")).andReturn(createAuthorizationHeader("pedro",
"")).anyTimes();
+        expect(request.getRemoteHost()).andReturn("localhost").anyTimes();
+        expect(request.getMethod()).andReturn("GET");
+        expect(request.getMethod()).andReturn("POST");
+        replay(request);
+        
+        HttpServletResponse response = createMock(HttpServletResponse.class);
+        replay(response);
+        
+        boolean accessAllowed = testFilter.isAccessAllowed(request, response, null);
+        assertTrue("Access allowed for GET", !accessAllowed);
+        
+        accessAllowed = testFilter.isAccessAllowed(request, response, null);
+        assertTrue("Access allowed for POST", !accessAllowed);
+    }
 
     private String createAuthorizationHeader(String username, String password) {
     	return "Basic " + new String(Base64.encode((username + ":" + password).getBytes()));


Mime
View raw message