View Javadoc

1   package com.terradue.jcatalogue.client.internal.ahc;
2   
3   /*
4    *    Copyright 2011-2012 Terradue srl
5    *
6    *    Licensed under the Apache License, Version 2.0 (the "License");
7    *    you may not use this file except in compliance with the License.
8    *    You may obtain a copy of the License at
9    *
10   *       http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *    Unless required by applicable law or agreed to in writing, software
13   *    distributed under the License is distributed on an "AS IS" BASIS,
14   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *    See the License for the specific language governing permissions and
16   *    limitations under the License.
17   */
18  
19  import static com.ning.http.util.AsyncHttpProviderUtils.parseCookie;
20  import static com.terradue.jcatalogue.client.internal.lang.Assertions.checkState;
21  import static java.net.HttpURLConnection.HTTP_MOVED_PERM;
22  import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
23  import static java.net.HttpURLConnection.HTTP_OK;
24  import static java.util.regex.Pattern.CASE_INSENSITIVE;
25  
26  import java.net.URI;
27  import java.util.BitSet;
28  import java.util.LinkedList;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.concurrent.ConcurrentHashMap;
32  import java.util.concurrent.ConcurrentMap;
33  import java.util.regex.Pattern;
34  
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  
38  import com.ning.http.client.Cookie;
39  import com.ning.http.client.HttpResponseStatus;
40  import com.ning.http.client.RequestBuilder;
41  import com.ning.http.client.filter.FilterContext;
42  import com.ning.http.client.filter.FilterException;
43  import com.ning.http.client.filter.ResponseFilter;
44  import com.terradue.jcatalogue.client.HttpMethod;
45  import com.terradue.jcatalogue.client.Parameter;
46  
47  /**
48   * @since 0.8
49   */
50  final class UmSsoStatusResponseFilter
51      implements ResponseFilter
52  {
53  
54      private static final String SET_COOKIE = "Set-Cookie";
55  
56      private static final String CONTENT_TYPE = "Content-Type";
57  
58      private final Pattern textHtmlPattern = Pattern.compile( "text/html", CASE_INSENSITIVE );
59  
60      private final BitSet admittedStatuses = new BitSet();
61  
62      private final Logger logger = LoggerFactory.getLogger( getClass() );
63  
64      private final Map<String, UmSsoAccess> umSsoCredentials;
65  
66      private final ConcurrentMap<String, ConcurrentMap<String, Cookie>> cookiesRegistry;
67  
68      public UmSsoStatusResponseFilter( ConcurrentMap<String, UmSsoAccess> umSsoCredentials,
69                                        ConcurrentMap<String, ConcurrentMap<String, Cookie>> cookiesRegistry)
70      {
71          this.umSsoCredentials = umSsoCredentials;
72          this.cookiesRegistry = cookiesRegistry;
73  
74          admittedStatuses.set( HTTP_OK );
75          admittedStatuses.set( HTTP_MOVED_PERM );
76          admittedStatuses.set( HTTP_MOVED_TEMP );
77          admittedStatuses.set( 307 ); // Temporary Redirect
78      }
79  
80      @Override
81      @SuppressWarnings( { "rawtypes", "unchecked" } )
82      public FilterContext filter( FilterContext ctx )
83          throws FilterException
84      {
85          HttpResponseStatus responseStatus = ctx.getResponseStatus();
86  
87          // 1. verify the state is one of the admitted
88          checkState( admittedStatuses.get( responseStatus.getStatusCode() ),
89                      "Impossible to query the catalog %s, server replied %s",
90                      ctx.getRequest().getRawUrl(), responseStatus.getStatusText() );
91  
92          final String currentDomain = URI.create( ctx.getRequest().getUrl() ).getHost();
93  
94          // 2. collect all the cookies, indexing them per domain
95  
96          final List<String> cookiesString = ctx.getResponseHeaders().getHeaders().get( SET_COOKIE );
97          final List<Cookie> cookies = new LinkedList<Cookie>();
98  
99          // just log there are no cookies to manage for the current domain
100         if ( cookiesString == null || cookiesString.isEmpty() )
101         {
102             if ( logger.isDebugEnabled() )
103             {
104                 logger.debug( "No cookies to manage in domain {}", currentDomain );
105             }
106         }
107         else
108         {
109             // verify there is a cookies index for the current domain
110             ConcurrentMap<String, Cookie> domainCookies = cookiesRegistry.get( currentDomain );
111             if ( domainCookies == null )
112             {
113                 if ( logger.isDebugEnabled() )
114                 {
115                     logger.debug( "Domain {} was not already managed", currentDomain );
116                 }
117 
118                 // create the index and store it
119                 domainCookies = new ConcurrentHashMap<String, Cookie>();
120                 cookiesRegistry.put( currentDomain, domainCookies );
121             }
122 
123             // collect all cookies, adding/replacing them with latest updated value
124             for ( String cookieValue : cookiesString )
125             {
126                 if ( logger.isDebugEnabled() )
127                 {
128                     logger.debug( "Received cookie {}", cookieValue );
129                 }
130 
131                 Cookie currentCookie = parseCookie( cookieValue );
132 
133                 cookies.add( currentCookie );
134                 domainCookies.put( currentCookie.getName(), currentCookie );
135             }
136         }
137 
138         if ( logger.isDebugEnabled() )
139         {
140             logger.debug( "Checking server status code reply: {}", responseStatus.getStatusCode() );
141         }
142 
143         // check UM-SSO conditions - this is hack-ish but didn't find a better solution ATM
144         if ( HTTP_OK == responseStatus.getStatusCode() )
145         {
146             String contentType = ctx.getResponseHeaders().getHeaders().getFirstValue( CONTENT_TYPE );
147 
148             if ( logger.isDebugEnabled() )
149             {
150                 logger.debug( "Checking content type {} behavior", contentType );
151             }
152 
153             if ( !textHtmlPattern.matcher( contentType ).matches() )
154             {
155                 logger.debug( "Content type {} doesn't ned to be analyzed, just proceed", contentType );
156 
157                 // return ctx;
158             }
159 
160             if ( logger.isDebugEnabled() )
161             {
162                 logger.debug( "Checking UM-SSO auth credentials for current host {}", currentDomain );
163             }
164 
165             UmSsoAccess umSsoAccess = umSsoCredentials.get( currentDomain );
166 
167             if ( umSsoAccess != null )
168             {
169                 if ( logger.isDebugEnabled() )
170                 {
171                     logger.debug( "Redirecting request to {} {}",
172                                   umSsoAccess.getHttpMethod(), umSsoAccess.getLoginFormUrl() );
173                 }
174 
175                 RequestBuilder authRequestBuilder = new RequestBuilder( umSsoAccess.getHttpMethod().toString() )
176                                                              .setUrl( umSsoAccess.getLoginFormUrl().toString() );
177 
178                 for ( Cookie cookie : cookiesRegistry.get( currentDomain ).values() )
179                 {
180                     if ( logger.isDebugEnabled() )
181                     {
182                         logger.debug( "Adding {} for host {}", cookie, currentDomain );
183                     }
184 
185                     authRequestBuilder.addCookie( cookie );
186                 }
187 
188                 for ( Parameter parameter : umSsoAccess.getParameters() )
189                 {
190                     authRequestBuilder.addParameter( parameter.getName(), parameter.getValue() );
191                 }
192 
193                 return new FilterContext.FilterContextBuilder( ctx )
194                             .request( authRequestBuilder.build() )
195                             .asyncHandler( ctx.getAsyncHandler() )
196                             .replayRequest( true )
197                             .build();
198             }
199             else if ( logger.isWarnEnabled() )
200             {
201                 logger.warn( "Domain {} not managed for UM-SSO authentication!", currentDomain );
202             }
203 
204             return ctx;
205         }
206 
207         if ( logger.isDebugEnabled() )
208         {
209             logger.debug( "Proceeding on serving the request" );
210         }
211 
212         if ( HttpMethod.POST.toString().equals( ctx.getRequest().getMethod() ) )
213         {
214             RequestBuilder redirect = new RequestBuilder( ctx.getRequest() )
215                 .setMethod( HttpMethod.GET.toString() );
216 
217             // reply all cookies
218             for ( Cookie cookie : cookies )
219             {
220                 redirect.addOrReplaceCookie( cookie );
221             }
222 
223             return new FilterContext.FilterContextBuilder( ctx )
224                 .request( redirect.build() )
225                 .replayRequest( true )
226                 .build();
227         }
228 
229         return ctx;
230     }
231 
232 }