1 package org.apache.archiva.rest.services.v2;
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
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
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;
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;
60 import java.util.concurrent.atomic.AtomicInteger;
61 import java.util.concurrent.atomic.AtomicReference;
63 import static io.restassured.RestAssured.*;
64 import static io.restassured.http.ContentType.JSON;
65 import static org.junit.jupiter.api.Assertions.assertNotNull;
68 * Native REST tests do not use the JAX-RS client and can be used with a remote
69 * REST API service. The tests
71 * @author Martin Stockhammer <martin_s@apache.org>
75 public abstract class AbstractNativeRestServices
77 private AtomicReference<Path> projectDir = new AtomicReference<>();
78 private AtomicReference<Path> appServerBase = new AtomicReference<>( );
79 private AtomicReference<Path> basePath = new AtomicReference<>( );
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;
90 private RequestSpecification requestSpec;
91 protected Logger log = LoggerFactory.getLogger( getClass( ) );
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;
99 private final boolean remoteService;
101 private String adminToken;
102 private String adminRefreshToken;
105 public AbstractNativeRestServices( )
107 this.startServer = BaseSetup.startServer( );
108 this.serverPort = BaseSetup.getServerPort( );
109 this.baseUri = BaseSetup.getBaseUri( );
113 this.remoteService = false;
115 this.remoteService = true;
119 protected abstract String getServicePath( );
121 protected String getSpringConfigLocation( )
123 return "classpath*:META-INF/spring-context.xml,classpath:META-INF/spring-context-native-test.xml";
126 protected RequestSpecification getRequestSpec( )
128 return this.requestSpec;
131 protected String getContextRoot( )
137 protected String getServiceBasePath( )
139 return "/v2/archiva";
142 protected String getRedbackServiceBasePath( )
144 return "/v2/redback";
148 protected String getBasePath( )
150 return new StringBuilder( )
151 .append( getContextRoot( ) )
152 .append( getServiceBasePath( ) )
153 .append( getServicePath( ) ).toString( );
157 * Returns the server that was started, or null if not initialized before.
161 public Server getServer( )
163 return this.server.get( );
166 public int getServerPort( )
168 ServerConnector connector = serverConnector.get( );
169 if ( connector != null )
171 return connector.getLocalPort( );
180 * Returns true, if the server does exist and is running.
182 * @return true, if server does exist and is running.
184 public boolean isServerRunning( )
186 return serverStarted.get( ) == STARTED && this.server.get( ) != null && this.server.get( ).isRunning( );
189 private UserManager getUserManager( )
191 if ( this.userManager == null )
193 UserManager userManager = ContextLoaderListener.getCurrentWebApplicationContext( )
194 .getBean( "userManager#default", UserManager.class );
195 assertNotNull( userManager );
196 this.userManager = userManager;
198 return this.userManager;
201 private RoleManager getRoleManager( )
203 if ( this.roleManager == null )
205 RoleManager roleManager = ContextLoaderListener.getCurrentWebApplicationContext( )
206 .getBean( "roleManager", RoleManager.class );
207 assertNotNull( roleManager );
208 this.roleManager = roleManager;
210 return this.roleManager;
213 protected String getAdminPwd( )
215 return BaseSetup.getAdminPwd( );
218 protected String getAdminUser( )
220 return RedbackRoleConstants.ADMINISTRATOR_ACCOUNT_NAME;
223 private void setupAdminUser( ) throws UserManagerException, RoleManagerException
226 UserManager um = getUserManager( );
228 User adminUser = null;
231 adminUser = um.findUser( getAdminUser( ) );
233 catch ( UserNotFoundException e )
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 )
248 um.addUser( adminUser );
252 um.updateUser( adminUser, false );
254 getRoleManager( ).assignRole( "system-administrator", adminUser.getUsername( ) );
257 protected Path getProjectDirectory() {
258 if ( projectDir.get()==null) {
259 String propVal = System.getProperty("mvn.project.base.dir");
261 if (StringUtils.isEmpty(propVal)) {
262 newVal = Paths.get("").toAbsolutePath();
264 newVal = Paths.get(propVal).toAbsolutePath();
266 projectDir.compareAndSet(null, newVal);
268 return projectDir.get();
271 public Path getBasedir()
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 );
279 baseDirPath = getProjectDirectory( );
281 basePath.compareAndSet( null, baseDirPath );
283 return basePath.get( );
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( );
293 appserverPath = getBasedir( ).resolve( "target" ).resolve( "appserver-base-" + LocalTime.now( ).toSecondOfDay( ) );
295 appServerBase.compareAndSet( null, appserverPath );
297 return appServerBase.get();
300 private void removeAppsubFolder( Path appServerBase, String folder )
303 Path directory = appServerBase.resolve( folder );
304 if ( Files.exists(directory) )
306 org.apache.archiva.common.utils.FileUtils.deleteDirectory( directory );
310 public void startServer( )
313 if ( serverStarted.compareAndSet( STOPPED, STARTING ) )
317 log.info( "Starting server" );
318 Path appServerBase = getAppserverBase( );
320 removeAppsubFolder(appServerBase, "jcr");
321 removeAppsubFolder(appServerBase, "conf");
322 removeAppsubFolder(appServerBase, "data");
325 Server myServer = new Server( );
326 this.server.set( myServer );
327 this.serverConnector.set( new ServerConnector( myServer, new HttpConnectionFactory( ) ) );
328 myServer.addConnector( serverConnector.get( ) );
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( ) );
338 getServer( ).setHandler( context );
339 getServer( ).start( );
341 if ( log.isDebugEnabled( ) )
343 log.debug( "Jetty dump: {}", getServer( ).dump( ) );
347 log.info( "Started server on port {}", getServerPort( ) );
348 serverStarted.set( STARTED );
352 // In case, if the last statement was not reached
353 serverStarted.compareAndSet( STARTING, ERROR );
359 public void stopServer( )
362 if ( this.serverStarted.compareAndSet( STARTED, STOPPING ) )
366 final Server myServer = getServer( );
367 if ( myServer != null )
369 log.info( "Stopping server" );
372 serverStarted.set( STOPPED );
376 serverStarted.compareAndSet( STOPPING, ERROR );
381 log.error( "Server is not in STARTED state!" );
386 protected void setupNative( ) throws Exception
388 if ( this.startServer )
393 if ( StringUtils.isNotEmpty( serverPort ) )
395 RestAssured.port = Integer.parseInt( serverPort );
399 RestAssured.port = getServerPort( );
401 if ( StringUtils.isNotEmpty( baseUri ) )
403 RestAssured.baseURI = baseUri;
407 RestAssured.baseURI = "http://localhost";
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 );
422 protected RequestSpecBuilder getRequestSpecBuilder( ) {
423 return getRequestSpecBuilder( null );
426 protected RequestSpecBuilder getRequestSpecBuilder( String basePath )
428 String myBasePath = basePath == null ? getBasePath( ) : basePath;
429 return new RequestSpecBuilder( ).setBaseUri( baseURI )
431 .setBasePath( myBasePath )
432 .addHeader( "Origin", RestAssured.baseURI + ":" + RestAssured.port );
435 protected RequestSpecBuilder getAuthRequestSpecBuilder( )
437 return new RequestSpecBuilder( ).setBaseUri( baseURI )
439 .setBasePath( new StringBuilder( )
440 .append( getContextRoot( ) )
441 .append( getRedbackServiceBasePath() ).append("/auth").toString() )
442 .addHeader( "Origin", RestAssured.baseURI + ":" + RestAssured.port );
445 protected RequestSpecification getRequestSpec( String bearerToken )
447 return getRequestSpecBuilder( ).addHeader( "Authorization", "Bearer " + bearerToken ).build( );
450 protected RequestSpecification getRequestSpec( String bearerToken, String path)
452 return getRequestSpecBuilder( path ).addHeader( "Authorization", "Bearer " + bearerToken ).build( );
455 protected void shutdownNative( ) throws Exception
463 protected org.apache.archiva.redback.rest.api.model.User addRemoteUser(String userid, String password, String fullName, String mail) {
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() )
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" );
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() )
490 .when( ).post( "/authenticate").prettyPeek().then( ).statusCode( 200 )
491 .extract( ).response( );
492 result.getBody( ).prettyPrint( );
493 return result.body( ).jsonPath( ).getString( "access_token" );
495 protected String getAdminToken() {
496 if (this.adminToken == null) {
499 return this.adminToken;
503 protected String getAdminRefreshToken() {
504 if (this.adminRefreshToken == null) {
507 return this.adminRefreshToken;
510 public boolean isRemoteService() {
511 return this.remoteService;