001package org.apache.archiva.rest.services; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.admin.model.AuditInformation; 023import org.apache.archiva.admin.model.RepositoryAdminException; 024import org.apache.archiva.admin.model.admin.ArchivaAdministration; 025import org.apache.archiva.admin.model.beans.ProxyConnector; 026import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin; 027import org.apache.archiva.admin.model.proxyconnector.ProxyConnectorAdmin; 028import org.apache.archiva.metadata.model.facets.AuditEvent; 029import org.apache.archiva.repository.events.AuditListener; 030import org.apache.archiva.common.utils.VersionUtil; 031import org.apache.archiva.indexer.search.SearchResultHit; 032import org.apache.archiva.maven2.model.Artifact; 033import org.apache.archiva.metadata.model.ArtifactMetadata; 034import org.apache.archiva.metadata.repository.RepositorySessionFactory; 035import org.apache.archiva.redback.components.taskqueue.TaskQueueException; 036import org.apache.archiva.redback.configuration.UserConfiguration; 037import org.apache.archiva.redback.configuration.UserConfigurationKeys; 038import org.apache.archiva.redback.rest.services.RedbackAuthenticationThreadLocal; 039import org.apache.archiva.redback.rest.services.RedbackRequestInformation; 040import org.apache.archiva.redback.users.User; 041import org.apache.archiva.repository.RepositoryContentFactory; 042import org.apache.archiva.repository.RepositoryException; 043import org.apache.archiva.rest.api.services.ArchivaRestServiceException; 044import org.apache.archiva.rest.services.utils.ArtifactBuilder; 045import org.apache.archiva.scheduler.repository.DefaultRepositoryArchivaTaskScheduler; 046import org.apache.archiva.scheduler.repository.model.RepositoryArchivaTaskScheduler; 047import org.apache.archiva.scheduler.repository.model.RepositoryTask; 048import org.apache.archiva.security.AccessDeniedException; 049import org.apache.archiva.security.ArchivaSecurityException; 050import org.apache.archiva.security.PrincipalNotFoundException; 051import org.apache.archiva.security.UserRepositories; 052import org.apache.commons.lang.StringUtils; 053import org.modelmapper.ModelMapper; 054import org.modelmapper.PropertyMap; 055import org.modelmapper.convention.MatchingStrategies; 056import org.slf4j.Logger; 057import org.slf4j.LoggerFactory; 058import org.springframework.context.ApplicationContext; 059 060import javax.inject.Inject; 061import javax.inject.Named; 062import javax.servlet.http.HttpServletRequest; 063import javax.servlet.http.HttpServletResponse; 064import javax.ws.rs.core.Context; 065import javax.ws.rs.core.Response; 066 067import java.util.ArrayList; 068import java.util.Collection; 069import java.util.Collections; 070import java.util.HashMap; 071import java.util.List; 072import java.util.Map; 073 074/** 075 * abstract class with common utilities methods 076 * 077 * @author Olivier Lamy 078 * @since 1.4-M1 079 */ 080public abstract class AbstractRestService 081{ 082 083 protected final Logger log = LoggerFactory.getLogger( getClass() ); 084 085 @Inject 086 private List<AuditListener> auditListeners = new ArrayList<>(); 087 088 @Inject 089 protected UserRepositories userRepositories; 090 091 092 /** 093 * FIXME: this could be multiple implementations and needs to be configured. 094 */ 095 @Inject 096 @Named(value = "repositorySessionFactory") 097 protected RepositorySessionFactory repositorySessionFactory; 098 099 @Inject 100 protected ArchivaAdministration archivaAdministration; 101 102 @Inject 103 protected ProxyConnectorAdmin proxyConnectorAdmin; 104 105 @Inject 106 protected ManagedRepositoryAdmin managedRepositoryAdmin; 107 108 @Inject 109 protected RepositoryContentFactory repositoryContentFactory; 110 111 @Inject 112 @Named(value = "archivaTaskScheduler#repository") 113 protected RepositoryArchivaTaskScheduler repositoryTaskScheduler; 114 115 116 @Inject 117 @Named(value = "userConfiguration#default") 118 protected UserConfiguration config; 119 120 @Context 121 protected HttpServletRequest httpServletRequest; 122 123 @Context 124 protected HttpServletResponse httpServletResponse; 125 126 protected AuditInformation getAuditInformation() 127 { 128 RedbackRequestInformation redbackRequestInformation = RedbackAuthenticationThreadLocal.get(); 129 User user = redbackRequestInformation == null ? null : redbackRequestInformation.getUser(); 130 String remoteAddr = redbackRequestInformation == null ? null : redbackRequestInformation.getRemoteAddr(); 131 return new AuditInformation( user, remoteAddr ); 132 } 133 134 public List<AuditListener> getAuditListeners() 135 { 136 return auditListeners; 137 } 138 139 public void setAuditListeners( List<AuditListener> auditListeners ) 140 { 141 this.auditListeners = auditListeners; 142 } 143 144 protected List<String> getObservableRepos() 145 { 146 try 147 { 148 List<String> ids = userRepositories.getObservableRepositoryIds( getPrincipal() ); 149 return ids == null ? Collections.<String>emptyList() : ids; 150 } 151 catch ( PrincipalNotFoundException e ) 152 { 153 log.warn( e.getMessage(), e ); 154 } 155 catch ( AccessDeniedException e ) 156 { 157 log.warn( e.getMessage(), e ); 158 } 159 catch ( ArchivaSecurityException e ) 160 { 161 log.warn( e.getMessage(), e ); 162 } 163 return Collections.emptyList(); 164 } 165 166 protected String getPrincipal() 167 { 168 RedbackRequestInformation redbackRequestInformation = RedbackAuthenticationThreadLocal.get(); 169 170 return redbackRequestInformation == null 171 ? config.getString( UserConfigurationKeys.DEFAULT_GUEST ) 172 : ( redbackRequestInformation.getUser() == null 173 ? config.getString( UserConfigurationKeys.DEFAULT_GUEST ) 174 : redbackRequestInformation.getUser().getUsername() ); 175 } 176 177 protected String getBaseUrl() 178 throws RepositoryAdminException 179 { 180 String applicationUrl = archivaAdministration.getUiConfiguration().getApplicationUrl(); 181 if ( StringUtils.isNotBlank( applicationUrl ) ) 182 { 183 return applicationUrl; 184 } 185 return httpServletRequest.getScheme() + "://" + httpServletRequest.getServerName() + ( 186 httpServletRequest.getServerPort() == 80 ? "" : ":" + httpServletRequest.getServerPort() ) 187 + httpServletRequest.getContextPath(); 188 } 189 190 protected <T> Map<String, T> getBeansOfType( ApplicationContext applicationContext, Class<T> clazz ) 191 { 192 //TODO do some caching here !!! 193 // olamy : with plexus we get only roleHint 194 // as per convention we named spring bean role#hint remove role# if exists 195 Map<String, T> springBeans = applicationContext.getBeansOfType( clazz ); 196 197 Map<String, T> beans = new HashMap<>( springBeans.size() ); 198 199 for ( Map.Entry<String, T> entry : springBeans.entrySet() ) 200 { 201 String key = StringUtils.contains( entry.getKey(), '#' ) 202 ? StringUtils.substringAfterLast( entry.getKey(), "#" ) 203 : entry.getKey(); 204 beans.put( key, entry.getValue() ); 205 } 206 return beans; 207 } 208 209 protected void triggerAuditEvent( String repositoryId, String filePath, String action ) 210 { 211 AuditEvent auditEvent = new AuditEvent( repositoryId, getPrincipal(), filePath, action ); 212 AuditInformation auditInformation = getAuditInformation(); 213 auditEvent.setUserId( auditInformation.getUser() == null ? "" : auditInformation.getUser().getUsername() ); 214 auditEvent.setRemoteIP( auditInformation.getRemoteAddr() ); 215 for ( AuditListener auditListener : getAuditListeners() ) 216 { 217 auditListener.auditEvent( auditEvent ); 218 } 219 } 220 221 /** 222 * @param artifact 223 * @return 224 */ 225 protected String getArtifactUrl( Artifact artifact ) 226 throws ArchivaRestServiceException 227 { 228 return getArtifactUrl( artifact, null ); 229 } 230 231 232 protected String getArtifactUrl( Artifact artifact, String repositoryId ) 233 throws ArchivaRestServiceException 234 { 235 try 236 { 237 238 if ( httpServletRequest == null ) 239 { 240 return null; 241 } 242 243 StringBuilder sb = new StringBuilder( getBaseUrl() ); 244 245 sb.append( "/repository" ); 246 247 // when artifact come from a remote repository when have here the remote repo id 248 // we must replace it with a valid managed one available for the user. 249 if ( StringUtils.isEmpty( repositoryId ) ) 250 { 251 List<String> userRepos = userRepositories.getObservableRepositoryIds( getPrincipal() ); 252 // is it a good one? if yes nothing to 253 // if not search the repo who is proxy for this remote 254 if ( !userRepos.contains( artifact.getContext() ) ) 255 { 256 for ( Map.Entry<String, List<ProxyConnector>> entry : proxyConnectorAdmin.getProxyConnectorAsMap().entrySet() ) 257 { 258 for ( ProxyConnector proxyConnector : entry.getValue() ) 259 { 260 if ( StringUtils.equals( "remote-" + proxyConnector.getTargetRepoId(), 261 artifact.getContext() ) // 262 && userRepos.contains( entry.getKey() ) ) 263 { 264 sb.append( '/' ).append( entry.getKey() ); 265 } 266 } 267 } 268 269 } 270 else 271 { 272 sb.append( '/' ).append( artifact.getContext() ); 273 } 274 275 276 } 277 else 278 { 279 sb.append( '/' ).append( repositoryId ); 280 } 281 282 sb.append( '/' ).append( StringUtils.replaceChars( artifact.getGroupId(), '.', '/' ) ); 283 sb.append( '/' ).append( artifact.getArtifactId() ); 284 if ( VersionUtil.isSnapshot( artifact.getVersion() ) ) 285 { 286 sb.append( '/' ).append( VersionUtil.getBaseVersion( artifact.getVersion() ) ); 287 } 288 else 289 { 290 sb.append( '/' ).append( artifact.getVersion() ); 291 } 292 sb.append( '/' ).append( artifact.getArtifactId() ); 293 sb.append( '-' ).append( artifact.getVersion() ); 294 if ( StringUtils.isNotBlank( artifact.getClassifier() ) ) 295 { 296 sb.append( '-' ).append( artifact.getClassifier() ); 297 } 298 // maven-plugin packaging is a jar 299 if ( StringUtils.equals( "maven-plugin", artifact.getPackaging() ) ) 300 { 301 sb.append( "jar" ); 302 } 303 else 304 { 305 sb.append( '.' ).append( artifact.getFileExtension() ); 306 } 307 308 return sb.toString(); 309 } 310 catch ( Exception e ) 311 { 312 throw new ArchivaRestServiceException( e.getMessage(), 313 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 314 } 315 } 316 317 protected List<Artifact> buildArtifacts( Collection<ArtifactMetadata> artifactMetadatas, String repositoryId ) 318 throws ArchivaRestServiceException 319 { 320 try 321 { 322 if ( artifactMetadatas != null && !artifactMetadatas.isEmpty() ) 323 { 324 List<Artifact> artifacts = new ArrayList<>( artifactMetadatas.size() ); 325 for ( ArtifactMetadata artifact : artifactMetadatas ) 326 { 327 328 String repoId = repositoryId != null ? repositoryId : artifact.getRepositoryId(); 329 if ( repoId == null ) { 330 throw new IllegalStateException( "Repository Id is null" ); 331 } 332 333 ArtifactBuilder builder = 334 new ArtifactBuilder().forArtifactMetadata( artifact ).withManagedRepositoryContent( 335 repositoryContentFactory.getManagedRepositoryContent( repoId ) ); 336 Artifact art = builder.build(); 337 art.setUrl( getArtifactUrl( art, repositoryId ) ); 338 artifacts.add( art ); 339 } 340 return artifacts; 341 } 342 return Collections.emptyList(); 343 } 344 catch ( RepositoryException e ) 345 { 346 log.error( e.getMessage(), e ); 347 throw new ArchivaRestServiceException( e.getMessage(), 348 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 349 } 350 } 351 352 protected Boolean doScanRepository( String repositoryId, boolean fullScan ) 353 { 354 if ( repositoryTaskScheduler.isProcessingRepositoryTask( repositoryId ) ) 355 { 356 log.info( "scanning of repository with id {} already scheduled", repositoryId ); 357 return Boolean.FALSE; 358 } 359 RepositoryTask task = new RepositoryTask(); 360 task.setRepositoryId( repositoryId ); 361 task.setScanAll( fullScan ); 362 try 363 { 364 repositoryTaskScheduler.queueTask( task ); 365 } 366 catch ( TaskQueueException e ) 367 { 368 log.error( "failed to schedule scanning of repo with id {}", repositoryId, e ); 369 return false; 370 } 371 return true; 372 } 373 374 private static class ModelMapperHolder 375 { 376 private static ModelMapper MODEL_MAPPER = new ModelMapper(); 377 378 static 379 { 380 MODEL_MAPPER.addMappings( new SearchResultHitMap() ); 381 MODEL_MAPPER.getConfiguration().setMatchingStrategy( MatchingStrategies.STRICT ); 382 } 383 } 384 385 386 private static class SearchResultHitMap 387 extends PropertyMap<SearchResultHit, Artifact> 388 { 389 @Override 390 protected void configure() 391 { 392 skip().setId( null ); 393 } 394 } 395 396 ; 397 398 protected ModelMapper getModelMapper() 399 { 400 return ModelMapperHolder.MODEL_MAPPER; 401 } 402}