HTTP Basic authentication to REST API

Got an idea? Missing something? Post your feature request here.

Moderator: moderators

HTTP Basic authentication to REST API

Postby hovenko » Sun Mar 07, 2010 1:28 pm

Supplying username and password on the URL is considered a security risk, since URLs are logged on webservers and possible proxy servers across the Internet (for example your company proxy/firewall).

I made a patch to Subsonic 3.9 that will allow HTTP Basic Authentication to the REST web services, so you don't have to use the "p" and "u" parameters.

The patch also makes it possible to run the application in jetty, when developing and testing. To run jetty, execute this from the subsonic-main directory:
Code: Select all
mvn jetty:run -Dsubsonic.home=/tmp/subsonic-test


Code: Select all
diff --git a/subsonic-main/pom.xml b/subsonic-main/pom.xml                                                                   
index 5e2eccf..e7e97e7 100644                                                                                               
--- a/subsonic-main/pom.xml                                                                                                 
+++ b/subsonic-main/pom.xml                                                                                                 
@@ -275,6 +275,26 @@                                                                                                         
             <scope>test</scope>                                                                                             
         </dependency>                                                                                                       
                                                                                                                             
+        <dependency>                                                                                                       
+            <groupId>org.springframework</groupId>                                                                         
+            <artifactId>spring-test</artifactId>                                                                           
+            <version>2.5.6</version>                                                                                       
+            <scope>test</scope>                                                                                             
+        </dependency>                                                                                                       
+                                                                                                                           
+        <dependency>                                                                                                       
+            <groupId>org.mortbay.jetty</groupId>                                                                           
+            <artifactId>jetty</artifactId>                                                                                 
+            <version>6.1.5</version>                                                                                       
+            <scope>test</scope>                                                                                             
+            <exclusions>                                                                                                   
+                <exclusion>                                                                                                 
+                    <groupId>org.mortbay.jetty</groupId>                                                                   
+                    <artifactId>servlet-api</artifactId>                                                                   
+                </exclusion>                                                                                               
+            </exclusions>                                                                                                   
+        </dependency>                                                                                                       
+                                                                                                                           
     </dependencies>                                                                                                         
                                                                                                                             
     <profiles>                                                                                                             
@@ -329,6 +349,20 @@                                                                                                         
                 </executions>                                                                                               
             </plugin>                                                                                                       
                                                                                                                             
+            <plugin>                                                                                                       
+                <groupId>org.mortbay.jetty</groupId>                                                                       
+                <artifactId>maven-jetty-plugin</artifactId>                                                                 
+                <version>6.1.16</version>                                                                                   
+                <configuration>                                                                                             
+                    <connectors>                                                                                           
+                        <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">                           
+                            <port>8181</port>                                                                               
+                            <maxIdleTime>6000</maxIdleTime>                                                                 
+                        </connector>                                                                                       
+                    </connectors>                                                                                           
+                </configuration>                                                                                           
+            </plugin>                                                                                                       
+                                                                                                                           
         </plugins>                                                                                                         
     </build>                                                                                                               
</project>                                                                                                                 
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/security/RESTRequestParameterProcessingFilter.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/security/RESTRequestParameterProcessingFilter.java                                                                                                                                         
index 659d121..9f23090 100644                                                                                                                                                       
--- a/subsonic-main/src/main/java/net/sourceforge/subsonic/security/RESTRequestParameterProcessingFilter.java                                                                       
+++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/security/RESTRequestParameterProcessingFilter.java                                                                       
@@ -75,6 +75,8 @@ public class RESTRequestParameterProcessingFilter implements Filter {                                                                                             
         HttpServletRequest httpRequest = (HttpServletRequest) request;                                                                                                             
         HttpServletResponse httpResponse = (HttpServletResponse) response;                                                                                                         
                                                                                                                                                                                   
+        Authentication previousAuth = SecurityContextHolder.getContext().getAuthentication();                                                                                     
+                                                                                                                                                                                   
         String username = StringUtils.trimToNull(httpRequest.getParameter("u"));                                                                                                   
         String password = decrypt(StringUtils.trimToNull(httpRequest.getParameter("p")));                                                                                         
         String version = StringUtils.trimToNull(httpRequest.getParameter("v"));                                                                                                   
@@ -82,7 +84,25 @@ public class RESTRequestParameterProcessingFilter implements Filter {                                                                                           
                                                                                                                                                                                   
         RESTController.ErrorCode errorCode = null;                                                                                                                                 
                                                                                                                                                                                   
-        if (username == null || password == null || version == null || client == null) {                                                                                           
+        if (previousAuth == null) {                                                                                                                                               
+            /*                                                                                                                                                                     
+             * The username and password parameters are not required if the user                                                                                                   
+             * was previously authenticated, for example using Basic Auth                                                                                                         
+             */                                                                                                                                                                   
+            if (username == null || password == null) {                                                                                                                           
+                errorCode = RESTController.ErrorCode.MISSING_PARAMETER;                                                                                                           
+            }                                                                                                                                                                     
+        } else {                                                                                                                                                                   
+            if (username != null || password != null) {                                                                                                                           
+                LOG.warn("Username and password provided in URL params, "                                                                                                         
+                    + "but discarded. User already authenticated as "                                                                                                             
+                    + previousAuth.getName());                                                                                                                                     
+            }                                                                                                                                                                     
+                                                                                                                                                                                   
+            username = previousAuth.getName();                                                                                                                                     
+        }                                                                                                                                                                         
+
+        if (version == null || client == null) {
             errorCode = RESTController.ErrorCode.MISSING_PARAMETER;
         }

@@ -90,7 +110,9 @@ public class RESTRequestParameterProcessingFilter implements Filter {
             errorCode = checkAPIVersion(version);
         }

-        if (errorCode == null) {
+        if (previousAuth != null) {
+            // Already authenticated
+        } else if (errorCode == null) {
             errorCode = authenticate(username, password);
         }

@@ -202,4 +224,4 @@ public class RESTRequestParameterProcessingFilter implements Filter {
     public void setSettingsService(SettingsService settingsService) {
         this.settingsService = settingsService;
     }
-}
\ No newline at end of file
+}
diff --git a/subsonic-main/src/main/webapp/WEB-INF/applicationContext-security.xml b/subsonic-main/src/main/webapp/WEB-INF/applicationContext-security.xml
index 460fa3a..560be60 100644
--- a/subsonic-main/src/main/webapp/WEB-INF/applicationContext-security.xml
+++ b/subsonic-main/src/main/webapp/WEB-INF/applicationContext-security.xml
@@ -11,7 +11,7 @@
                 /wap**=httpSessionContextIntegrationFilter,logoutFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,basicExceptionTranslationFilter,filterInvocationInterceptor
                 /podcastReceiver**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
                 /podcast**=httpSessionContextIntegrationFilter,logoutFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,basicExceptionTranslationFilter,filterInvocationInterceptor
-                /rest/**=httpSessionContextIntegrationFilter,logoutFilter,restRequestParameterProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,basicExceptionTranslationFilter,filterInvocationInterceptor
+                /rest/**=httpSessionContextIntegrationFilter,logoutFilter,basicProcessingFilter,restRequestParameterProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,basicExceptionTranslationFilter,filterInvocationInterceptor
                 /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
             </value>
         </property>
hovenko
 
Posts: 2
Joined: Sun Mar 07, 2010 1:18 pm

Postby sindre_mehus » Mon Mar 08, 2010 8:36 am

Very nice!

I'll review your changes and include them in the 4.0 release.

Have you by any chance tested if this works also when redirecting from a xxx.subsonic.org address?

Thanks a lot for your contribution :-)
Sindre
Subsonic developer
User avatar
sindre_mehus
 
Posts: 1955
Joined: Tue Nov 29, 2005 6:19 pm
Location: Oslo, Norway

Postby hovenko » Mon Mar 08, 2010 2:58 pm

sindre_mehus wrote:Have you by any chance tested if this works also when redirecting from a xxx.subsonic.org address?


No, I havn't, sorry.

How is the xxx.subsonic.org address working? Is it just a HTTP redirect or a proxy?

In case it's a redirect, I guess it's mostly up to the HTTP/REST client to pass the correct authentication header on to the site after redirection.
hovenko
 
Posts: 2
Joined: Sun Mar 07, 2010 1:18 pm

Postby sindre_mehus » Mon Mar 08, 2010 5:51 pm

It's a http redirect, so I guess it should work. I'll test it with the Android client once I get round to it.
Subsonic developer
User avatar
sindre_mehus
 
Posts: 1955
Joined: Tue Nov 29, 2005 6:19 pm
Location: Oslo, Norway


Return to Feature Requests

Who is online

Users browsing this forum: No registered users and 9 guests