Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

ArchivaDavResourceFactory.java 64KB

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