You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ArchivaDavResourceFactory.java 64KB

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