View Javadoc
1   package org.apache.maven.it;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.it.util.ResourceExtractor;
23  import org.apache.maven.it.utils.DeployedResource;
24  import org.codehaus.plexus.util.StringUtils;
25  import org.eclipse.jetty.security.ConstraintMapping;
26  import org.eclipse.jetty.security.ConstraintSecurityHandler;
27  import org.eclipse.jetty.security.HashLoginService;
28  import org.eclipse.jetty.server.Handler;
29  import org.eclipse.jetty.server.NetworkConnector;
30  import org.eclipse.jetty.server.Request;
31  import org.eclipse.jetty.server.Server;
32  import org.eclipse.jetty.server.handler.AbstractHandler;
33  import org.eclipse.jetty.server.handler.HandlerList;
34  import org.eclipse.jetty.servlet.ServletContextHandler;
35  import org.eclipse.jetty.util.B64Code;
36  import org.eclipse.jetty.util.security.Constraint;
37  import org.eclipse.jetty.util.security.Password;
38  
39  import javax.servlet.http.HttpServletRequest;
40  import javax.servlet.http.HttpServletResponse;
41  import java.io.File;
42  import java.nio.charset.StandardCharsets;
43  import java.util.Collections;
44  import java.util.Deque;
45  import java.util.concurrent.ConcurrentLinkedDeque;
46  
47  import static org.eclipse.jetty.servlet.ServletContextHandler.SECURITY;
48  import static org.eclipse.jetty.servlet.ServletContextHandler.SESSIONS;
49  import static org.eclipse.jetty.util.security.Constraint.__BASIC_AUTH;
50  
51  /**
52   * This is a test set for <a href="https://issues.apache.org/jira/browse/MNG-4470">MNG-4470</a>.
53   *
54   * @author Benjamin Bentmann
55   *
56   */
57  public class MavenITmng4470AuthenticatedDeploymentToProxyTest
58      extends AbstractMavenIntegrationTestCase
59  {
60      private Server server;
61  
62      private int port;
63  
64      private volatile boolean deployed;
65  
66      private final Deque<DeployedResource> deployedResources = new ConcurrentLinkedDeque<>();
67  
68      public MavenITmng4470AuthenticatedDeploymentToProxyTest()
69      {
70          super( "[2.0.3,3.0-alpha-1),[3.0-alpha-6,)" );
71      }
72  
73      @Override
74      protected void setUp()
75          throws Exception
76      {
77          Handler proxyHandler = new AbstractHandler()
78          {
79              @Override
80              public void handle( String target, Request baseRequest, HttpServletRequest request,
81                                  HttpServletResponse response )
82              {
83                  System.out.println( "Handling " + request.getMethod() + " " + request.getRequestURL() );
84  
85                  String auth = request.getHeader( "Proxy-Authorization" );
86                  if ( auth != null )
87                  {
88                      auth = auth.substring( auth.indexOf( ' ' ) + 1 ).trim();
89                      auth = B64Code.decode( auth, StandardCharsets.US_ASCII.name() );
90                  }
91                  System.out.println( "Proxy-Authorization: " + auth );
92  
93                  if ( !"proxyuser:proxypass".equals( auth ) )
94                  {
95                      response.setStatus( HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED );
96                      response.addHeader( "Proxy-Authenticate", "Basic realm=\"Squid proxy-caching web server\"" );
97                      ( (Request) request ).setHandled( true );
98                  }
99  
100                 DeployedResource deployedResource = new DeployedResource();
101 
102                 deployedResource.httpMethod = request.getMethod();
103                 deployedResource.requestUri = request.getRequestURI();
104                 deployedResource.transferEncoding = request.getHeader( "Transfer-Encoding" );
105                 deployedResource.contentLength = request.getHeader( "Content-Length" );
106 
107                 deployedResources.add( deployedResource );
108             }
109         };
110 
111         Handler repoHandler = new AbstractHandler()
112         {
113             @Override
114             public void handle( String target, Request baseRequest, HttpServletRequest request,
115                                 HttpServletResponse response )
116             {
117                 System.out.println( "Handling " + request.getMethod() + " " + request.getRequestURL() );
118 
119                 if ( "PUT".equalsIgnoreCase( request.getMethod() ) )
120                 {
121                     response.setStatus( HttpServletResponse.SC_OK );
122                     deployed = true;
123                 }
124                 else
125                 {
126                     response.setStatus( HttpServletResponse.SC_NOT_FOUND );
127                 }
128 
129                 ( (Request) request ).setHandled( true );
130 
131                 DeployedResource deployedResource = new DeployedResource();
132 
133                 deployedResource.httpMethod = request.getMethod();
134                 deployedResource.requestUri = request.getRequestURI();
135                 deployedResource.transferEncoding = request.getHeader( "Transfer-Encoding" );
136                 deployedResource.contentLength = request.getHeader( "Content-Length" );
137 
138                 deployedResources.add( deployedResource );
139             }
140         };
141 
142         Constraint constraint = new Constraint();
143         constraint.setName( Constraint.__BASIC_AUTH );
144         constraint.setRoles( new String[]{ "deployer" } );
145         constraint.setAuthenticate( true );
146 
147         ConstraintMapping constraintMapping = new ConstraintMapping();
148         constraintMapping.setConstraint( constraint );
149         constraintMapping.setPathSpec( "/*" );
150 
151         HashLoginService userRealm = new HashLoginService( "TestRealm" );
152         userRealm.putUser( "testuser", new Password( "testtest" ), new String[] { "deployer" } );
153 
154         server = new Server( 0 );
155         ServletContextHandler ctx = new ServletContextHandler( server, "/", SESSIONS | SECURITY );
156         ConstraintSecurityHandler securityHandler = (ConstraintSecurityHandler) ctx.getSecurityHandler();
157         securityHandler.setLoginService( userRealm );
158         securityHandler.setAuthMethod( __BASIC_AUTH );
159         securityHandler.setConstraintMappings( new ConstraintMapping[] { constraintMapping } );
160 
161         HandlerList handlerList = new HandlerList();
162         handlerList.addHandler( proxyHandler );
163         handlerList.addHandler( securityHandler );
164         handlerList.addHandler( repoHandler );
165 
166         server.setHandler( handlerList );
167         server.start();
168         if ( server.isFailed() )
169         {
170             fail( "Couldn't bind the server socket to a free port!" );
171         }
172         port = ( (NetworkConnector) server.getConnectors()[0] ).getLocalPort();
173         System.out.println( "Bound server socket to the port " + port );
174         deployed = false;
175     }
176 
177     @Override
178     protected void tearDown()
179         throws Exception
180     {
181         if ( server != null )
182         {
183             server.stop();
184             server.join();
185         }
186     }
187 
188     /**
189      * Test that deployment (of a release) to a proxy that requires authentication works.
190      *
191      * @throws Exception in case of failure
192      */
193     public void testitRelease()
194         throws Exception
195     {
196         testit( "release" );
197     }
198 
199     /**
200      * Test that deployment (of a snapshot) to a proxy that requires authentication works.
201      *
202      * @throws Exception in case of failure
203      */
204     public void testitSnapshot()
205         throws Exception
206     {
207         testit( "snapshot" );
208     }
209 
210     private void testit( String project )
211         throws Exception
212     {
213         File testDir = ResourceExtractor.simpleExtractResources( getClass(), "/mng-4470/" + project );
214 
215         Verifier verifier = newVerifier( testDir.getAbsolutePath() );
216         verifier.setAutoclean( false );
217         verifier.filterFile( "settings-template.xml", "settings.xml", "UTF-8",
218                              Collections.singletonMap( "@port@", Integer.toString( port ) ) );
219         verifier.addCliOption( "--settings" );
220         verifier.addCliOption( "settings.xml" );
221         verifier.executeGoal( "validate" );
222         verifier.verifyErrorFreeLog();
223         verifier.resetStreams();
224 
225         for ( DeployedResource deployedResource : deployedResources )
226         {
227             if ( StringUtils.equalsIgnoreCase( "chunked", deployedResource.transferEncoding ) )
228             {
229                 fail( "deployedResource " + deployedResource
230                           + " use chuncked transfert encoding some http server doesn't support that" );
231             }
232         }
233 
234         assertTrue( deployed );
235     }
236 }