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