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.proxyconnector.ProxyConnectorAdmin; 027import org.apache.archiva.common.utils.VersionUtil; 028import org.apache.archiva.indexer.search.SearchResultHit; 029import org.apache.archiva.maven2.model.Artifact; 030import org.apache.archiva.metadata.model.ArtifactMetadata; 031import org.apache.archiva.metadata.model.facets.AuditEvent; 032import org.apache.archiva.metadata.repository.RepositorySessionFactory; 033import org.apache.archiva.components.taskqueue.TaskQueueException; 034import org.apache.archiva.redback.configuration.UserConfiguration; 035import org.apache.archiva.redback.configuration.UserConfigurationKeys; 036import org.apache.archiva.redback.rest.services.RedbackAuthenticationThreadLocal; 037import org.apache.archiva.redback.rest.services.RedbackRequestInformation; 038import org.apache.archiva.redback.users.User; 039import org.apache.archiva.repository.ManagedRepository; 040import org.apache.archiva.repository.ManagedRepositoryContent; 041import org.apache.archiva.repository.RepositoryException; 042import org.apache.archiva.repository.RepositoryRegistry; 043import org.apache.archiva.metadata.audit.AuditListener; 044import org.apache.archiva.rest.api.services.ArchivaRestServiceException; 045import org.apache.archiva.rest.services.utils.ArtifactBuilder; 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.lang3.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; 066import java.util.ArrayList; 067import java.util.Collection; 068import java.util.Collections; 069import java.util.HashMap; 070import java.util.List; 071import java.util.Map; 072 073/** 074 * abstract class with common utilities methods 075 * 076 * @author Olivier Lamy 077 * @since 1.4-M1 078 */ 079public abstract class AbstractRestService 080{ 081 082 protected final Logger log = LoggerFactory.getLogger( getClass() ); 083 084 @Inject 085 private List<AuditListener> auditListeners = new ArrayList<>(); 086 087 @Inject 088 protected UserRepositories userRepositories; 089 090 091 /** 092 * FIXME: this could be multiple implementations and needs to be configured. 093 */ 094 @Inject 095 @Named(value = "repositorySessionFactory") 096 protected RepositorySessionFactory repositorySessionFactory; 097 098 @Inject 099 protected ArchivaAdministration archivaAdministration; 100 101 @Inject 102 protected ProxyConnectorAdmin proxyConnectorAdmin; 103 104 @Inject 105 protected RepositoryRegistry repositoryRegistry; 106 107 @Inject 108 @Named(value = "archivaTaskScheduler#repository") 109 protected RepositoryArchivaTaskScheduler repositoryTaskScheduler; 110 111 112 @Inject 113 @Named(value = "userConfiguration#default") 114 protected UserConfiguration config; 115 116 @Context 117 protected HttpServletRequest httpServletRequest; 118 119 @Context 120 protected HttpServletResponse httpServletResponse; 121 122 protected AuditInformation getAuditInformation() 123 { 124 RedbackRequestInformation redbackRequestInformation = RedbackAuthenticationThreadLocal.get(); 125 User user = redbackRequestInformation == null ? null : redbackRequestInformation.getUser(); 126 String remoteAddr = redbackRequestInformation == null ? null : redbackRequestInformation.getRemoteAddr(); 127 return new AuditInformation( user, remoteAddr ); 128 } 129 130 public List<AuditListener> getAuditListeners() 131 { 132 return auditListeners; 133 } 134 135 public void setAuditListeners( List<AuditListener> auditListeners ) 136 { 137 this.auditListeners = auditListeners; 138 } 139 140 protected List<String> getObservableRepos() 141 { 142 try 143 { 144 List<String> ids = userRepositories.getObservableRepositoryIds( getPrincipal() ); 145 return ids == null ? Collections.<String>emptyList() : ids; 146 } 147 catch ( PrincipalNotFoundException e ) 148 { 149 log.warn( e.getMessage(), e ); 150 } 151 catch ( AccessDeniedException e ) 152 { 153 log.warn( e.getMessage(), e ); 154 } 155 catch ( ArchivaSecurityException e ) 156 { 157 log.warn( e.getMessage(), e ); 158 } 159 return Collections.emptyList(); 160 } 161 162 protected String getPrincipal() 163 { 164 RedbackRequestInformation redbackRequestInformation = RedbackAuthenticationThreadLocal.get(); 165 166 return redbackRequestInformation == null 167 ? config.getString( UserConfigurationKeys.DEFAULT_GUEST ) 168 : ( redbackRequestInformation.getUser() == null 169 ? config.getString( UserConfigurationKeys.DEFAULT_GUEST ) 170 : redbackRequestInformation.getUser().getUsername() ); 171 } 172 173 protected String getBaseUrl() 174 throws RepositoryAdminException 175 { 176 String applicationUrl = archivaAdministration.getUiConfiguration().getApplicationUrl(); 177 if ( StringUtils.isNotBlank( applicationUrl ) ) 178 { 179 return applicationUrl; 180 } 181 return httpServletRequest.getScheme() + "://" + httpServletRequest.getServerName() + ( 182 httpServletRequest.getServerPort() == 80 ? "" : ":" + httpServletRequest.getServerPort() ) 183 + httpServletRequest.getContextPath(); 184 } 185 186 protected <T> Map<String, T> getBeansOfType( ApplicationContext applicationContext, Class<T> clazz ) 187 { 188 //TODO do some caching here !!! 189 // olamy : with plexus we get only roleHint 190 // as per convention we named spring bean role#hint remove role# if exists 191 Map<String, T> springBeans = applicationContext.getBeansOfType( clazz ); 192 193 Map<String, T> beans = new HashMap<>( springBeans.size() ); 194 195 for ( Map.Entry<String, T> entry : springBeans.entrySet() ) 196 { 197 String key = StringUtils.contains( entry.getKey(), '#' ) 198 ? StringUtils.substringAfterLast( entry.getKey(), "#" ) 199 : entry.getKey(); 200 beans.put( key, entry.getValue() ); 201 } 202 return beans; 203 } 204 205 protected void triggerAuditEvent( String repositoryId, String filePath, String action ) 206 { 207 AuditEvent auditEvent = new AuditEvent( repositoryId, getPrincipal(), filePath, action ); 208 AuditInformation auditInformation = getAuditInformation(); 209 auditEvent.setUserId( auditInformation.getUser() == null ? "" : auditInformation.getUser().getUsername() ); 210 auditEvent.setRemoteIP( auditInformation.getRemoteAddr() ); 211 for ( AuditListener auditListener : getAuditListeners() ) 212 { 213 auditListener.auditEvent( auditEvent ); 214 } 215 } 216 217 /** 218 * @param artifact 219 * @return 220 */ 221 protected String getArtifactUrl( Artifact artifact ) 222 throws ArchivaRestServiceException 223 { 224 return getArtifactUrl( artifact, null ); 225 } 226 227 228 protected String getArtifactUrl( Artifact artifact, String repositoryId ) 229 throws ArchivaRestServiceException 230 { 231 try 232 { 233 234 if ( httpServletRequest == null ) 235 { 236 return null; 237 } 238 239 StringBuilder sb = new StringBuilder( getBaseUrl() ); 240 241 sb.append( "/repository" ); 242 243 // when artifact come from a remote repository when have here the remote repo id 244 // we must replace it with a valid managed one available for the user. 245 if ( StringUtils.isEmpty( repositoryId ) ) 246 { 247 List<String> userRepos = userRepositories.getObservableRepositoryIds( getPrincipal() ); 248 // is it a good one? if yes nothing to 249 // if not search the repo who is proxy for this remote 250 if ( !userRepos.contains( artifact.getContext() ) ) 251 { 252 for ( Map.Entry<String, List<ProxyConnector>> entry : proxyConnectorAdmin.getProxyConnectorAsMap().entrySet() ) 253 { 254 for ( ProxyConnector proxyConnector : entry.getValue() ) 255 { 256 if ( StringUtils.equals( "remote-" + proxyConnector.getTargetRepoId(), 257 artifact.getContext() ) // 258 && userRepos.contains( entry.getKey() ) ) 259 { 260 sb.append( '/' ).append( entry.getKey() ); 261 } 262 } 263 } 264 265 } 266 else 267 { 268 sb.append( '/' ).append( artifact.getContext() ); 269 } 270 271 272 } 273 else 274 { 275 sb.append( '/' ).append( repositoryId ); 276 } 277 278 sb.append( '/' ).append( StringUtils.replaceChars( artifact.getGroupId(), '.', '/' ) ); 279 sb.append( '/' ).append( artifact.getArtifactId() ); 280 if ( VersionUtil.isSnapshot( artifact.getVersion() ) ) 281 { 282 sb.append( '/' ).append( VersionUtil.getBaseVersion( artifact.getVersion() ) ); 283 } 284 else 285 { 286 sb.append( '/' ).append( artifact.getVersion() ); 287 } 288 sb.append( '/' ).append( artifact.getArtifactId() ); 289 sb.append( '-' ).append( artifact.getVersion() ); 290 if ( StringUtils.isNotBlank( artifact.getClassifier() ) ) 291 { 292 sb.append( '-' ).append( artifact.getClassifier() ); 293 } 294 // maven-plugin packaging is a jar 295 if ( StringUtils.equals( "maven-plugin", artifact.getPackaging() ) ) 296 { 297 sb.append( "jar" ); 298 } 299 else 300 { 301 sb.append( '.' ).append( artifact.getFileExtension() ); 302 } 303 304 return sb.toString(); 305 } 306 catch ( Exception e ) 307 { 308 throw new ArchivaRestServiceException( e.getMessage(), 309 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 310 } 311 } 312 313 protected List<Artifact> buildArtifacts( Collection<ArtifactMetadata> artifactMetadatas, String repositoryId ) 314 throws ArchivaRestServiceException 315 { 316 try 317 { 318 if ( artifactMetadatas != null && !artifactMetadatas.isEmpty() ) 319 { 320 List<Artifact> artifacts = new ArrayList<>( artifactMetadatas.size() ); 321 for ( ArtifactMetadata artifact : artifactMetadatas ) 322 { 323 324 String repoId = repositoryId != null ? repositoryId : artifact.getRepositoryId(); 325 if ( repoId == null ) { 326 throw new IllegalStateException( "Repository Id is null" ); 327 } 328 ManagedRepository repo = repositoryRegistry.getManagedRepository( repoId ); 329 if (repo==null) { 330 throw new RepositoryException( "Repository not found "+repoId ); 331 } 332 ManagedRepositoryContent content = repo.getContent( ); 333 ArtifactBuilder builder = 334 new ArtifactBuilder().forArtifactMetadata( artifact ).withManagedRepositoryContent( 335 content ); 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}