001package org.apache.archiva.webdav; 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.RepositoryAdminException; 023import org.apache.archiva.admin.model.beans.ManagedRepository; 024import org.apache.archiva.admin.model.beans.RemoteRepository; 025import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin; 026import org.apache.archiva.admin.model.remote.RemoteRepositoryAdmin; 027import org.apache.archiva.audit.Auditable; 028import org.apache.archiva.common.filelock.FileLockManager; 029import org.apache.archiva.common.plexusbridge.PlexusSisuBridge; 030import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException; 031import org.apache.archiva.common.utils.PathUtil; 032import org.apache.archiva.common.utils.VersionUtil; 033import org.apache.archiva.configuration.ArchivaConfiguration; 034import org.apache.archiva.configuration.RepositoryGroupConfiguration; 035import org.apache.archiva.indexer.merger.IndexMerger; 036import org.apache.archiva.indexer.merger.IndexMergerException; 037import org.apache.archiva.indexer.merger.IndexMergerRequest; 038import org.apache.archiva.indexer.merger.MergedRemoteIndexesTask; 039import org.apache.archiva.indexer.merger.MergedRemoteIndexesTaskRequest; 040import org.apache.archiva.indexer.merger.TemporaryGroupIndex; 041import org.apache.archiva.indexer.search.RepositorySearch; 042import org.apache.archiva.maven2.metadata.MavenMetadataReader; 043import org.apache.archiva.metadata.model.facets.AuditEvent; 044import org.apache.archiva.metadata.repository.storage.RelocationException; 045import org.apache.archiva.metadata.repository.storage.RepositoryStorage; 046import org.apache.archiva.model.ArchivaRepositoryMetadata; 047import org.apache.archiva.model.ArtifactReference; 048import org.apache.archiva.policies.ProxyDownloadException; 049import org.apache.archiva.proxy.model.RepositoryProxyConnectors; 050import org.apache.archiva.redback.authentication.AuthenticationException; 051import org.apache.archiva.redback.authentication.AuthenticationResult; 052import org.apache.archiva.redback.authorization.AuthorizationException; 053import org.apache.archiva.redback.authorization.UnauthorizedException; 054import org.apache.archiva.redback.integration.filter.authentication.HttpAuthenticator; 055import org.apache.archiva.redback.policy.AccountLockedException; 056import org.apache.archiva.redback.policy.MustChangePasswordException; 057import org.apache.archiva.redback.system.SecuritySession; 058import org.apache.archiva.redback.users.User; 059import org.apache.archiva.redback.users.UserManager; 060import org.apache.archiva.repository.ManagedRepositoryContent; 061import org.apache.archiva.repository.RepositoryContentFactory; 062import org.apache.archiva.repository.RepositoryException; 063import org.apache.archiva.repository.RepositoryNotFoundException; 064import org.apache.archiva.repository.content.maven2.RepositoryRequest; 065import org.apache.archiva.repository.events.AuditListener; 066import org.apache.archiva.repository.layout.LayoutException; 067import org.apache.archiva.repository.metadata.MetadataTools; 068import org.apache.archiva.repository.metadata.RepositoryMetadataException; 069import org.apache.archiva.repository.metadata.RepositoryMetadataMerge; 070import org.apache.archiva.repository.metadata.RepositoryMetadataWriter; 071import org.apache.archiva.scheduler.repository.model.RepositoryArchivaTaskScheduler; 072import org.apache.archiva.security.ServletAuthenticator; 073import org.apache.archiva.webdav.util.MimeTypes; 074import org.apache.archiva.webdav.util.TemporaryGroupIndexSessionCleaner; 075import org.apache.archiva.webdav.util.WebdavMethodUtil; 076import org.apache.archiva.xml.XMLException; 077import org.apache.commons.io.FileUtils; 078import org.apache.commons.io.FilenameUtils; 079import org.apache.commons.lang.StringUtils; 080import org.apache.commons.lang.SystemUtils; 081import org.apache.jackrabbit.webdav.DavException; 082import org.apache.jackrabbit.webdav.DavResource; 083import org.apache.jackrabbit.webdav.DavResourceFactory; 084import org.apache.jackrabbit.webdav.DavResourceLocator; 085import org.apache.jackrabbit.webdav.DavServletRequest; 086import org.apache.jackrabbit.webdav.DavServletResponse; 087import org.apache.jackrabbit.webdav.DavSession; 088import org.apache.jackrabbit.webdav.lock.LockManager; 089import org.apache.jackrabbit.webdav.lock.SimpleLockManager; 090import org.apache.maven.index.context.IndexingContext; 091import org.codehaus.plexus.digest.ChecksumFile; 092import org.codehaus.plexus.digest.Digester; 093import org.codehaus.plexus.digest.DigesterException; 094import org.slf4j.Logger; 095import org.slf4j.LoggerFactory; 096import org.slf4j.MarkerFactory; 097import org.springframework.context.ApplicationContext; 098import org.springframework.stereotype.Service; 099 100import javax.annotation.PostConstruct; 101import javax.inject.Inject; 102import javax.inject.Named; 103import javax.servlet.http.HttpServletResponse; 104import javax.servlet.http.HttpSession; 105import java.io.File; 106import java.io.IOException; 107import java.nio.file.Files; 108import java.util.ArrayList; 109import java.util.Date; 110import java.util.HashMap; 111import java.util.HashSet; 112import java.util.List; 113import java.util.Map; 114import java.util.Set; 115 116/** 117 * 118 */ 119@Service( "davResourceFactory#archiva" ) 120public class ArchivaDavResourceFactory 121 implements DavResourceFactory, Auditable 122{ 123 private static final String PROXIED_SUFFIX = " (proxied)"; 124 125 private static final String HTTP_PUT_METHOD = "PUT"; 126 127 private Logger log = LoggerFactory.getLogger( ArchivaDavResourceFactory.class ); 128 129 @Inject 130 private List<AuditListener> auditListeners = new ArrayList<>(); 131 132 @Inject 133 private RepositoryContentFactory repositoryFactory; 134 135 private RepositoryRequest repositoryRequest; 136 137 @Inject 138 @Named( value = "repositoryProxyConnectors#default" ) 139 private RepositoryProxyConnectors connectors; 140 141 @Inject 142 private MetadataTools metadataTools; 143 144 @Inject 145 private MimeTypes mimeTypes; 146 147 private ArchivaConfiguration archivaConfiguration; 148 149 @Inject 150 private ServletAuthenticator servletAuth; 151 152 @Inject 153 @Named( value = "httpAuthenticator#basic" ) 154 private HttpAuthenticator httpAuth; 155 156 @Inject 157 private RemoteRepositoryAdmin remoteRepositoryAdmin; 158 159 @Inject 160 private ManagedRepositoryAdmin managedRepositoryAdmin; 161 162 @Inject 163 private IndexMerger indexMerger; 164 165 @Inject 166 private RepositorySearch repositorySearch; 167 168 /** 169 * Lock Manager - use simple implementation from JackRabbit 170 */ 171 private final LockManager lockManager = new SimpleLockManager(); 172 173 private ChecksumFile checksum; 174 175 private Digester digestSha1; 176 177 private Digester digestMd5; 178 179 @Inject 180 @Named( value = "archivaTaskScheduler#repository" ) 181 private RepositoryArchivaTaskScheduler scheduler; 182 183 @Inject 184 @Named( value = "fileLockManager#default" ) 185 private FileLockManager fileLockManager; 186 187 private ApplicationContext applicationContext; 188 189 @Inject 190 public ArchivaDavResourceFactory( ApplicationContext applicationContext, PlexusSisuBridge plexusSisuBridge, 191 ArchivaConfiguration archivaConfiguration ) 192 throws PlexusSisuBridgeException 193 { 194 this.archivaConfiguration = archivaConfiguration; 195 this.applicationContext = applicationContext; 196 this.checksum = plexusSisuBridge.lookup( ChecksumFile.class ); 197 198 this.digestMd5 = plexusSisuBridge.lookup( Digester.class, "md5" ); 199 this.digestSha1 = plexusSisuBridge.lookup( Digester.class, "sha1" ); 200 201 // TODO remove this hard dependency on maven !! 202 repositoryRequest = new RepositoryRequest( ); 203 } 204 205 @PostConstruct 206 public void initialize() 207 { 208 // no op 209 } 210 211 @Override 212 public DavResource createResource( final DavResourceLocator locator, final DavServletRequest request, 213 final DavServletResponse response ) 214 throws DavException 215 { 216 ArchivaDavResourceLocator archivaLocator = checkLocatorIsInstanceOfRepositoryLocator( locator ); 217 218 RepositoryGroupConfiguration repoGroupConfig = 219 archivaConfiguration.getConfiguration().getRepositoryGroupsAsMap().get( archivaLocator.getRepositoryId() ); 220 221 String activePrincipal = getActivePrincipal( request ); 222 223 List<String> resourcesInAbsolutePath = new ArrayList<>(); 224 225 boolean readMethod = WebdavMethodUtil.isReadMethod( request.getMethod() ); 226 DavResource resource; 227 if ( repoGroupConfig != null ) 228 { 229 if ( !readMethod ) 230 { 231 throw new DavException( HttpServletResponse.SC_METHOD_NOT_ALLOWED, 232 "Write method not allowed for repository groups." ); 233 } 234 235 log.debug( "Repository group '{}' accessed by '{}", repoGroupConfig.getId(), activePrincipal ); 236 237 // handle browse requests for virtual repos 238 if ( getLogicalResource( archivaLocator, null, true ).endsWith( "/" ) ) 239 { 240 try 241 { 242 DavResource davResource = 243 getResourceFromGroup( request, repoGroupConfig.getRepositories(), archivaLocator, 244 repoGroupConfig ); 245 246 setHeaders( response, locator, davResource, true ); 247 248 return davResource; 249 250 } 251 catch ( RepositoryAdminException e ) 252 { 253 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e ); 254 } 255 } 256 else 257 { 258 // make a copy to avoid potential concurrent modifications (eg. by configuration) 259 // TODO: ultimately, locking might be more efficient than copying in this fashion since updates are 260 // infrequent 261 List<String> repositories = new ArrayList<>( repoGroupConfig.getRepositories() ); 262 resource = processRepositoryGroup( request, archivaLocator, repositories, activePrincipal, 263 resourcesInAbsolutePath, repoGroupConfig ); 264 } 265 } 266 else 267 { 268 269 try 270 { 271 RemoteRepository remoteRepository = 272 remoteRepositoryAdmin.getRemoteRepository( archivaLocator.getRepositoryId() ); 273 274 if ( remoteRepository != null ) 275 { 276 String logicalResource = getLogicalResource( archivaLocator, null, false ); 277 IndexingContext indexingContext = remoteRepositoryAdmin.createIndexContext( remoteRepository ); 278 File resourceFile = StringUtils.equals( logicalResource, "/" ) 279 ? new File( indexingContext.getIndexDirectoryFile().getParent() ) 280 : new File( indexingContext.getIndexDirectoryFile().getParent(), logicalResource ); 281 resource = new ArchivaDavResource( resourceFile.getAbsolutePath(), // 282 locator.getResourcePath(), // 283 null, // 284 request.getRemoteAddr(), // 285 activePrincipal, // 286 request.getDavSession(), // 287 archivaLocator, // 288 this, // 289 mimeTypes, // 290 auditListeners, // 291 scheduler, // 292 fileLockManager ); 293 setHeaders( response, locator, resource, false ); 294 return resource; 295 } 296 } 297 catch ( RepositoryAdminException e ) 298 { 299 log.debug( "RepositoryException remote repository with d'{}' not found, msg: {}", 300 archivaLocator.getRepositoryId(), e.getMessage() ); 301 } 302 303 ManagedRepositoryContent managedRepositoryContent = null; 304 305 try 306 { 307 managedRepositoryContent = 308 repositoryFactory.getManagedRepositoryContent( archivaLocator.getRepositoryId() ); 309 } 310 catch ( RepositoryNotFoundException e ) 311 { 312 throw new DavException( HttpServletResponse.SC_NOT_FOUND, 313 "Invalid repository: " + archivaLocator.getRepositoryId() ); 314 } 315 catch ( RepositoryException e ) 316 { 317 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e ); 318 } 319 320 log.debug( "Managed repository '{}' accessed by '{}'", managedRepositoryContent.getId(), activePrincipal ); 321 322 try 323 { 324 resource = processRepository( request, archivaLocator, activePrincipal, managedRepositoryContent, 325 managedRepositoryAdmin.getManagedRepository( 326 archivaLocator.getRepositoryId() ) ); 327 328 String logicalResource = getLogicalResource( archivaLocator, null, false ); 329 resourcesInAbsolutePath.add( 330 new File( managedRepositoryContent.getRepoRoot(), logicalResource ).getAbsolutePath() ); 331 332 } 333 catch ( RepositoryAdminException e ) 334 { 335 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e ); 336 } 337 } 338 339 String requestedResource = request.getRequestURI(); 340 341 // MRM-872 : merge all available metadata 342 // merge metadata only when requested via the repo group 343 if ( ( repositoryRequest.isMetadata( requestedResource ) || repositoryRequest.isMetadataSupportFile( 344 requestedResource ) ) && repoGroupConfig != null ) 345 { 346 // this should only be at the project level not version level! 347 if ( isProjectReference( requestedResource ) ) 348 { 349 350 ArchivaDavResource res = (ArchivaDavResource) resource; 351 String filePath = 352 StringUtils.substringBeforeLast( res.getLocalResource().getAbsolutePath().replace( '\\', '/' ), 353 "/" ); 354 filePath = filePath + "/maven-metadata-" + repoGroupConfig.getId() + ".xml"; 355 356 // for MRM-872 handle checksums of the merged metadata files 357 if ( repositoryRequest.isSupportFile( requestedResource ) ) 358 { 359 File metadataChecksum = 360 new File( filePath + "." + StringUtils.substringAfterLast( requestedResource, "." ) ); 361 362 if ( metadataChecksum.exists() ) 363 { 364 LogicalResource logicalResource = 365 new LogicalResource( getLogicalResource( archivaLocator, null, false ) ); 366 367 resource = 368 new ArchivaDavResource( metadataChecksum.getAbsolutePath(), logicalResource.getPath(), null, 369 request.getRemoteAddr(), activePrincipal, request.getDavSession(), 370 archivaLocator, this, mimeTypes, auditListeners, scheduler, 371 fileLockManager ); 372 } 373 } 374 else 375 { 376 if ( resourcesInAbsolutePath != null && resourcesInAbsolutePath.size() > 1 ) 377 { 378 // merge the metadata of all repos under group 379 ArchivaRepositoryMetadata mergedMetadata = new ArchivaRepositoryMetadata(); 380 for ( String resourceAbsPath : resourcesInAbsolutePath ) 381 { 382 try 383 { 384 File metadataFile = new File( resourceAbsPath ); 385 ArchivaRepositoryMetadata repoMetadata = MavenMetadataReader.read( metadataFile ); 386 mergedMetadata = RepositoryMetadataMerge.merge( mergedMetadata, repoMetadata ); 387 } 388 catch ( XMLException e ) 389 { 390 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 391 "Error occurred while reading metadata file." ); 392 } 393 catch ( RepositoryMetadataException r ) 394 { 395 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 396 "Error occurred while merging metadata file." ); 397 } 398 } 399 400 try 401 { 402 File resourceFile = writeMergedMetadataToFile( mergedMetadata, filePath ); 403 404 LogicalResource logicalResource = 405 new LogicalResource( getLogicalResource( archivaLocator, null, false ) ); 406 407 resource = 408 new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(), null, 409 request.getRemoteAddr(), activePrincipal, 410 request.getDavSession(), archivaLocator, this, mimeTypes, 411 auditListeners, scheduler, fileLockManager ); 412 } 413 catch ( RepositoryMetadataException r ) 414 { 415 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 416 "Error occurred while writing metadata file." ); 417 } 418 catch ( IOException ie ) 419 { 420 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 421 "Error occurred while generating checksum files." ); 422 } 423 catch ( DigesterException de ) 424 { 425 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 426 "Error occurred while generating checksum files." 427 + de.getMessage() ); 428 } 429 } 430 } 431 } 432 } 433 434 setHeaders( response, locator, resource, false ); 435 436 // compatibility with MRM-440 to ensure browsing the repository works ok 437 if ( resource.isCollection() && !request.getRequestURI().endsWith( "/" ) ) 438 { 439 throw new BrowserRedirectException( resource.getHref() ); 440 } 441 resource.addLockManager( lockManager ); 442 return resource; 443 } 444 445 private DavResource processRepositoryGroup( final DavServletRequest request, 446 ArchivaDavResourceLocator archivaLocator, List<String> repositories, 447 String activePrincipal, List<String> resourcesInAbsolutePath, 448 RepositoryGroupConfiguration repoGroupConfig ) 449 throws DavException 450 { 451 DavResource resource = null; 452 List<DavException> storedExceptions = new ArrayList<>(); 453 454 String pathInfo = StringUtils.removeEnd( request.getPathInfo(), "/" ); 455 456 String rootPath = StringUtils.substringBeforeLast( pathInfo, "/" ); 457 458 if ( StringUtils.endsWith( rootPath, repoGroupConfig.getMergedIndexPath() ) ) 459 { 460 // we are in the case of index file request 461 String requestedFileName = StringUtils.substringAfterLast( pathInfo, "/" ); 462 File temporaryIndexDirectory = 463 buildMergedIndexDirectory( repositories, activePrincipal, request, repoGroupConfig ); 464 465 File resourceFile = new File( temporaryIndexDirectory, requestedFileName ); 466 resource = new ArchivaDavResource( resourceFile.getAbsolutePath(), requestedFileName, null, 467 request.getRemoteAddr(), activePrincipal, request.getDavSession(), 468 archivaLocator, this, mimeTypes, auditListeners, scheduler, 469 fileLockManager ); 470 471 } 472 else 473 { 474 for ( String repositoryId : repositories ) 475 { 476 ManagedRepositoryContent managedRepositoryContent; 477 try 478 { 479 managedRepositoryContent = repositoryFactory.getManagedRepositoryContent( repositoryId ); 480 } 481 catch ( RepositoryNotFoundException e ) 482 { 483 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e ); 484 } 485 catch ( RepositoryException e ) 486 { 487 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e ); 488 } 489 490 try 491 { 492 ManagedRepository managedRepository = managedRepositoryAdmin.getManagedRepository( repositoryId ); 493 DavResource updatedResource = 494 processRepository( request, archivaLocator, activePrincipal, managedRepositoryContent, 495 managedRepository ); 496 if ( resource == null ) 497 { 498 resource = updatedResource; 499 } 500 501 String logicalResource = getLogicalResource( archivaLocator, null, false ); 502 if ( logicalResource.endsWith( "/" ) ) 503 { 504 logicalResource = logicalResource.substring( 1 ); 505 } 506 resourcesInAbsolutePath.add( 507 new File( managedRepositoryContent.getRepoRoot(), logicalResource ).getAbsolutePath() ); 508 } 509 catch ( DavException e ) 510 { 511 storedExceptions.add( e ); 512 } 513 catch ( RepositoryAdminException e ) 514 { 515 storedExceptions.add( new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e ) ); 516 } 517 } 518 } 519 if ( resource == null ) 520 { 521 if ( !storedExceptions.isEmpty() ) 522 { 523 // MRM-1232 524 for ( DavException e : storedExceptions ) 525 { 526 if ( 401 == e.getErrorCode() ) 527 { 528 throw e; 529 } 530 } 531 532 throw new DavException( HttpServletResponse.SC_NOT_FOUND ); 533 } 534 else 535 { 536 throw new DavException( HttpServletResponse.SC_NOT_FOUND ); 537 } 538 } 539 return resource; 540 } 541 542 private String getLogicalResource( ArchivaDavResourceLocator archivaLocator, ManagedRepository managedRepository, 543 boolean useOrigResourcePath ) 544 { 545 // FIXME remove this hack 546 // but currently managedRepository can be null in case of group 547 String layout = managedRepository == null ? new ManagedRepository().getLayout() : managedRepository.getLayout(); 548 RepositoryStorage repositoryStorage = 549 this.applicationContext.getBean( "repositoryStorage#" + layout, RepositoryStorage.class ); 550 String path = repositoryStorage.getFilePath( 551 useOrigResourcePath ? archivaLocator.getOrigResourcePath() : archivaLocator.getResourcePath(), 552 managedRepository ); 553 log.debug( "found path {} for resourcePath: '{}' with managedRepo '{}' and layout '{}'", path, 554 archivaLocator.getResourcePath(), managedRepository == null ? "null" : managedRepository.getId(), 555 layout ); 556 return path; 557 } 558 559 private String evaluatePathWithVersion( ArchivaDavResourceLocator archivaLocator, // 560 ManagedRepositoryContent managedRepositoryContent, // 561 String contextPath ) 562 throws DavException 563 { 564 String layout = managedRepositoryContent.getRepository() == null 565 ? new ManagedRepository().getLayout() 566 : managedRepositoryContent.getRepository().getLayout(); 567 RepositoryStorage repositoryStorage = 568 this.applicationContext.getBean( "repositoryStorage#" + layout, RepositoryStorage.class ); 569 try 570 { 571 return repositoryStorage.getFilePathWithVersion( archivaLocator.getResourcePath(), // 572 managedRepositoryContent ); 573 } 574 catch ( RelocationException e ) 575 { 576 String path = e.getPath(); 577 log.debug( "Relocation to {}", path ); 578 579 throw new BrowserRedirectException( addHrefPrefix( contextPath, path ), e.getRelocationType() ); 580 } 581 catch ( XMLException e ) 582 { 583 log.error( e.getMessage(), e ); 584 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e ); 585 } 586 } 587 588 private DavResource processRepository( final DavServletRequest request, ArchivaDavResourceLocator archivaLocator, 589 String activePrincipal, ManagedRepositoryContent managedRepositoryContent, 590 ManagedRepository managedRepository ) 591 throws DavException 592 { 593 DavResource resource = null; 594 if ( isAuthorized( request, managedRepositoryContent.getId() ) ) 595 { 596 boolean readMethod = WebdavMethodUtil.isReadMethod( request.getMethod() ); 597 // Maven Centric part ask evaluation if -SNAPSHOT 598 // MRM-1846 test if read method to prevent issue with maven 2.2.1 and uniqueVersion false 599 600 String path = readMethod 601 ? evaluatePathWithVersion( archivaLocator, managedRepositoryContent, request.getContextPath() ) 602 : getLogicalResource( archivaLocator, managedRepository, false ); 603 if ( path.startsWith( "/" ) ) 604 { 605 path = path.substring( 1 ); 606 } 607 LogicalResource logicalResource = new LogicalResource( path ); 608 File resourceFile = new File( managedRepositoryContent.getRepoRoot(), path ); 609 resource = 610 new ArchivaDavResource( resourceFile.getAbsolutePath(), path, managedRepositoryContent.getRepository(), 611 request.getRemoteAddr(), activePrincipal, request.getDavSession(), 612 archivaLocator, this, mimeTypes, auditListeners, scheduler, fileLockManager ); 613 614 if ( WebdavMethodUtil.isReadMethod( request.getMethod() ) ) 615 { 616 if ( archivaLocator.getHref( false ).endsWith( "/" ) && !resourceFile.isDirectory() ) 617 { 618 // force a resource not found 619 throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Resource does not exist" ); 620 } 621 else 622 { 623 if ( !resource.isCollection() ) 624 { 625 boolean previouslyExisted = resourceFile.exists(); 626 627 boolean fromProxy = fetchContentFromProxies( managedRepositoryContent, request, logicalResource ); 628 629 // At this point the incoming request can either be in default or 630 // legacy layout format. 631 try 632 { 633 // Perform an adjustment of the resource to the managed 634 // repository expected path. 635 String localResourcePath = 636 repositoryRequest.toNativePath( logicalResource.getPath(), managedRepositoryContent ); 637 resourceFile = new File( managedRepositoryContent.getRepoRoot(), localResourcePath ); 638 resource = 639 new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(), 640 managedRepositoryContent.getRepository(), 641 request.getRemoteAddr(), activePrincipal, 642 request.getDavSession(), archivaLocator, this, mimeTypes, 643 auditListeners, scheduler, fileLockManager ); 644 } 645 catch ( LayoutException e ) 646 { 647 if ( !resourceFile.exists() ) 648 { 649 throw new DavException( HttpServletResponse.SC_NOT_FOUND, e ); 650 } 651 } 652 653 if ( fromProxy ) 654 { 655 String action = ( previouslyExisted ? AuditEvent.MODIFY_FILE : AuditEvent.CREATE_FILE ) 656 + PROXIED_SUFFIX; 657 658 log.debug( "Proxied artifact '{}' in repository '{}' (current user '{}')", 659 resourceFile.getName(), managedRepositoryContent.getId(), activePrincipal ); 660 661 triggerAuditEvent( request.getRemoteAddr(), archivaLocator.getRepositoryId(), 662 logicalResource.getPath(), action, activePrincipal ); 663 } 664 665 if ( !resourceFile.exists() ) 666 { 667 throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Resource does not exist" ); 668 } 669 } 670 } 671 } 672 673 if ( request.getMethod().equals( HTTP_PUT_METHOD ) ) 674 { 675 String resourcePath = logicalResource.getPath(); 676 677 // check if target repo is enabled for releases 678 // we suppose that release-artifacts can be deployed only to repos enabled for releases 679 if ( managedRepositoryContent.getRepository().isReleases() && !repositoryRequest.isMetadata( 680 resourcePath ) && !repositoryRequest.isSupportFile( resourcePath ) ) 681 { 682 ArtifactReference artifact = null; 683 try 684 { 685 artifact = managedRepositoryContent.toArtifactReference( resourcePath ); 686 687 if ( !VersionUtil.isSnapshot( artifact.getVersion() ) ) 688 { 689 // check if artifact already exists and if artifact re-deployment to the repository is allowed 690 if ( managedRepositoryContent.hasContent( artifact ) 691 && managedRepositoryContent.getRepository().isBlockRedeployments() ) 692 { 693 log.warn( "Overwriting released artifacts in repository '{}' is not allowed.", 694 managedRepositoryContent.getId() ); 695 throw new DavException( HttpServletResponse.SC_CONFLICT, 696 "Overwriting released artifacts is not allowed." ); 697 } 698 } 699 } 700 catch ( LayoutException e ) 701 { 702 log.warn( "Artifact path '{}' is invalid.", resourcePath ); 703 } 704 } 705 706 /* 707 * Create parent directories that don't exist when writing a file This actually makes this 708 * implementation not compliant to the WebDAV RFC - but we have enough knowledge about how the 709 * collection is being used to do this reasonably and some versions of Maven's WebDAV don't correctly 710 * create the collections themselves. 711 */ 712 713 File rootDirectory = new File( managedRepositoryContent.getRepoRoot() ); 714 File destDir = new File( rootDirectory, logicalResource.getPath() ).getParentFile(); 715 716 if ( !destDir.exists() ) 717 { 718 destDir.mkdirs(); 719 String relPath = PathUtil.getRelative( rootDirectory.getAbsolutePath(), destDir ); 720 721 log.debug( "Creating destination directory '{}' (current user '{}')", destDir.getName(), 722 activePrincipal ); 723 724 triggerAuditEvent( request.getRemoteAddr(), managedRepositoryContent.getId(), relPath, 725 AuditEvent.CREATE_DIR, activePrincipal ); 726 } 727 } 728 } 729 return resource; 730 } 731 732 @Override 733 public DavResource createResource( final DavResourceLocator locator, final DavSession davSession ) 734 throws DavException 735 { 736 ArchivaDavResourceLocator archivaLocator = checkLocatorIsInstanceOfRepositoryLocator( locator ); 737 738 ManagedRepositoryContent managedRepositoryContent; 739 try 740 { 741 managedRepositoryContent = 742 repositoryFactory.getManagedRepositoryContent( archivaLocator.getRepositoryId() ); 743 } 744 catch ( RepositoryNotFoundException e ) 745 { 746 throw new DavException( HttpServletResponse.SC_NOT_FOUND, 747 "Invalid repository: " + archivaLocator.getRepositoryId() ); 748 } 749 catch ( RepositoryException e ) 750 { 751 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e ); 752 } 753 754 DavResource resource = null; 755 try 756 { 757 String logicalResource = getLogicalResource( archivaLocator, managedRepositoryAdmin.getManagedRepository( 758 archivaLocator.getRepositoryId() ), false ); 759 if ( logicalResource.startsWith( "/" ) ) 760 { 761 logicalResource = logicalResource.substring( 1 ); 762 } 763 File resourceFile = new File( managedRepositoryContent.getRepoRoot(), logicalResource ); 764 resource = new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource, 765 managedRepositoryContent.getRepository(), davSession, archivaLocator, 766 this, mimeTypes, auditListeners, scheduler, fileLockManager ); 767 768 resource.addLockManager( lockManager ); 769 } 770 catch ( RepositoryAdminException e ) 771 { 772 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e ); 773 } 774 return resource; 775 } 776 777 private boolean fetchContentFromProxies( ManagedRepositoryContent managedRepository, DavServletRequest request, 778 LogicalResource resource ) 779 throws DavException 780 { 781 String path = resource.getPath(); 782 if ( repositoryRequest.isSupportFile( path ) ) 783 { 784 File proxiedFile = connectors.fetchFromProxies( managedRepository, path ); 785 786 return ( proxiedFile != null ); 787 } 788 789 // Is it a Metadata resource? 790 if ( repositoryRequest.isDefault( path ) && repositoryRequest.isMetadata( path ) ) 791 { 792 return connectors.fetchMetadataFromProxies( managedRepository, path ).isModified(); 793 } 794 795 // Is it an Archetype Catalog? 796 if ( repositoryRequest.isArchetypeCatalog( path ) ) 797 { 798 // FIXME we must implement a merge of remote archetype catalog from remote servers. 799 File proxiedFile = connectors.fetchFromProxies( managedRepository, path ); 800 801 return ( proxiedFile != null ); 802 } 803 804 // Not any of the above? Then it's gotta be an artifact reference. 805 try 806 { 807 // Get the artifact reference in a layout neutral way. 808 ArtifactReference artifact = repositoryRequest.toArtifactReference( path ); 809 810 if ( artifact != null ) 811 { 812 String repositoryLayout = managedRepository.getRepository().getLayout(); 813 814 RepositoryStorage repositoryStorage = 815 this.applicationContext.getBean( "repositoryStorage#" + repositoryLayout, RepositoryStorage.class ); 816 repositoryStorage.applyServerSideRelocation( managedRepository, artifact ); 817 818 File proxiedFile = connectors.fetchFromProxies( managedRepository, artifact ); 819 820 resource.setPath( managedRepository.toPath( artifact ) ); 821 822 log.debug( "Proxied artifact '{}:{}:{}'", artifact.getGroupId(), artifact.getArtifactId(), 823 artifact.getVersion() ); 824 825 return ( proxiedFile != null ); 826 } 827 } 828 catch ( LayoutException e ) 829 { 830 /* eat it */ 831 } 832 catch ( ProxyDownloadException e ) 833 { 834 log.error( e.getMessage(), e ); 835 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 836 "Unable to fetch artifact resource." ); 837 } 838 return false; 839 } 840 841 // TODO: remove? 842 843 private void triggerAuditEvent( String remoteIP, String repositoryId, String resource, String action, 844 String principal ) 845 { 846 AuditEvent event = new AuditEvent( repositoryId, principal, resource, action ); 847 event.setRemoteIP( remoteIP ); 848 849 for ( AuditListener listener : auditListeners ) 850 { 851 listener.auditEvent( event ); 852 } 853 } 854 855 @Override 856 public void addAuditListener( AuditListener listener ) 857 { 858 this.auditListeners.add( listener ); 859 } 860 861 @Override 862 public void clearAuditListeners() 863 { 864 this.auditListeners.clear(); 865 } 866 867 @Override 868 public void removeAuditListener( AuditListener listener ) 869 { 870 this.auditListeners.remove( listener ); 871 } 872 873 private void setHeaders( DavServletResponse response, DavResourceLocator locator, DavResource resource, 874 boolean group ) 875 { 876 // [MRM-503] - Metadata file need Pragma:no-cache response 877 // header. 878 if ( locator.getResourcePath().endsWith( "/maven-metadata.xml" ) || ( resource instanceof ArchivaDavResource 879 && ( ArchivaDavResource.class.cast( resource ).getLocalResource().isDirectory() ) ) ) 880 { 881 response.setHeader( "Pragma", "no-cache" ); 882 response.setHeader( "Cache-Control", "no-cache" ); 883 response.setDateHeader( "Last-Modified", new Date().getTime() ); 884 } 885 // if the resource is a directory don't cache it as new groupId deployed will be available 886 // without need of refreshing browser 887 else if ( locator.getResourcePath().endsWith( "/maven-metadata.xml" ) || ( 888 resource instanceof ArchivaVirtualDavResource && ( new File( 889 ArchivaVirtualDavResource.class.cast( resource ).getLogicalResource() ).isDirectory() ) ) ) 890 { 891 response.setHeader( "Pragma", "no-cache" ); 892 response.setHeader( "Cache-Control", "no-cache" ); 893 response.setDateHeader( "Last-Modified", new Date().getTime() ); 894 } 895 else if ( group ) 896 { 897 if ( resource instanceof ArchivaVirtualDavResource ) 898 { 899 //MRM-1854 here we have a directory so force "Last-Modified" 900 response.setDateHeader( "Last-Modified", new Date().getTime() ); 901 } 902 } 903 else 904 { 905 // We need to specify this so connecting wagons can work correctly 906 response.setDateHeader( "Last-Modified", resource.getModificationTime() ); 907 } 908 // TODO: [MRM-524] determine http caching options for other types of files (artifacts, sha1, md5, snapshots) 909 } 910 911 private ArchivaDavResourceLocator checkLocatorIsInstanceOfRepositoryLocator( DavResourceLocator locator ) 912 throws DavException 913 { 914 if ( !( locator instanceof ArchivaDavResourceLocator ) ) 915 { 916 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 917 "Locator does not implement RepositoryLocator" ); 918 } 919 920 // Hidden paths 921 if ( locator.getResourcePath().startsWith( ArchivaDavResource.HIDDEN_PATH_PREFIX ) ) 922 { 923 throw new DavException( HttpServletResponse.SC_NOT_FOUND ); 924 } 925 926 ArchivaDavResourceLocator archivaLocator = (ArchivaDavResourceLocator) locator; 927 928 // MRM-419 - Windows Webdav support. Should not 404 if there is no content. 929 if ( StringUtils.isEmpty( archivaLocator.getRepositoryId() ) ) 930 { 931 throw new DavException( HttpServletResponse.SC_NO_CONTENT ); 932 } 933 return archivaLocator; 934 } 935 936 private String addHrefPrefix( String contextPath, String path ) { 937 String prefix = archivaConfiguration.getConfiguration().getWebapp().getUi().getApplicationUrl(); 938 if (prefix == null || prefix.isEmpty()) { 939 prefix = contextPath; 940 } 941 return prefix + ( StringUtils.startsWith( path, "/" ) ? "" : 942 ( StringUtils.endsWith( prefix, "/" ) ? "" : "/" ) ) 943 + path; 944 } 945 946 private static class LogicalResource 947 { 948 private String path; 949 950 public LogicalResource( String path ) 951 { 952 this.path = path; 953 } 954 955 public String getPath() 956 { 957 return path; 958 } 959 960 public void setPath( String path ) 961 { 962 this.path = path; 963 } 964 } 965 966 protected boolean isAuthorized( DavServletRequest request, String repositoryId ) 967 throws DavException 968 { 969 try 970 { 971 AuthenticationResult result = httpAuth.getAuthenticationResult( request, null ); 972 SecuritySession securitySession = httpAuth.getSecuritySession( request.getSession( true ) ); 973 974 return servletAuth.isAuthenticated( request, result ) // 975 && servletAuth.isAuthorized( request, securitySession, repositoryId, // 976 WebdavMethodUtil.getMethodPermission( request.getMethod() ) ); 977 } 978 catch ( AuthenticationException e ) 979 { 980 // safety check for MRM-911 981 String guest = UserManager.GUEST_USERNAME; 982 try 983 { 984 if ( servletAuth.isAuthorized( guest, 985 ( (ArchivaDavResourceLocator) request.getRequestLocator() ).getRepositoryId(), 986 WebdavMethodUtil.getMethodPermission( request.getMethod() ) ) ) 987 { 988 return true; 989 } 990 } 991 catch ( UnauthorizedException ae ) 992 { 993 throw new UnauthorizedDavException( repositoryId, 994 "You are not authenticated and authorized to access any repository." ); 995 } 996 997 throw new UnauthorizedDavException( repositoryId, "You are not authenticated" ); 998 } 999 catch ( MustChangePasswordException e ) 1000 { 1001 throw new UnauthorizedDavException( repositoryId, "You must change your password." ); 1002 } 1003 catch ( AccountLockedException e ) 1004 { 1005 throw new UnauthorizedDavException( repositoryId, "User account is locked." ); 1006 } 1007 catch ( AuthorizationException e ) 1008 { 1009 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 1010 "Fatal Authorization Subsystem Error." ); 1011 } 1012 catch ( UnauthorizedException e ) 1013 { 1014 throw new UnauthorizedDavException( repositoryId, e.getMessage() ); 1015 } 1016 } 1017 1018 private DavResource getResourceFromGroup( DavServletRequest request, List<String> repositories, 1019 ArchivaDavResourceLocator locator, 1020 RepositoryGroupConfiguration repositoryGroupConfiguration ) 1021 throws DavException, RepositoryAdminException 1022 { 1023 if ( repositoryGroupConfiguration.getRepositories() == null 1024 || repositoryGroupConfiguration.getRepositories().isEmpty() ) 1025 { 1026 File file = 1027 new File( System.getProperty( "appserver.base" ), "groups/" + repositoryGroupConfiguration.getId() ); 1028 1029 return new ArchivaDavResource( file.getPath(), "groups/" + repositoryGroupConfiguration.getId(), null, 1030 request.getDavSession(), locator, this, mimeTypes, auditListeners, scheduler, 1031 fileLockManager ); 1032 } 1033 List<File> mergedRepositoryContents = new ArrayList<>(); 1034 // multiple repo types so we guess they are all the same type 1035 // so use the first one 1036 // FIXME add a method with group in the repository storage 1037 String firstRepoId = repositoryGroupConfiguration.getRepositories().get( 0 ); 1038 1039 String path = getLogicalResource( locator, managedRepositoryAdmin.getManagedRepository( firstRepoId ), false ); 1040 if ( path.startsWith( "/" ) ) 1041 { 1042 path = path.substring( 1 ); 1043 } 1044 LogicalResource logicalResource = new LogicalResource( path ); 1045 1046 // flow: 1047 // if the current user logged in has permission to any of the repositories, allow user to 1048 // browse the repo group but displaying only the repositories which the user has permission to access. 1049 // otherwise, prompt for authentication. 1050 1051 String activePrincipal = getActivePrincipal( request ); 1052 1053 boolean allow = isAllowedToContinue( request, repositories, activePrincipal ); 1054 1055 // remove last / 1056 String pathInfo = StringUtils.removeEnd( request.getPathInfo(), "/" ); 1057 1058 if ( allow ) 1059 { 1060 1061 if ( StringUtils.endsWith( pathInfo, repositoryGroupConfiguration.getMergedIndexPath() ) ) 1062 { 1063 File mergedRepoDir = 1064 buildMergedIndexDirectory( repositories, activePrincipal, request, repositoryGroupConfiguration ); 1065 mergedRepositoryContents.add( mergedRepoDir ); 1066 } 1067 else 1068 { 1069 if ( StringUtils.equalsIgnoreCase( pathInfo, "/" + repositoryGroupConfiguration.getId() ) ) 1070 { 1071 File tmpDirectory = new File( SystemUtils.getJavaIoTmpDir(), 1072 repositoryGroupConfiguration.getId() + "/" 1073 + repositoryGroupConfiguration.getMergedIndexPath() ); 1074 if ( !tmpDirectory.exists() ) 1075 { 1076 synchronized ( tmpDirectory.getAbsolutePath() ) 1077 { 1078 if ( !tmpDirectory.exists() ) 1079 { 1080 tmpDirectory.mkdirs(); 1081 } 1082 } 1083 } 1084 mergedRepositoryContents.add( tmpDirectory.getParentFile() ); 1085 } 1086 for ( String repository : repositories ) 1087 { 1088 ManagedRepositoryContent managedRepository = null; 1089 1090 try 1091 { 1092 managedRepository = repositoryFactory.getManagedRepositoryContent( repository ); 1093 } 1094 catch ( RepositoryNotFoundException e ) 1095 { 1096 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 1097 "Invalid managed repository <" + repository + ">: " + e.getMessage() ); 1098 } 1099 catch ( RepositoryException e ) 1100 { 1101 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 1102 "Invalid managed repository <" + repository + ">: " + e.getMessage() ); 1103 } 1104 1105 File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource.getPath() ); 1106 if ( resourceFile.exists() ) 1107 { 1108 // in case of group displaying index directory doesn't have sense !! 1109 String repoIndexDirectory = managedRepository.getRepository().getIndexDirectory(); 1110 if ( StringUtils.isNotEmpty( repoIndexDirectory ) ) 1111 { 1112 if ( !new File( repoIndexDirectory ).isAbsolute() ) 1113 { 1114 repoIndexDirectory = new File( managedRepository.getRepository().getLocation(), 1115 StringUtils.isEmpty( repoIndexDirectory ) 1116 ? ".indexer" 1117 : repoIndexDirectory ).getAbsolutePath(); 1118 } 1119 } 1120 if ( StringUtils.isEmpty( repoIndexDirectory ) ) 1121 { 1122 repoIndexDirectory = new File( managedRepository.getRepository().getLocation(), 1123 ".indexer" ).getAbsolutePath(); 1124 } 1125 1126 if ( !StringUtils.equals( FilenameUtils.normalize( repoIndexDirectory ), 1127 FilenameUtils.normalize( resourceFile.getAbsolutePath() ) ) ) 1128 { 1129 // for prompted authentication 1130 if ( httpAuth.getSecuritySession( request.getSession( true ) ) != null ) 1131 { 1132 try 1133 { 1134 if ( isAuthorized( request, repository ) ) 1135 { 1136 mergedRepositoryContents.add( resourceFile ); 1137 log.debug( "Repository '{}' accessed by '{}'", repository, activePrincipal ); 1138 } 1139 } 1140 catch ( DavException e ) 1141 { 1142 // TODO: review exception handling 1143 1144 log.debug( "Skipping repository '{}' for user '{}': {}", managedRepository, 1145 activePrincipal, e.getMessage() ); 1146 1147 } 1148 1149 } 1150 else 1151 { 1152 // for the current user logged in 1153 try 1154 { 1155 if ( servletAuth.isAuthorized( activePrincipal, repository, 1156 WebdavMethodUtil.getMethodPermission( 1157 request.getMethod() ) ) ) 1158 { 1159 mergedRepositoryContents.add( resourceFile ); 1160 log.debug( "Repository '{}' accessed by '{}'", repository, activePrincipal ); 1161 } 1162 } 1163 catch ( UnauthorizedException e ) 1164 { 1165 // TODO: review exception handling 1166 1167 log.debug( "Skipping repository '{}' for user '{}': {}", managedRepository, 1168 activePrincipal, e.getMessage() ); 1169 1170 } 1171 } 1172 } 1173 } 1174 } 1175 } 1176 } 1177 else 1178 { 1179 throw new UnauthorizedDavException( locator.getRepositoryId(), "User not authorized." ); 1180 } 1181 1182 ArchivaVirtualDavResource resource = 1183 new ArchivaVirtualDavResource( mergedRepositoryContents, logicalResource.getPath(), mimeTypes, locator, 1184 this ); 1185 1186 // compatibility with MRM-440 to ensure browsing the repository group works ok 1187 if ( resource.isCollection() && !request.getRequestURI().endsWith( "/" ) ) 1188 { 1189 throw new BrowserRedirectException( resource.getHref() ); 1190 } 1191 1192 return resource; 1193 } 1194 1195 protected String getActivePrincipal( DavServletRequest request ) 1196 { 1197 User sessionUser = httpAuth.getSessionUser( request.getSession() ); 1198 return sessionUser != null ? sessionUser.getUsername() : UserManager.GUEST_USERNAME; 1199 } 1200 1201 /** 1202 * Check if the current user is authorized to access any of the repos 1203 * 1204 * @param request 1205 * @param repositories 1206 * @param activePrincipal 1207 * @return 1208 */ 1209 private boolean isAllowedToContinue( DavServletRequest request, List<String> repositories, String activePrincipal ) 1210 { 1211 // when no repositories configured it's impossible to browse nothing ! 1212 // at least make possible to see nothing :-) 1213 if ( repositories == null || repositories.isEmpty() ) 1214 { 1215 return true; 1216 } 1217 1218 boolean allow = false; 1219 1220 // if securitySession != null, it means that the user was prompted for authentication 1221 if ( httpAuth.getSecuritySession( request.getSession() ) != null ) 1222 { 1223 for ( String repository : repositories ) 1224 { 1225 try 1226 { 1227 if ( isAuthorized( request, repository ) ) 1228 { 1229 allow = true; 1230 break; 1231 } 1232 } 1233 catch ( DavException e ) 1234 { 1235 continue; 1236 } 1237 } 1238 } 1239 else 1240 { 1241 for ( String repository : repositories ) 1242 { 1243 try 1244 { 1245 if ( servletAuth.isAuthorized( activePrincipal, repository, 1246 WebdavMethodUtil.getMethodPermission( request.getMethod() ) ) ) 1247 { 1248 allow = true; 1249 break; 1250 } 1251 } 1252 catch ( UnauthorizedException e ) 1253 { 1254 continue; 1255 } 1256 } 1257 } 1258 1259 return allow; 1260 } 1261 1262 private File writeMergedMetadataToFile( ArchivaRepositoryMetadata mergedMetadata, String outputFilename ) 1263 throws RepositoryMetadataException, DigesterException, IOException 1264 { 1265 File outputFile = new File( outputFilename ); 1266 if ( outputFile.exists() ) 1267 { 1268 FileUtils.deleteQuietly( outputFile ); 1269 } 1270 1271 outputFile.getParentFile().mkdirs(); 1272 RepositoryMetadataWriter.write( mergedMetadata, outputFile ); 1273 1274 createChecksumFile( outputFilename, digestSha1 ); 1275 createChecksumFile( outputFilename, digestMd5 ); 1276 1277 return outputFile; 1278 } 1279 1280 private void createChecksumFile( String path, Digester digester ) 1281 throws DigesterException, IOException 1282 { 1283 File checksumFile = new File( path + digester.getFilenameExtension() ); 1284 if ( !checksumFile.exists() ) 1285 { 1286 FileUtils.deleteQuietly( checksumFile ); 1287 checksum.createChecksum( new File( path ), digester ); 1288 } 1289 else if ( !checksumFile.isFile() ) 1290 { 1291 log.error( "Checksum file is not a file." ); 1292 } 1293 } 1294 1295 private boolean isProjectReference( String requestedResource ) 1296 { 1297 try 1298 { 1299 metadataTools.toVersionedReference( requestedResource ); 1300 return false; 1301 } 1302 catch ( RepositoryMetadataException re ) 1303 { 1304 return true; 1305 } 1306 } 1307 1308 protected File buildMergedIndexDirectory( List<String> repositories, String activePrincipal, 1309 DavServletRequest request, 1310 RepositoryGroupConfiguration repositoryGroupConfiguration ) 1311 throws DavException 1312 { 1313 1314 try 1315 { 1316 HttpSession session = request.getSession(); 1317 1318 Map<String, TemporaryGroupIndex> temporaryGroupIndexMap = 1319 (Map<String, TemporaryGroupIndex>) session.getAttribute( 1320 TemporaryGroupIndexSessionCleaner.TEMPORARY_INDEX_SESSION_KEY ); 1321 if ( temporaryGroupIndexMap == null ) 1322 { 1323 temporaryGroupIndexMap = new HashMap<>(); 1324 } 1325 1326 TemporaryGroupIndex tmp = temporaryGroupIndexMap.get( repositoryGroupConfiguration.getId() ); 1327 1328 if ( tmp != null && tmp.getDirectory() != null && tmp.getDirectory().exists() ) 1329 { 1330 if ( System.currentTimeMillis() - tmp.getCreationTime() > ( 1331 repositoryGroupConfiguration.getMergedIndexTtl() * 60 * 1000 ) ) 1332 { 1333 log.debug( MarkerFactory.getMarker( "group.merged.index" ), 1334 "tmp group index '{}' is too old so delete it", repositoryGroupConfiguration.getId() ); 1335 indexMerger.cleanTemporaryGroupIndex( tmp ); 1336 } 1337 else 1338 { 1339 log.debug( MarkerFactory.getMarker( "group.merged.index" ), 1340 "merged index for group '{}' found in cache", repositoryGroupConfiguration.getId() ); 1341 return tmp.getDirectory(); 1342 } 1343 } 1344 1345 Set<String> authzRepos = new HashSet<String>(); 1346 1347 String permission = WebdavMethodUtil.getMethodPermission( request.getMethod() ); 1348 1349 for ( String repository : repositories ) 1350 { 1351 try 1352 { 1353 if ( servletAuth.isAuthorized( activePrincipal, repository, permission ) ) 1354 { 1355 authzRepos.add( repository ); 1356 authzRepos.addAll( this.repositorySearch.getRemoteIndexingContextIds( repository ) ); 1357 } 1358 } 1359 catch ( UnauthorizedException e ) 1360 { 1361 // TODO: review exception handling 1362 1363 log.debug( "Skipping repository '{}' for user '{}': {}", repository, activePrincipal, 1364 e.getMessage() ); 1365 } 1366 } 1367 log.info( "generate temporary merged index for repository group '{}' for repositories '{}'", 1368 repositoryGroupConfiguration.getId(), authzRepos ); 1369 1370 File tempRepoFile = Files.createTempDirectory( "temp" ).toFile(); 1371 tempRepoFile.deleteOnExit(); 1372 1373 IndexMergerRequest indexMergerRequest = 1374 new IndexMergerRequest( authzRepos, true, repositoryGroupConfiguration.getId(), 1375 repositoryGroupConfiguration.getMergedIndexPath(), 1376 repositoryGroupConfiguration.getMergedIndexTtl() ).mergedIndexDirectory( 1377 tempRepoFile ).temporary( true ); 1378 1379 MergedRemoteIndexesTaskRequest taskRequest = 1380 new MergedRemoteIndexesTaskRequest( indexMergerRequest, indexMerger ); 1381 1382 MergedRemoteIndexesTask job = new MergedRemoteIndexesTask( taskRequest ); 1383 1384 IndexingContext indexingContext = job.execute().getIndexingContext(); 1385 1386 File mergedRepoDir = indexingContext.getIndexDirectoryFile(); 1387 TemporaryGroupIndex temporaryGroupIndex = 1388 new TemporaryGroupIndex( mergedRepoDir, indexingContext.getId(), repositoryGroupConfiguration.getId(), 1389 repositoryGroupConfiguration.getMergedIndexTtl() ) // 1390 .setCreationTime( new Date().getTime() ); 1391 temporaryGroupIndexMap.put( repositoryGroupConfiguration.getId(), temporaryGroupIndex ); 1392 session.setAttribute( TemporaryGroupIndexSessionCleaner.TEMPORARY_INDEX_SESSION_KEY, 1393 temporaryGroupIndexMap ); 1394 return mergedRepoDir; 1395 } 1396 catch ( RepositoryAdminException e ) 1397 { 1398 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e ); 1399 } 1400 catch ( IndexMergerException e ) 1401 { 1402 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e ); 1403 } 1404 catch ( IOException e ) 1405 { 1406 throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e ); 1407 } 1408 } 1409 1410 1411 public void setServletAuth( ServletAuthenticator servletAuth ) 1412 { 1413 this.servletAuth = servletAuth; 1414 } 1415 1416 public void setHttpAuth( HttpAuthenticator httpAuth ) 1417 { 1418 this.httpAuth = httpAuth; 1419 } 1420 1421 public void setScheduler( RepositoryArchivaTaskScheduler scheduler ) 1422 { 1423 this.scheduler = scheduler; 1424 } 1425 1426 public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration ) 1427 { 1428 this.archivaConfiguration = archivaConfiguration; 1429 } 1430 1431 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory ) 1432 { 1433 this.repositoryFactory = repositoryFactory; 1434 } 1435 1436 public void setRepositoryRequest( RepositoryRequest repositoryRequest ) 1437 { 1438 this.repositoryRequest = repositoryRequest; 1439 } 1440 1441 public void setConnectors( RepositoryProxyConnectors connectors ) 1442 { 1443 this.connectors = connectors; 1444 } 1445 1446 public RemoteRepositoryAdmin getRemoteRepositoryAdmin() 1447 { 1448 return remoteRepositoryAdmin; 1449 } 1450 1451 public void setRemoteRepositoryAdmin( RemoteRepositoryAdmin remoteRepositoryAdmin ) 1452 { 1453 this.remoteRepositoryAdmin = remoteRepositoryAdmin; 1454 } 1455 1456 public ManagedRepositoryAdmin getManagedRepositoryAdmin() 1457 { 1458 return managedRepositoryAdmin; 1459 } 1460 1461 public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin ) 1462 { 1463 this.managedRepositoryAdmin = managedRepositoryAdmin; 1464 } 1465}