]> source.dussan.org Git - archiva.git/blob
4949e55a7b18de5cf70b8ef3fe0313caee3174dd
[archiva.git] /
1 package org.apache.archiva.rest.v2.svc;
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  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  */
20
21 import com.fasterxml.jackson.databind.DeserializationFeature;
22 import com.fasterxml.jackson.databind.ObjectMapper;
23 import com.fasterxml.jackson.databind.PropertyNamingStrategy;
24 import io.restassured.RestAssured;
25 import io.restassured.builder.RequestSpecBuilder;
26 import io.restassured.config.ObjectMapperConfig;
27 import io.restassured.config.RestAssuredConfig;
28 import io.restassured.path.json.mapper.factory.Jackson2ObjectMapperFactory;
29 import io.restassured.response.Response;
30 import io.restassured.specification.RequestSpecification;
31 import org.apache.archiva.redback.integration.security.role.RedbackRoleConstants;
32 import org.apache.archiva.redback.rest.services.BaseSetup;
33 import org.apache.archiva.redback.role.RoleManager;
34 import org.apache.archiva.redback.role.RoleManagerException;
35 import org.apache.archiva.redback.users.User;
36 import org.apache.archiva.redback.users.UserManager;
37 import org.apache.archiva.redback.users.UserManagerException;
38 import org.apache.archiva.redback.users.UserNotFoundException;
39 import org.apache.commons.lang3.StringUtils;
40 import org.apache.commons.lang3.SystemUtils;
41 import org.apache.cxf.transport.servlet.CXFServlet;
42 import org.eclipse.jetty.server.HttpConnectionFactory;
43 import org.eclipse.jetty.server.Server;
44 import org.eclipse.jetty.server.ServerConnector;
45 import org.eclipse.jetty.server.session.SessionHandler;
46 import org.eclipse.jetty.servlet.ServletContextHandler;
47 import org.eclipse.jetty.servlet.ServletHolder;
48 import org.junit.jupiter.api.Tag;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51 import org.springframework.web.context.ContextLoaderListener;
52
53 import java.lang.reflect.Type;
54 import java.nio.file.Files;
55 import java.nio.file.Path;
56 import java.nio.file.Paths;
57 import java.time.LocalTime;
58 import java.util.HashMap;
59 import java.util.Map;
60 import java.util.concurrent.atomic.AtomicInteger;
61 import java.util.concurrent.atomic.AtomicReference;
62
63 import static io.restassured.RestAssured.*;
64 import static io.restassured.http.ContentType.JSON;
65 import static org.junit.jupiter.api.Assertions.assertNotNull;
66
67 /**
68  * Native REST tests do not use the JAX-RS client and can be used with a remote
69  * REST API service. The tests
70  *
71  * @author Martin Stockhammer <martin_s@apache.org>
72  */
73 @Tag( "rest-native" )
74 @Tag( "rest-v2" )
75 public abstract class AbstractNativeRestServices
76 {
77     private AtomicReference<Path> projectDir = new AtomicReference<>();
78     private AtomicReference<Path> appServerBase = new AtomicReference<>( );
79     private AtomicReference<Path> basePath = new AtomicReference<>( );
80
81     public static final int STOPPED = 0;
82     public static final int STOPPING = 1;
83     public static final int STARTING = 2;
84     public static final int STARTED = 3;
85     public static final int ERROR = 4;
86     private final boolean startServer;
87     private final String serverPort;
88     private final String baseUri;
89
90     private RequestSpecification requestSpec;
91     protected Logger log = LoggerFactory.getLogger( getClass( ) );
92
93     private static AtomicReference<Server> server = new AtomicReference<>( );
94     private static AtomicReference<ServerConnector> serverConnector = new AtomicReference<>( );
95     private static AtomicInteger serverStarted = new AtomicInteger( STOPPED );
96     private UserManager userManager;
97     private RoleManager roleManager;
98
99     private final boolean remoteService;
100
101     private String adminToken;
102     private String adminRefreshToken;
103
104
105     public AbstractNativeRestServices( )
106     {
107         this.startServer = BaseSetup.startServer( );
108         this.serverPort = BaseSetup.getServerPort( );
109         this.baseUri = BaseSetup.getBaseUri( );
110
111         if ( startServer )
112         {
113             this.remoteService = false;
114         } else {
115             this.remoteService = true;
116         }
117     }
118
119     protected abstract String getServicePath( );
120
121     protected String getSpringConfigLocation( )
122     {
123         return "classpath*:META-INF/spring-context.xml,classpath:META-INF/spring-context-native-test.xml";
124     }
125
126     protected RequestSpecification getRequestSpec( )
127     {
128         return this.requestSpec;
129     }
130
131     protected String getContextRoot( )
132     {
133         return "/api";
134     }
135
136
137     protected String getServiceBasePath( )
138     {
139         return "/v2/archiva";
140     }
141
142     protected String getRedbackServiceBasePath( )
143     {
144         return "/v2/redback";
145     }
146
147
148     protected String getBasePath( )
149     {
150         return new StringBuilder( )
151             .append( getContextRoot( ) )
152             .append( getServiceBasePath( ) )
153             .append( getServicePath( ) ).toString( );
154     }
155
156     /**
157      * Returns the server that was started, or null if not initialized before.
158      *
159      * @return
160      */
161     public Server getServer( )
162     {
163         return this.server.get( );
164     }
165
166     public int getServerPort( )
167     {
168         ServerConnector connector = serverConnector.get( );
169         if ( connector != null )
170         {
171             return connector.getLocalPort( );
172         }
173         else
174         {
175             return 0;
176         }
177     }
178
179     /**
180      * Returns true, if the server does exist and is running.
181      *
182      * @return true, if server does exist and is running.
183      */
184     public boolean isServerRunning( )
185     {
186         return serverStarted.get( ) == STARTED && this.server.get( ) != null && this.server.get( ).isRunning( );
187     }
188
189     private UserManager getUserManager( )
190     {
191         if ( this.userManager == null )
192         {
193             UserManager userManager = ContextLoaderListener.getCurrentWebApplicationContext( )
194                 .getBean( "userManager#default", UserManager.class );
195             assertNotNull( userManager );
196             this.userManager = userManager;
197         }
198         return this.userManager;
199     }
200
201     private RoleManager getRoleManager( )
202     {
203         if ( this.roleManager == null )
204         {
205             RoleManager roleManager = ContextLoaderListener.getCurrentWebApplicationContext( )
206                 .getBean( "roleManager", RoleManager.class );
207             assertNotNull( roleManager );
208             this.roleManager = roleManager;
209         }
210         return this.roleManager;
211     }
212
213     protected String getAdminPwd( )
214     {
215         return BaseSetup.getAdminPwd( );
216     }
217
218     protected String getAdminUser( )
219     {
220         return RedbackRoleConstants.ADMINISTRATOR_ACCOUNT_NAME;
221     }
222
223     private void setupAdminUser( ) throws UserManagerException, RoleManagerException
224     {
225
226         UserManager um = getUserManager( );
227
228         User adminUser = null;
229         try
230         {
231             adminUser = um.findUser( getAdminUser( ) );
232         }
233         catch ( UserNotFoundException e )
234         {
235             // ignore
236         }
237         adminUser = um.createUser( getAdminUser( ), "Administrator", "admin@local.home" );
238         adminUser.setUsername( getAdminUser( ) );
239         adminUser.setPassword( getAdminPwd( ) );
240         adminUser.setFullName( "the admin user" );
241         adminUser.setEmail( "toto@toto.fr" );
242         adminUser.setPermanent( true );
243         adminUser.setValidated( true );
244         adminUser.setLocked( false );
245         adminUser.setPasswordChangeRequired( false );
246         if ( adminUser == null )
247         {
248             um.addUser( adminUser );
249         }
250         else
251         {
252             um.updateUser( adminUser, false );
253         }
254         getRoleManager( ).assignRole( "system-administrator", adminUser.getUsername( ) );
255     }
256
257     protected Path getProjectDirectory() {
258         if ( projectDir.get()==null) {
259             String propVal = System.getProperty("mvn.project.base.dir");
260             Path newVal;
261             if (StringUtils.isEmpty(propVal)) {
262                 newVal = Paths.get("").toAbsolutePath();
263             } else {
264                 newVal = Paths.get(propVal).toAbsolutePath();
265             }
266             projectDir.compareAndSet(null, newVal);
267         }
268         return projectDir.get();
269     }
270
271     public Path getBasedir()
272     {
273         if (basePath.get()==null) {
274             String baseDir = System.getProperty( "basedir" );
275             final Path baseDirPath;
276             if (StringUtils.isNotEmpty( baseDir ))  {
277                 baseDirPath = Paths.get( baseDir );
278             } else {
279                 baseDirPath = getProjectDirectory( );
280             }
281             basePath.compareAndSet( null, baseDirPath );
282         }
283         return basePath.get( );
284     }
285
286     Path getAppserverBase() {
287         if (appServerBase.get()==null) {
288             String basePath = System.getProperty( "appserver.base" );
289             final Path appserverPath;
290             if (StringUtils.isNotEmpty( basePath )) {
291                 appserverPath = Paths.get( basePath ).toAbsolutePath( );
292             } else {
293                 appserverPath = getBasedir( ).resolve( "target" ).resolve( "appserver-base-" + LocalTime.now( ).toSecondOfDay( ) );
294             }
295             appServerBase.compareAndSet( null, appserverPath );
296         }
297         return appServerBase.get();
298     }
299
300     private void removeAppsubFolder( Path appServerBase, String folder )
301         throws Exception
302     {
303         Path directory = appServerBase.resolve( folder );
304         if ( Files.exists(directory) )
305         {
306             org.apache.archiva.common.utils.FileUtils.deleteDirectory( directory );
307         }
308     }
309
310     public void startServer( )
311         throws Exception
312     {
313         if ( serverStarted.compareAndSet( STOPPED, STARTING ) )
314         {
315             try
316             {
317                 log.info( "Starting server" );
318                 Path appServerBase = getAppserverBase( );
319
320                 removeAppsubFolder(appServerBase, "jcr");
321                 removeAppsubFolder(appServerBase, "conf");
322                 removeAppsubFolder(appServerBase, "data");
323
324
325                 Server myServer = new Server( );
326                 this.server.set( myServer );
327                 this.serverConnector.set( new ServerConnector( myServer, new HttpConnectionFactory( ) ) );
328                 myServer.addConnector( serverConnector.get( ) );
329
330                 ServletHolder servletHolder = new ServletHolder( new CXFServlet( ) );
331                 ServletContextHandler context = new ServletContextHandler( ServletContextHandler.SESSIONS );
332                 context.setResourceBase( SystemUtils.JAVA_IO_TMPDIR );
333                 context.setSessionHandler( new SessionHandler( ) );
334                 context.addServlet( servletHolder, getContextRoot( ) + "/*" );
335                 context.setInitParameter( "contextConfigLocation", getSpringConfigLocation( ) );
336                 context.addEventListener( new ContextLoaderListener( ) );
337
338                 getServer( ).setHandler( context );
339                 getServer( ).start( );
340
341                 if ( log.isDebugEnabled( ) )
342                 {
343                     log.debug( "Jetty dump: {}", getServer( ).dump( ) );
344                 }
345
346                 setupAdminUser( );
347                 log.info( "Started server on port {}", getServerPort( ) );
348                 serverStarted.set( STARTED );
349             }
350             finally
351             {
352                 // In case, if the last statement was not reached
353                 serverStarted.compareAndSet( STARTING, ERROR );
354             }
355         }
356
357     }
358
359     public void stopServer( )
360         throws Exception
361     {
362         if ( this.serverStarted.compareAndSet( STARTED, STOPPING ) )
363         {
364             try
365             {
366                 final Server myServer = getServer( );
367                 if ( myServer != null )
368                 {
369                     log.info( "Stopping server" );
370                     myServer.stop( );
371                 }
372                 serverStarted.set( STOPPED );
373             }
374             finally
375             {
376                 serverStarted.compareAndSet( STOPPING, ERROR );
377             }
378         }
379         else
380         {
381             log.error( "Server is not in STARTED state!" );
382         }
383     }
384
385
386     protected void setupNative( ) throws Exception
387     {
388         if ( this.startServer )
389         {
390             startServer( );
391         }
392
393         if ( StringUtils.isNotEmpty( serverPort ) )
394         {
395             RestAssured.port = Integer.parseInt( serverPort );
396         }
397         else
398         {
399             RestAssured.port = getServerPort( );
400         }
401         if ( StringUtils.isNotEmpty( baseUri ) )
402         {
403             RestAssured.baseURI = baseUri;
404         }
405         else
406         {
407             RestAssured.baseURI = "http://localhost";
408         }
409         String basePath = getBasePath( );
410         this.requestSpec = getRequestSpecBuilder( ).build( );
411         RestAssured.basePath = basePath;
412         RestAssured.config = RestAssuredConfig.config().objectMapperConfig(new ObjectMapperConfig().jackson2ObjectMapperFactory(
413             ( cls, charset ) -> {
414                 ObjectMapper om = new ObjectMapper().findAndRegisterModules();
415                 om.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
416                 om.setPropertyNamingStrategy( PropertyNamingStrategy.SNAKE_CASE );
417                 return om;
418             }
419         ));
420     }
421
422     protected RequestSpecBuilder getRequestSpecBuilder( ) {
423         return getRequestSpecBuilder( null );
424     }
425
426     protected RequestSpecBuilder getRequestSpecBuilder( String basePath )
427     {
428         String myBasePath = basePath == null ? getBasePath( ) : basePath;
429         return new RequestSpecBuilder( ).setBaseUri( baseURI )
430             .setPort( port )
431             .setBasePath( myBasePath )
432             .addHeader( "Origin", RestAssured.baseURI + ":" + RestAssured.port );
433     }
434
435     protected RequestSpecBuilder getAuthRequestSpecBuilder( )
436     {
437         return new RequestSpecBuilder( ).setBaseUri( baseURI )
438             .setPort( port )
439             .setBasePath( new StringBuilder( )
440                 .append( getContextRoot( ) )
441                 .append( getRedbackServiceBasePath() ).append("/auth").toString() )
442             .addHeader( "Origin", RestAssured.baseURI + ":" + RestAssured.port );
443     }
444
445     protected RequestSpecification getRequestSpec( String bearerToken )
446     {
447         return getRequestSpecBuilder( ).addHeader( "Authorization", "Bearer " + bearerToken ).build( );
448     }
449
450     protected RequestSpecification getRequestSpec( String bearerToken, String path)
451     {
452         return getRequestSpecBuilder( path  ).addHeader( "Authorization", "Bearer " + bearerToken ).build( );
453     }
454
455     protected void shutdownNative( ) throws Exception
456     {
457         if (startServer)
458         {
459             stopServer( );
460         }
461     }
462
463     protected org.apache.archiva.redback.rest.api.model.User addRemoteUser(String userid, String password, String fullName, String mail) {
464
465         return null;
466     }
467
468     protected void initAdminToken() {
469         Map<String, Object> jsonAsMap = new HashMap<>();
470         jsonAsMap.put( "grant_type", "authorization_code" );
471         jsonAsMap.put("user_id", getAdminUser());
472         jsonAsMap.put("password", getAdminPwd() );
473         Response result = given( ).spec( getAuthRequestSpecBuilder().build() )
474             .contentType( JSON )
475             .body( jsonAsMap )
476             .when( ).post( "/authenticate").then( ).statusCode( 200 )
477             .extract( ).response( );
478         this.adminToken = result.body( ).jsonPath( ).getString( "access_token" );
479         this.adminRefreshToken = result.body( ).jsonPath( ).getString( "refresh_token" );
480     }
481
482     protected String getUserToken(String userId, String password) {
483         Map<String, Object> jsonAsMap = new HashMap<>();
484         jsonAsMap.put( "grant_type", "authorization_code" );
485         jsonAsMap.put("user_id", userId);
486         jsonAsMap.put("password", password );
487         Response result = given( ).spec( getAuthRequestSpecBuilder().build() )
488             .contentType( JSON )
489             .body( jsonAsMap )
490             .when( ).post( "/authenticate").then( ).statusCode( 200 )
491             .extract( ).response( );
492         return result.body( ).jsonPath( ).getString( "access_token" );
493     }
494     protected String getAdminToken()  {
495         if (this.adminToken == null) {
496             initAdminToken();
497         }
498         return this.adminToken;
499     }
500
501
502     protected String getAdminRefreshToken()  {
503         if (this.adminRefreshToken == null) {
504             initAdminToken();
505         }
506         return this.adminRefreshToken;
507     }
508
509     public boolean isRemoteService() {
510         return this.remoteService;
511     }
512 }