From 0593d6f86fcae4f606009b11eff3adb8f554333f Mon Sep 17 00:00:00 2001 From: sonatype-zion Date: Sun, 9 Jun 2019 20:05:32 -0400 Subject: [PATCH] upstream: b=master,r=d6a18e2c3d6731d5c7d56ecde7710a69ea4d0c6f,t=2019-06-07-1023-08730 --- assemblies/nexus-core-feature/pom.xml | 7 + .../src/main/feature/feature.xml | 1 + .../orient/DatabaseServerImplTest.java | 8 +- .../nexus/blobstore/api/BlobStore.java | 8 + .../nexus/blobstore/BlobStoreSupport.java | 5 + .../nexus/blobstore/group/BlobStoreGroup.java | 5 + .../internal/BlobStoreGroupDescriptor.java | 2 +- .../BlobStoreGroupDescriptorTest.groovy | 6 +- .../nexus/orient/DatabaseInstanceNames.java | 7 +- .../nexus/orient/restore/RestoreFile.java | 4 +- .../restore/RestoreServiceImplTest.groovy | 1 - .../static/rapture/NX/view/SignIn.js | 26 +++ .../nexus/repository/ETagHeaderUtils.java | 46 +++++ .../repository/proxy/ProxyFacetSupport.java | 29 +-- .../view/handlers/ContentHeadersHandler.java | 3 +- .../nexus/repository/ETagHeaderUtilsTest.java | 51 +++++ .../handlers/ContentHeadersHandlerTest.java | 12 ++ .../config/AdminPasswordFileManager.java | 2 + .../AdminPasswordFileManagerImpl.java | 5 + .../AdminPasswordFileManagerImplTest.java | 6 + .../rapture/NX/coreui/app/PluginConfig.js | 8 +- .../rapture/NX/coreui/app/PluginStrings.js | 11 -- .../NX/coreui/controller/LicenseUsers.js | 107 ----------- .../coreui/view/licensing/LicenseUserList.js | 73 ------- .../coreui/view/licensing/LicensingDetails.js | 5 - .../coreui/view/repository/recipe/AptProxy.js | 2 + .../internal/OnboardingStateContributor.java | 28 ++- .../NX/onboarding/app/PluginStrings.js | 3 +- .../NX/onboarding/controller/Onboarding.js | 18 +- .../OnboardingStateContributorTest.java | 50 ++--- .../nexus/repository/apt/AptFacet.java | 14 +- .../repository/apt/AptRestoreFacet.java} | 41 ++-- .../repository/apt/AptUploadHandler.java | 2 +- .../apt/internal/AptBrowseNodeGenerator.java | 4 +- .../apt/internal/AptComponentDirector.java | 98 ++++++++++ .../repository/apt/internal/AptFacetImpl.java | 47 +++-- .../apt/internal/AptPackageParser.java | 11 ++ .../apt/internal/AptRestoreFacetImpl.java | 92 +++++++++ .../apt/internal/AptSecurityFacet.java | 2 +- .../apt/internal/AptWritePolicySelector.java | 2 +- .../repository/apt/internal/FacetHelper.java | 6 +- .../apt/internal/debian/ControlFile.java | 22 +-- .../internal/debian/ControlFileParser.java | 4 +- .../apt/internal/debian/DebianVersion.java | 14 +- .../apt/internal/debian/PackageInfo.java | 4 +- .../apt/internal/debian/Release.java | 4 +- .../repository/apt/internal/debian/Utils.java | 2 +- .../apt/internal/gpg/AptSigningFacet.java | 4 +- .../apt/internal/gpg/AptSigningHandler.java | 4 +- .../AptHostedComponentMaintenanceFacet.java | 2 +- .../apt/internal/hosted/AptHostedFacet.java | 36 ++-- .../apt/internal/hosted/AptHostedHandler.java | 9 +- .../apt/internal/hosted/AptHostedRecipe.java | 9 +- .../hosted/AptHostedSnapshotFacet.java | 4 +- .../hosted/CompressingTempFileStore.java | 4 +- .../apt/internal/proxy/AptProxyFacet.java | 28 +-- .../apt/internal/proxy/AptProxyRecipe.java | 14 +- .../internal/proxy/AptProxySnapshotFacet.java | 2 +- .../AllSnapshotComponentSelector.java | 4 +- .../internal/snapshot/AptSnapshotFacet.java | 8 +- .../snapshot/AptSnapshotFacetSupport.java | 16 +- .../internal/snapshot/AptSnapshotHandler.java | 8 +- .../FilteredSnapshotComponentSelector.java | 6 +- .../snapshot/SnapshotComponentSelector.java | 4 +- .../apt/internal/snapshot/SnapshotItem.java | 6 +- .../internal/AptComponentDirectorTest.java | 108 +++++++++++ .../npm/internal/NpmStreamPayload.java | 18 +- plugins/nexus-restore-apt/pom.xml | 66 +++++++ .../apt/internal/AptRestoreBlobData.java} | 25 ++- .../apt/internal/AptRestoreBlobStrategy.java | 124 ++++++++++++ .../internal/AptRestoreBlobStrategyTest.java | 179 ++++++++++++++++++ plugins/pom.xml | 15 ++ revision.txt | 2 +- 73 files changed, 1145 insertions(+), 468 deletions(-) create mode 100644 components/nexus-repository/src/main/java/org/sonatype/nexus/repository/ETagHeaderUtils.java create mode 100644 components/nexus-repository/src/test/java/org/sonatype/nexus/repository/ETagHeaderUtilsTest.java delete mode 100644 plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/controller/LicenseUsers.js delete mode 100644 plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/licensing/LicenseUserList.js rename plugins/{nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/store/LicenseUser.js => nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/AptRestoreFacet.java} (53%) create mode 100644 plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptComponentDirector.java create mode 100644 plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptRestoreFacetImpl.java create mode 100644 plugins/nexus-repository-apt/src/test/java/org/sonatype/nexus/repository/apt/internal/AptComponentDirectorTest.java create mode 100644 plugins/nexus-restore-apt/pom.xml rename plugins/{nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/model/LicenseUser.js => nexus-restore-apt/src/main/java/org/sonatype/nexus/blobstore/restore/apt/internal/AptRestoreBlobData.java} (64%) create mode 100644 plugins/nexus-restore-apt/src/main/java/org/sonatype/nexus/blobstore/restore/apt/internal/AptRestoreBlobStrategy.java create mode 100644 plugins/nexus-restore-apt/src/test/java/org/sonatype/nexus/blobstore/restore/apt/internal/AptRestoreBlobStrategyTest.java diff --git a/assemblies/nexus-core-feature/pom.xml b/assemblies/nexus-core-feature/pom.xml index 474b199b42..f29e7dc7d1 100644 --- a/assemblies/nexus-core-feature/pom.xml +++ b/assemblies/nexus-core-feature/pom.xml @@ -144,6 +144,13 @@ xml + + org.sonatype.nexus.plugins + nexus-restore-apt + features + xml + + org.sonatype.nexus.plugins nexus-restore-maven diff --git a/assemblies/nexus-core-feature/src/main/feature/feature.xml b/assemblies/nexus-core-feature/src/main/feature/feature.xml index d8257ad498..51ab5ed125 100644 --- a/assemblies/nexus-core-feature/src/main/feature/feature.xml +++ b/assemblies/nexus-core-feature/src/main/feature/feature.xml @@ -28,6 +28,7 @@ nexus-repository-raw nexus-restore-maven nexus-blobstore-s3 + nexus-restore-apt nexus-restore-npm nexus-restore-pypi nexus-restore-raw diff --git a/components/nexus-base/src/test/java/org/sonatype/nexus/internal/orient/DatabaseServerImplTest.java b/components/nexus-base/src/test/java/org/sonatype/nexus/internal/orient/DatabaseServerImplTest.java index 6bb5418963..3884c026e4 100644 --- a/components/nexus-base/src/test/java/org/sonatype/nexus/internal/orient/DatabaseServerImplTest.java +++ b/components/nexus-base/src/test/java/org/sonatype/nexus/internal/orient/DatabaseServerImplTest.java @@ -85,7 +85,7 @@ public void testOnlyOurDatabasesAreReported() { candidates.forEach(this::createDatabase); - assertThat(underTest.databases(), containsInAnyOrder("component", "config", "security", "accesslog")); + assertThat(underTest.databases(), containsInAnyOrder("component", "config", "security")); } @Test @@ -109,13 +109,9 @@ public void testDatabasesAreReportedAsTheyAppear() { assertThat(underTest.databases(), containsInAnyOrder("component", "config")); - createDatabase("accesslog"); - - assertThat(underTest.databases(), containsInAnyOrder("component", "config", "accesslog")); - createDatabase("security"); - assertThat(underTest.databases(), containsInAnyOrder("component", "config", "security", "accesslog")); + assertThat(underTest.databases(), containsInAnyOrder("component", "config", "security")); } private void createDatabase(final String name) { diff --git a/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStore.java b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStore.java index cfcb883a44..ce37221cf3 100644 --- a/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStore.java +++ b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStore.java @@ -262,4 +262,12 @@ default boolean isWritable() { * @since 3.15 */ boolean isStarted(); + + /** + * Returns true if the blobstore has no blobs within it. + * + * @return {@code true} if the blobstore has no blobs within it. + * @since 3.next + */ + boolean isEmpty(); } diff --git a/components/nexus-blobstore/src/main/java/org/sonatype/nexus/blobstore/BlobStoreSupport.java b/components/nexus-blobstore/src/main/java/org/sonatype/nexus/blobstore/BlobStoreSupport.java index 04266afd72..33de617dd6 100644 --- a/components/nexus-blobstore/src/main/java/org/sonatype/nexus/blobstore/BlobStoreSupport.java +++ b/components/nexus-blobstore/src/main/java/org/sonatype/nexus/blobstore/BlobStoreSupport.java @@ -249,4 +249,9 @@ private void updateTimer(final String name, final long value) { timer.update(value, TimeUnit.NANOSECONDS); } } + + @Override + public boolean isEmpty() { + return !getBlobIdStream().findAny().isPresent(); + } } diff --git a/components/nexus-blobstore/src/main/java/org/sonatype/nexus/blobstore/group/BlobStoreGroup.java b/components/nexus-blobstore/src/main/java/org/sonatype/nexus/blobstore/group/BlobStoreGroup.java index 9c2739b678..b8aaa773b7 100644 --- a/components/nexus-blobstore/src/main/java/org/sonatype/nexus/blobstore/group/BlobStoreGroup.java +++ b/components/nexus-blobstore/src/main/java/org/sonatype/nexus/blobstore/group/BlobStoreGroup.java @@ -299,6 +299,11 @@ public boolean isWritable() { return false; } + @Override + public boolean isEmpty() { + return members.get().stream().map(BlobStore::isEmpty).reduce(true, Boolean::logicalAnd); + } + @Override public boolean exists(final BlobId blobId) { return members.get().stream() diff --git a/components/nexus-blobstore/src/main/java/org/sonatype/nexus/blobstore/group/internal/BlobStoreGroupDescriptor.java b/components/nexus-blobstore/src/main/java/org/sonatype/nexus/blobstore/group/internal/BlobStoreGroupDescriptor.java index 1d6329124b..ed3ba75d42 100644 --- a/components/nexus-blobstore/src/main/java/org/sonatype/nexus/blobstore/group/internal/BlobStoreGroupDescriptor.java +++ b/components/nexus-blobstore/src/main/java/org/sonatype/nexus/blobstore/group/internal/BlobStoreGroupDescriptor.java @@ -183,7 +183,7 @@ private void validateOnlyEmptyOrNotWritableExistingMembersRemoved(final String n for (String existingMemberName : memberNames(currentConfiguration)) { if (!memberNames.contains(existingMemberName)) { BlobStore existingMember = blobStoreManager.get(existingMemberName); - if (existingMember.isWritable() || existingMember.getMetrics().getBlobCount() > 0L) { + if (existingMember.isWritable() || !existingMember.isEmpty()) { throw new ValidationException( format("Blob Store '%s' cannot be removed from Blob Store Group '%s', " + "use 'Admin - Remove a member from a blob store group' task instead", diff --git a/components/nexus-blobstore/src/test/java/org/sonatype/nexus/blobstore/group/internal/BlobStoreGroupDescriptorTest.groovy b/components/nexus-blobstore/src/test/java/org/sonatype/nexus/blobstore/group/internal/BlobStoreGroupDescriptorTest.groovy index 0e5f0d0c21..720e4ef90e 100644 --- a/components/nexus-blobstore/src/test/java/org/sonatype/nexus/blobstore/group/internal/BlobStoreGroupDescriptorTest.groovy +++ b/components/nexus-blobstore/src/test/java/org/sonatype/nexus/blobstore/group/internal/BlobStoreGroupDescriptorTest.groovy @@ -15,6 +15,7 @@ package org.sonatype.nexus.blobstore.group.internal import javax.validation.ValidationException import org.sonatype.nexus.blobstore.BlobStoreUtil +import org.sonatype.nexus.blobstore.api.BlobId import org.sonatype.nexus.blobstore.api.BlobStore import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration import org.sonatype.nexus.blobstore.api.BlobStoreManager @@ -127,9 +128,8 @@ class BlobStoreGroupDescriptorTest def 'members cant be removed directly unless read only and empty'() { given: 'an existing blob store group with two members' blobStores.store1 = mockBlobStore('store1', FILE) - def nonEmptyMetrics = Mock(BlobStoreMetrics) - nonEmptyMetrics.getBlobCount() >> 1L - blobStores.nonEmptyStore = mockBlobStore('nonEmptyStore', FILE, [:], true, nonEmptyMetrics) + blobStores.nonEmptyStore = mockBlobStore('nonEmptyStore', FILE, [:], true) + blobStores.nonEmptyStore.getBlobIdStream() >> [Mock(BlobId)].stream() blobStores.group1 = mockBlobStoreGroup('group1', [blobStores.store1, blobStores.nonEmptyStore]) when: 'the member is removed' diff --git a/components/nexus-orient/src/main/java/org/sonatype/nexus/orient/DatabaseInstanceNames.java b/components/nexus-orient/src/main/java/org/sonatype/nexus/orient/DatabaseInstanceNames.java index 13de3dfd76..64fcc2a820 100644 --- a/components/nexus-orient/src/main/java/org/sonatype/nexus/orient/DatabaseInstanceNames.java +++ b/components/nexus-orient/src/main/java/org/sonatype/nexus/orient/DatabaseInstanceNames.java @@ -38,13 +38,8 @@ public class DatabaseInstanceNames */ public static final String SECURITY = "security"; - /** - * Name of the database storing access log data for licensing. - */ - public static final String ACCESSLOG = "accesslog"; - public static final Set DATABASE_NAMES = ImmutableSet.of( - ACCESSLOG, CONFIG, COMPONENT, SECURITY); + CONFIG, COMPONENT, SECURITY); private DatabaseInstanceNames() { // no construction diff --git a/components/nexus-orient/src/main/java/org/sonatype/nexus/orient/restore/RestoreFile.java b/components/nexus-orient/src/main/java/org/sonatype/nexus/orient/restore/RestoreFile.java index fdb64ff2f5..ca682edabb 100644 --- a/components/nexus-orient/src/main/java/org/sonatype/nexus/orient/restore/RestoreFile.java +++ b/components/nexus-orient/src/main/java/org/sonatype/nexus/orient/restore/RestoreFile.java @@ -32,8 +32,8 @@ * * Examples: *
- *   accesslog-2017-07-06-11-16-49-3.4.1.bak
- *   accesslog-2017-07-06-11-16-49.bak
+ *   component-2017-07-06-11-16-49-3.4.1.bak
+ *   component-2017-07-06-11-16-49.bak
  * 
* * The nxrm_version will not be present for files generated pre-3.4.1. diff --git a/components/nexus-orient/src/test/java/org/sonatype/nexus/orient/restore/RestoreServiceImplTest.groovy b/components/nexus-orient/src/test/java/org/sonatype/nexus/orient/restore/RestoreServiceImplTest.groovy index 4b7850bc67..66079fdf0b 100644 --- a/components/nexus-orient/src/test/java/org/sonatype/nexus/orient/restore/RestoreServiceImplTest.groovy +++ b/components/nexus-orient/src/test/java/org/sonatype/nexus/orient/restore/RestoreServiceImplTest.groovy @@ -67,7 +67,6 @@ class RestoreServiceImplTest restorer.getPendingRestore(names.next()) >> mockRestoreFile(_, '2017-07-06-11-16-49', null) restorer.getPendingRestore(names.next()) >> mockRestoreFile(_, '2017-07-06-11-16-50', null) restorer.getPendingRestore(names.next()) >> mockRestoreFile(_, '2017-07-06-11-16-51', null) - restorer.getPendingRestore(names.next()) >> mockRestoreFile(_, '2017-07-06-11-16-52', null) when: 'start is executed' restoreService.start() diff --git a/components/nexus-rapture/src/main/resources/static/rapture/NX/view/SignIn.js b/components/nexus-rapture/src/main/resources/static/rapture/NX/view/SignIn.js index 776536b4cb..e823fd3abd 100644 --- a/components/nexus-rapture/src/main/resources/static/rapture/NX/view/SignIn.js +++ b/components/nexus-rapture/src/main/resources/static/rapture/NX/view/SignIn.js @@ -78,6 +78,32 @@ Ext.define('NX.view.SignIn', { }); me.callParent(); + }, + + addMessage: function(message) { + var me = this, + htmlMessage = '
' + message + '

', + messageCmp = me.down('#signinMessage'); + + if (messageCmp) { + messageCmp.html(htmlMessage); + } + else { + me.down('form').insert(0, { + xtype: 'component', + itemId: 'signinMessage', + html: htmlMessage + }); + } + }, + + clearMessage: function() { + var me = this, + messageCmp = me.down('#signinMessage'); + + if (messageCmp) { + me.down('form').remove(messageCmp); + } } }); diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/ETagHeaderUtils.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/ETagHeaderUtils.java new file mode 100644 index 0000000000..2cbdcf5eac --- /dev/null +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/ETagHeaderUtils.java @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +import static org.apache.commons.lang.StringUtils.isEmpty; + +public class ETagHeaderUtils +{ + public static final String WEAK_DESIGNATOR = "W/"; + + private ETagHeaderUtils() { + } + + /** + * Adds quotes to etag header per spec. + * https://tools.ietf.org/html/rfc7232#section-2.3 + */ + public static String quote(final String etag) { + if (etag.startsWith(WEAK_DESIGNATOR)) { + return etag; + } else { + return "\"" + etag + "\""; + } + } + + /** + * Removes quotes from etag header. + */ + public static String extract(final String etag) { + if (!isEmpty(etag) && etag.startsWith("\"") && etag.endsWith("\"")) { + return etag.substring(1, etag.length() - 1); + } else { + return etag; + } + } +} diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyFacetSupport.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyFacetSupport.java index 66d13b7d50..a222728409 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyFacetSupport.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/proxy/ProxyFacetSupport.java @@ -27,6 +27,7 @@ import org.sonatype.nexus.common.io.Cooperation; import org.sonatype.nexus.common.io.CooperationFactory; import org.sonatype.nexus.repository.BadRequestException; +import org.sonatype.nexus.repository.ETagHeaderUtils; import org.sonatype.nexus.repository.FacetSupport; import org.sonatype.nexus.repository.InvalidContentException; import org.sonatype.nexus.repository.cache.CacheController; @@ -45,7 +46,6 @@ import org.sonatype.nexus.validation.constraint.Url; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Strings; import com.google.common.io.Closeables; import com.google.common.net.HttpHeaders; import org.apache.http.Header; @@ -73,6 +73,7 @@ public abstract class ProxyFacetSupport extends FacetSupport implements ProxyFacet { + @VisibleForTesting static final String CONFIG_KEY = "proxy"; @@ -416,7 +417,7 @@ protected Content fetch(String url, Context context, @Nullable Content stale) th } final String etag = stale.getAttributes().get(Content.CONTENT_ETAG, String.class); if (etag != null) { - request.addHeader(HttpHeaders.IF_NONE_MATCH, "\"" + etag + "\""); + request.addHeader(HttpHeaders.IF_NONE_MATCH, ETagHeaderUtils.quote(etag)); } } log.debug("Fetching: {}", request); @@ -435,7 +436,9 @@ protected Content fetch(String url, Context context, @Nullable Content stale) th final Content result = createContent(context, response); result.getAttributes().set(Content.CONTENT_LAST_MODIFIED, extractLastModified(request, response)); - result.getAttributes().set(Content.CONTENT_ETAG, extractETag(response)); + final Header etagHeader = response.getLastHeader(HttpHeaders.ETAG); + result.getAttributes().set(Content.CONTENT_ETAG, etagHeader == null ? null : ETagHeaderUtils.extract(etagHeader.getValue())); + result.getAttributes().set(CacheInfo.class, cacheInfo); return result; } @@ -509,26 +512,6 @@ private DateTime extractLastModified(final HttpRequestBase request, final HttpRe return null; } - /** - * Extract ETag from response if possible, or {@code null}. - */ - @Nullable - private String extractETag(final HttpResponse response) { - final Header etagHeader = response.getLastHeader(HttpHeaders.ETAG); - if (etagHeader != null) { - final String etag = etagHeader.getValue(); - if (!Strings.isNullOrEmpty(etag)) { - if (etag.startsWith("\"") && etag.endsWith("\"")) { - return etag.substring(1, etag.length() - 1); - } - else { - return etag; - } - } - } - return null; - } - /** * For whatever component/asset */ diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ContentHeadersHandler.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ContentHeadersHandler.java index f7199b434d..c02639dd78 100644 --- a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ContentHeadersHandler.java +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/view/handlers/ContentHeadersHandler.java @@ -17,6 +17,7 @@ import javax.inject.Singleton; import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.ETagHeaderUtils; import org.sonatype.nexus.repository.view.Content; import org.sonatype.nexus.repository.view.Context; import org.sonatype.nexus.repository.view.Handler; @@ -53,7 +54,7 @@ public Response handle(@Nonnull final Context context) throws Exception { } final String etag = content.getAttributes().get(Content.CONTENT_ETAG, String.class); if (etag != null) { - response.getHeaders().set(HttpHeaders.ETAG, "\"" + etag + "\""); + response.getHeaders().set(HttpHeaders.ETAG, ETagHeaderUtils.quote(etag)); } } return response; diff --git a/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/ETagHeaderUtilsTest.java b/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/ETagHeaderUtilsTest.java new file mode 100644 index 0000000000..2ae6e17639 --- /dev/null +++ b/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/ETagHeaderUtilsTest.java @@ -0,0 +1,51 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class ETagHeaderUtilsTest +{ + @Test + public void quoteStrong() { + assertEquals("\"foobar\"", ETagHeaderUtils.quote("foobar")); + } + + @Test + public void quoteWeak() { + assertEquals("W/\"foobar\"", ETagHeaderUtils.quote("W/\"foobar\"")); + } + + @Test + public void extractNull() { + assertNull(ETagHeaderUtils.extract(null)); + } + + @Test + public void extractEmpty() { + assertEquals("", ETagHeaderUtils.extract("")); + } + + @Test + public void extractStrong() { + assertEquals("foobar", ETagHeaderUtils.extract("\"foobar\"")); + } + + @Test + public void extractWeak() { + assertEquals("W/\"foobar\"", ETagHeaderUtils.extract("W/\"foobar\"")); + } +} diff --git a/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/ContentHeadersHandlerTest.java b/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/ContentHeadersHandlerTest.java index f42d045483..a9a2b30f48 100644 --- a/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/ContentHeadersHandlerTest.java +++ b/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/view/handlers/ContentHeadersHandlerTest.java @@ -70,6 +70,18 @@ public void okResponse() throws Exception { assertThat(r.getHeaders().get(HttpHeaders.ETAG), equalTo("\"etag\"")); } + @Test + public void okResponseWithWeakEtag() throws Exception { + final Content content = new Content(new StringPayload(payloadString, "text/plain")); + content.getAttributes().set(Content.CONTENT_LAST_MODIFIED, now); + content.getAttributes().set(Content.CONTENT_ETAG, "W/\"etag\""); + when(context.proceed()).thenReturn(HttpResponses.ok(content)); + final Response r = subject.handle(context); + assertThat(r.getStatus().isSuccessful(), is(true)); + assertThat(r.getHeaders().get(HttpHeaders.LAST_MODIFIED), equalTo(DateTimeUtils.formatDateTime(now))); + assertThat(r.getHeaders().get(HttpHeaders.ETAG), equalTo("W/\"etag\"")); + } + @Test public void okResponseNoExtraData() throws Exception { when(context.proceed()).thenReturn( diff --git a/components/nexus-security/src/main/java/org/sonatype/nexus/security/config/AdminPasswordFileManager.java b/components/nexus-security/src/main/java/org/sonatype/nexus/security/config/AdminPasswordFileManager.java index cc8c9bf946..1041fc3770 100644 --- a/components/nexus-security/src/main/java/org/sonatype/nexus/security/config/AdminPasswordFileManager.java +++ b/components/nexus-security/src/main/java/org/sonatype/nexus/security/config/AdminPasswordFileManager.java @@ -21,6 +21,8 @@ public interface AdminPasswordFileManager { boolean exists(); + String getPath(); + boolean writeFile(String password) throws IOException; String readFile() throws IOException; diff --git a/components/nexus-security/src/main/java/org/sonatype/nexus/security/internal/AdminPasswordFileManagerImpl.java b/components/nexus-security/src/main/java/org/sonatype/nexus/security/internal/AdminPasswordFileManagerImpl.java index 5a1455c863..294cffd9a0 100644 --- a/components/nexus-security/src/main/java/org/sonatype/nexus/security/internal/AdminPasswordFileManagerImpl.java +++ b/components/nexus-security/src/main/java/org/sonatype/nexus/security/internal/AdminPasswordFileManagerImpl.java @@ -77,6 +77,11 @@ public boolean exists() { return adminPasswordFile.exists(); } + @Override + public String getPath() { + return adminPasswordFile.getAbsolutePath(); + } + @Override public String readFile() throws IOException { if (adminPasswordFile.exists()) { diff --git a/components/nexus-security/src/test/java/org/sonatype/nexus/security/internal/AdminPasswordFileManagerImplTest.java b/components/nexus-security/src/test/java/org/sonatype/nexus/security/internal/AdminPasswordFileManagerImplTest.java index 457524a4d8..d8cb12ca6d 100644 --- a/components/nexus-security/src/test/java/org/sonatype/nexus/security/internal/AdminPasswordFileManagerImplTest.java +++ b/components/nexus-security/src/test/java/org/sonatype/nexus/security/internal/AdminPasswordFileManagerImplTest.java @@ -55,6 +55,12 @@ public void testExists() throws Exception { assertThat(underTest.exists(), is(false)); } + @Test + public void testGetPath() { + File passwordFile = new File(applicationDirectories.getWorkDirectory(), "admin.password"); + assertThat(passwordFile.getAbsolutePath(), is(underTest.getPath())); + } + @Test public void testWriteFile() throws Exception { underTest.writeFile("testpass"); diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginConfig.js b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginConfig.js index 7182536711..b38d731945 100644 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginConfig.js +++ b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginConfig.js @@ -175,12 +175,8 @@ Ext.define('NX.coreui.app.PluginConfig', { return NX.app.Application.bundleActive('org.sonatype.nexus.plugins.nexus-coreui-plugin'); } }, - { id: 'NX.coreui.controller.Licensing', - active: function () { - return NX.app.Application.bundleActive('com.sonatype.nexus.plugins.nexus-licensing-plugin'); - } - }, - { id: 'NX.coreui.controller.LicenseUsers', + { + id: 'NX.coreui.controller.Licensing', active: function () { return NX.app.Application.bundleActive('com.sonatype.nexus.plugins.nexus-licensing-plugin'); } diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginStrings.js b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginStrings.js index bbe4aa15cd..8411c5b43f 100644 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginStrings.js +++ b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginStrings.js @@ -1310,7 +1310,6 @@ Ext.define('NX.coreui.app.PluginStrings', { Licensing_LicensingDetails_ExpirationDate_FieldLabel: 'Expiration date', Licensing_LicensingDetails_Type_FieldLabel: 'License type', Licensing_LicensingDetails_LicensedUsers_FieldLabel: 'Number of licensed users', - Licensing_LicensingDetails_Connections_FieldLabel: 'Number of unique IP addresses that have connected in the last 7 days', Licensing_LicensingDetails_Fingerprint_FieldLabel: 'Fingerprint', Licensing_LicensingDetails_InstallLicense_Title: 'Install license', Licensing_LicensingDetails_InstallLicense_Html: '

Installing a new license requires restarting the server to take effect

', @@ -1323,16 +1322,6 @@ Ext.define('NX.coreui.app.PluginStrings', { Licensing_Install_Success: 'License installed. Restart is only required if you are enabling new PRO features.', Licensing_Authentication_Validation: '{0} a license requires validation of your credentials.', - // Admin -> System -> Licensing -> Recent Connections - LicenseUsers_Title: 'Recent Connections', - LicenseUsers_Description: 'Reports active users in the last 7 days', - Licensing_LicenseUserList_Download_Button: 'Download', - Licensing_LicenseUserList_IP_Header: 'IP', - Licensing_LicenseUserList_Date_Header: 'Date', - Licensing_LicenseUserList_User_Header: 'User', - Licensing_LicenseUserList_Agent_Header: 'User agent', - Licensing_LicenseUserList_EmptyText: 'No active users in the last 7 days.', - //Nexus Lifecycle -> Server Clm_ClmSettings_Permission_Error: 'You do not have permission to configure IQ Server', Clm_Text: 'IQ Server', diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/controller/LicenseUsers.js b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/controller/LicenseUsers.js deleted file mode 100644 index 3098d6797a..0000000000 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/controller/LicenseUsers.js +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Sonatype Nexus (TM) Open Source Version - * Copyright (c) 2008-present Sonatype, Inc. - * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. - * - * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, - * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. - * - * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks - * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the - * Eclipse Foundation. All other trademarks are the property of their respective owners. - */ -/*global Ext, NX*/ - -/** - * License Users controller. - * - * @since 3.0 - */ -Ext.define('NX.coreui.controller.LicenseUsers', { - extend: 'NX.app.Controller', - requires: [ - 'NX.Permissions', - 'NX.util.DownloadHelper', - 'NX.util.Url', - 'NX.I18n' - ], - - stores: [ - 'LicenseUser' - ], - views: [ - 'licensing.LicenseUserList' - ], - refs: [ - { - ref: 'list', - selector: 'nx-coreui-licensing-licenseuser-list' - } - ], - - /** - * @override - */ - init: function () { - var me = this; - - me.getApplication().getIconController().addIcons({ - 'licenseuser-default': { - file: 'ssl_certificates.png', - variants: ['x16', 'x32'] - } - }); - - me.getApplication().getFeaturesController().registerFeature({ - mode: 'admin', - path: '/System/Licensing/License Users', - text: NX.I18n.get('LicenseUsers_Title'), - description: NX.I18n.get('LicenseUsers_Description'), - view: { xtype: 'nx-coreui-licensing-licenseuser-list' }, - iconConfig: { - file: 'ssl_tls_manager.png', - variants: ['x16', 'x32'] - }, - visible: function () { - return NX.Permissions.check('nexus:licensing:read'); - } - }, me); - - me.listen({ - controller: { - '#Refresh': { - refresh: me.load - } - }, - component: { - 'nx-coreui-licensing-licenseuser-list': { - afterrender: me.load - }, - 'nx-coreui-licensing-licenseuser-list button[action=download]': { - click: me.download - } - } - }); - }, - - /** - * @private - * Load active user store. - */ - load: function () { - var list = this.getList(); - - if (list) { - list.getStore().load(); - } - }, - - /** - * @private - * Download active users in CSV format. - */ - download: function () { - NX.util.DownloadHelper.downloadUrl(NX.util.Url.urlOf('service/rest/licensing/csv_access_data')); - } - -}); diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/licensing/LicenseUserList.js b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/licensing/LicenseUserList.js deleted file mode 100644 index 32c3e98e66..0000000000 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/licensing/LicenseUserList.js +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Sonatype Nexus (TM) Open Source Version - * Copyright (c) 2008-present Sonatype, Inc. - * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. - * - * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, - * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. - * - * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks - * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the - * Eclipse Foundation. All other trademarks are the property of their respective owners. - */ -/*global Ext, NX*/ - -/** - * License User grid. - * - * @since 3.0 - */ -Ext.define('NX.coreui.view.licensing.LicenseUserList', { - extend: 'Ext.grid.Panel', - alias: 'widget.nx-coreui-licensing-licenseuser-list', - requires: [ - 'NX.I18n' - ], - - stateful: true, - stateId: 'nx-coreui-licensing-licenseuser-list', - - /** - * @override - */ - initComponent: function() { - Ext.apply(this, { - store: 'LicenseUser', - - columns: [ - { - xtype: 'nx-iconcolumn', - width: 36, - iconVariant: 'x16', - iconName: function () { - return 'licenseuser-default'; - } - }, - { header: NX.I18n.get('Licensing_LicenseUserList_IP_Header'), dataIndex: 'ip', stateId: 'ip', flex: 1 }, - { header: NX.I18n.get('Licensing_LicenseUserList_Date_Header'), dataIndex: 'timestamp', stateId: 'timestamp', flex: 1 }, - { header: NX.I18n.get('Licensing_LicenseUserList_User_Header'), dataIndex: 'userId', stateId: 'userId', flex: 1 }, - { header: NX.I18n.get('Licensing_LicenseUserList_Agent_Header'), dataIndex: 'userAgent', stateId: 'userAgent', flex: 2 } - ], - - viewConfig: { - emptyText: NX.I18n.get('Licensing_LicenseUserList_EmptyText'), - deferEmptyText: false - }, - - dockedItems: [{ - xtype: 'nx-actions', - items: { - xtype: 'button', - text: NX.I18n.get('Licensing_LicenseUserList_Download_Button'), - glyph: 'xf019@FontAwesome' /* fa-download */, - action: 'download' - } - }], - - plugins: [{ ptype: 'gridfilterbox', emptyText: 'No license user matched criteria "$filter"' }] - }); - - this.callParent(); - } - -}); diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/licensing/LicensingDetails.js b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/licensing/LicensingDetails.js index 793ff5a6a9..d2e67f83e4 100644 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/licensing/LicensingDetails.js +++ b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/licensing/LicensingDetails.js @@ -88,11 +88,6 @@ Ext.define('NX.coreui.view.licensing.LicensingDetails', { fieldLabel: NX.I18n.get('Licensing_LicensingDetails_LicensedUsers_FieldLabel'), xtype: 'displayfield' }, - { - name: 'connections', - fieldLabel: NX.I18n.get('Licensing_LicensingDetails_Connections_FieldLabel'), - xtype: 'displayfield' - }, { name: 'fingerprint', fieldLabel: NX.I18n.get('Licensing_LicensingDetails_Fingerprint_FieldLabel'), diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/repository/recipe/AptProxy.js b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/repository/recipe/AptProxy.js index 080d316582..de8f91b2bd 100644 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/repository/recipe/AptProxy.js +++ b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/repository/recipe/AptProxy.js @@ -21,6 +21,7 @@ Ext.define('NX.coreui.view.repository.recipe.AptProxy', { requires: [ 'NX.coreui.view.repository.facet.AptFacet', 'NX.coreui.view.repository.facet.ProxyFacet', + 'NX.coreui.view.repository.facet.RoutingRuleFacet', 'NX.coreui.view.repository.facet.StorageFacet', 'NX.coreui.view.repository.facet.HttpClientFacet', 'NX.coreui.view.repository.facet.NegativeCacheFacet', @@ -36,6 +37,7 @@ Ext.define('NX.coreui.view.repository.recipe.AptProxy', { me.items = [ {xtype: 'nx-aptui-repository-apt-facet'}, {xtype: 'nx-coreui-repository-proxy-facet'}, + {xtype: 'nx-coreui-repository-routing-rule-facet'}, {xtype: 'nx-coreui-repository-storage-facet'}, {xtype: 'nx-coreui-repository-negativecache-facet'}, {xtype: 'nx-coreui-repository-cleanup-policy-facet'}, diff --git a/plugins/nexus-onboarding-plugin/src/main/java/org/sonatype/nexus/onboarding/internal/OnboardingStateContributor.java b/plugins/nexus-onboarding-plugin/src/main/java/org/sonatype/nexus/onboarding/internal/OnboardingStateContributor.java index 834210a68b..b3ffd76772 100644 --- a/plugins/nexus-onboarding-plugin/src/main/java/org/sonatype/nexus/onboarding/internal/OnboardingStateContributor.java +++ b/plugins/nexus-onboarding-plugin/src/main/java/org/sonatype/nexus/onboarding/internal/OnboardingStateContributor.java @@ -12,7 +12,7 @@ */ package org.sonatype.nexus.onboarding.internal; -import java.util.Collections; +import java.util.HashMap; import java.util.Map; import javax.annotation.Nullable; @@ -23,9 +23,7 @@ import org.sonatype.nexus.onboarding.OnboardingConfiguration; import org.sonatype.nexus.onboarding.OnboardingManager; import org.sonatype.nexus.rapture.StateContributor; -import org.sonatype.nexus.security.SecurityHelper; - -import org.apache.shiro.subject.Subject; +import org.sonatype.nexus.security.config.AdminPasswordFileManager; import static com.google.common.base.Preconditions.checkNotNull; @@ -41,18 +39,18 @@ public class OnboardingStateContributor private final OnboardingManager onboardingManager; - private final SecurityHelper securityHelper; + private final AdminPasswordFileManager adminPasswordFileManager; private boolean needsOnboarding = true; @Inject public OnboardingStateContributor(final OnboardingConfiguration onboardingConfiguration, final OnboardingManager onboardingManager, - final SecurityHelper securityHelper) + final AdminPasswordFileManager adminPasswordFileManager) { this.onboardingConfiguration = checkNotNull(onboardingConfiguration); this.onboardingManager = checkNotNull(onboardingManager); - this.securityHelper = checkNotNull(securityHelper); + this.adminPasswordFileManager = checkNotNull(adminPasswordFileManager); } @Nullable @@ -61,18 +59,16 @@ public Map getState() { //cache the onboarding flag, once it's false there is no longer a need to check anymore needsOnboarding = needsOnboarding && onboardingConfiguration.isEnabled() && onboardingManager.needsOnboarding(); - if (needsOnboarding && isAdmin(securityHelper.subject())) { - return Collections.singletonMap("onboarding.required", true); - } + HashMap properties = new HashMap<>(); - return null; - } + if (needsOnboarding) { + properties.put("onboarding.required", true); + } - private boolean isAdmin(Subject subject) { - if (subject == null || subject.getPrincipal() == null) { - return false; + if (adminPasswordFileManager.exists()) { + properties.put("admin.password.file", adminPasswordFileManager.getPath()); } - return subject.isPermitted("nexus:*"); + return properties.isEmpty() ? null : properties; } } diff --git a/plugins/nexus-onboarding-plugin/src/main/resources/static/rapture/NX/onboarding/app/PluginStrings.js b/plugins/nexus-onboarding-plugin/src/main/resources/static/rapture/NX/onboarding/app/PluginStrings.js index 591bc74b92..0ced730557 100644 --- a/plugins/nexus-onboarding-plugin/src/main/resources/static/rapture/NX/onboarding/app/PluginStrings.js +++ b/plugins/nexus-onboarding-plugin/src/main/resources/static/rapture/NX/onboarding/app/PluginStrings.js @@ -25,7 +25,8 @@ Ext.define('NX.onboarding.app.PluginStrings', { keys: { Onboarding_Text: 'Onboarding', - Onboarding_Description: 'Configuration changes requiring attention' + Onboarding_Description: 'Configuration changes requiring attention', + Onboarding_Authenticate: 'Your admin user password is located in
{0} on the server.' }, bundles: { diff --git a/plugins/nexus-onboarding-plugin/src/main/resources/static/rapture/NX/onboarding/controller/Onboarding.js b/plugins/nexus-onboarding-plugin/src/main/resources/static/rapture/NX/onboarding/controller/Onboarding.js index 0dc3a790f9..414cf9d105 100644 --- a/plugins/nexus-onboarding-plugin/src/main/resources/static/rapture/NX/onboarding/controller/Onboarding.js +++ b/plugins/nexus-onboarding-plugin/src/main/resources/static/rapture/NX/onboarding/controller/Onboarding.js @@ -50,6 +50,9 @@ Ext.define('NX.onboarding.controller.Onboarding', { component: { 'nx-onboarding-wizard': { closed: me.reset + }, + 'nx-signin': { + beforeshow: me.beforeShowSignin } }, controller: { @@ -65,8 +68,21 @@ Ext.define('NX.onboarding.controller.Onboarding', { }); }, + beforeShowSignin: function(signin) { + var doOnboarding = NX.State.getValue('onboarding.required'), + passwordFile = NX.State.getValue("admin.password.file"), + msg = NX.I18n.format('Onboarding_Authenticate', Ext.htmlEncode(passwordFile)); + + if (doOnboarding && passwordFile) { + signin.addMessage(msg); + } + else { + signin.clearMessage(); + } + }, + stateChanged: function() { - if (NX.State.getValue('onboarding.required', false)) { + if (NX.State.getValue('onboarding.required') && NX.State.getUser()) { this.loadItems(); } }, diff --git a/plugins/nexus-onboarding-plugin/src/test/java/org/sonatype/nexus/onboarding/internal/OnboardingStateContributorTest.java b/plugins/nexus-onboarding-plugin/src/test/java/org/sonatype/nexus/onboarding/internal/OnboardingStateContributorTest.java index 72ecd0c5f4..bd177a2209 100644 --- a/plugins/nexus-onboarding-plugin/src/test/java/org/sonatype/nexus/onboarding/internal/OnboardingStateContributorTest.java +++ b/plugins/nexus-onboarding-plugin/src/test/java/org/sonatype/nexus/onboarding/internal/OnboardingStateContributorTest.java @@ -19,9 +19,8 @@ import org.sonatype.nexus.onboarding.OnboardingConfiguration; import org.sonatype.nexus.onboarding.OnboardingItem; import org.sonatype.nexus.onboarding.OnboardingManager; -import org.sonatype.nexus.security.SecurityHelper; +import org.sonatype.nexus.security.config.AdminPasswordFileManager; -import org.apache.shiro.subject.Subject; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -41,7 +40,7 @@ public class OnboardingStateContributorTest private OnboardingManager onboardingManager; @Mock - private SecurityHelper securityHelper; + private AdminPasswordFileManager adminPasswordFileManager; @Mock private OnboardingItem onboardingItem1; @@ -49,9 +48,6 @@ public class OnboardingStateContributorTest @Mock private OnboardingItem onboardingItem2; - @Mock - private Subject subject; - private OnboardingStateContributor underTest; @Before @@ -59,42 +55,26 @@ public void setup() { when(onboardingConfiguration.isEnabled()).thenReturn(true); when(onboardingManager.getOnboardingItems()).thenReturn(Arrays.asList(onboardingItem1, onboardingItem2)); when(onboardingManager.needsOnboarding()).thenReturn(true); - when(securityHelper.subject()).thenReturn(subject); - when(subject.isPermitted("nexus:*")).thenReturn(true); - when(subject.getPrincipal()).thenReturn(new Object()); + when(adminPasswordFileManager.exists()).thenReturn(true); + when(adminPasswordFileManager.getPath()).thenReturn("path/to/file"); - underTest = new OnboardingStateContributor(onboardingConfiguration, onboardingManager, securityHelper); + underTest = new OnboardingStateContributor(onboardingConfiguration, onboardingManager, adminPasswordFileManager); } @Test public void testGetState() { Map state = underTest.getState(); - assertThat(state.size(), is(1)); + assertThat(state.size(), is(2)); assertThat(state.get("onboarding.required"), is(true)); + assertThat(state.get("admin.password.file"), is("path/to/file")); } @Test public void testGetState_noItems() { when(onboardingManager.needsOnboarding()).thenReturn(false); + when(adminPasswordFileManager.exists()).thenReturn(false); - Map state = underTest.getState(); - assertThat(state, nullValue()); - } - - @Test - public void testGetState_noAuthz() { - when(subject.isPermitted("nexus:*")).thenReturn(false); - - Map state = underTest.getState(); - assertThat(state, nullValue()); - } - - @Test - public void testGetState_noPrincipal() { - when(subject.getPrincipal()).thenReturn(null); - - Map state = underTest.getState(); - assertThat(state, nullValue()); + assertThat(underTest.getState(), nullValue()); } @Test @@ -105,12 +85,20 @@ public void testGetState_cacheOnboardingState() { when(onboardingManager.needsOnboarding()).thenReturn(false); state = underTest.getState(); - assertThat(state, nullValue()); + assertThat(state.get("onboarding.required"), nullValue()); //set to true to validate that cache kicks in and still doesn't add data to the map when(onboardingManager.needsOnboarding()).thenReturn(true); state = underTest.getState(); - assertThat(state, nullValue()); + assertThat(state.get("onboarding.required"), nullValue()); + } + + @Test + public void testGetState_noAdminPasswordFile() { + when(adminPasswordFileManager.exists()).thenReturn(false); + + Map state = underTest.getState(); + assertThat(state.get("admin.password.file"), nullValue()); } } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/AptFacet.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/AptFacet.java index 018430686e..2e51ae5f31 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/AptFacet.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/AptFacet.java @@ -18,6 +18,8 @@ import org.sonatype.nexus.repository.Facet; import org.sonatype.nexus.repository.apt.internal.debian.PackageInfo; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.StorageTx; import org.sonatype.nexus.repository.view.Content; import org.sonatype.nexus.repository.view.Payload; @@ -29,13 +31,17 @@ public interface AptFacet extends Facet { @Nullable - Content get(String path) throws IOException; + Content get(final String path) throws IOException; - Content put(String path, Payload payload) throws IOException; + Content put(final String path, final Payload payload) throws IOException; - Content put(String path, Payload payload, @Nullable PackageInfo packageInfo) throws IOException; + Content put(final String path, final Payload payload, @Nullable final PackageInfo packageInfo) throws IOException; - boolean delete(String path) throws IOException; + boolean delete(final String path) throws IOException; + + Asset findOrCreateDebAsset(final StorageTx tx, final String path, final PackageInfo packageInfo); + + Asset findOrCreateMetadataAsset(final StorageTx tx, final String path); boolean isFlat(); diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/store/LicenseUser.js b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/AptRestoreFacet.java similarity index 53% rename from plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/store/LicenseUser.js rename to plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/AptRestoreFacet.java index 1a2802b90c..730fa59070 100644 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/store/LicenseUser.js +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/AptRestoreFacet.java @@ -10,31 +10,28 @@ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the * Eclipse Foundation. All other trademarks are the property of their respective owners. */ -/*global Ext, NX*/ +package org.sonatype.nexus.repository.apt; + +import java.io.IOException; + +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.repository.Facet; +import org.sonatype.nexus.repository.Type; +import org.sonatype.nexus.repository.storage.AssetBlob; +import org.sonatype.nexus.repository.storage.Query; +import org.sonatype.nexus.repository.view.Content; /** - * License User store. - * - * @since 3.0 + * @since 3.next */ -Ext.define('NX.coreui.store.LicenseUser', { - extend: 'Ext.data.Store', - model: 'NX.coreui.model.LicenseUser', - - proxy: { - type: 'direct', - paramsAsHash: false, +@Facet.Exposed +public interface AptRestoreFacet extends Facet +{ + Content restore(final AssetBlob assetBlob, final String path) throws IOException; - api: { - read: 'NX.direct.licensing_Licensing.readLicenseUsers' - }, + boolean assetExists(final String path); - reader: { - type: 'json', - rootProperty: 'data', - successProperty: 'success' - } - }, + Query getComponentQuery(final Blob blob) throws IOException; - sorters: { property: 'timestamp', direction: 'DESC' } -}); + boolean componentRequired(final String name); +} diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/AptUploadHandler.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/AptUploadHandler.java index c92aaca8b2..49a03643ee 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/AptUploadHandler.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/AptUploadHandler.java @@ -63,7 +63,7 @@ public AptUploadHandler(@Named("simple") final VariableResolverAdapter variableR } @Override - public UploadResponse handle(Repository repository, ComponentUpload upload) throws IOException { + public UploadResponse handle(final Repository repository, final ComponentUpload upload) throws IOException { AptHostedFacet hostedFacet = repository.facet(AptHostedFacet.class); StorageFacet storageFacet = repository.facet(StorageFacet.class); diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptBrowseNodeGenerator.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptBrowseNodeGenerator.java index 0dc9b9cbf0..85a6fd6763 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptBrowseNodeGenerator.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptBrowseNodeGenerator.java @@ -37,7 +37,7 @@ public class AptBrowseNodeGenerator { @Override - public List computeAssetPath(Asset asset, Component component) { + public List computeAssetPath(final Asset asset, final Component component) { List path; if (component != null) { path = computeComponentPath(asset, component); @@ -57,7 +57,7 @@ public List computeAssetPath(Asset asset, Component component) { } @Override - public List computeComponentPath(Asset asset, Component component) { + public List computeComponentPath(final Asset asset, final Component component) { List path = new ArrayList<>(); path.add("packages"); path.add(component.name().substring(0, 1).toLowerCase()); diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptComponentDirector.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptComponentDirector.java new file mode 100644 index 0000000000..cc8912c001 --- /dev/null +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptComponentDirector.java @@ -0,0 +1,98 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.apt.internal; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.apt.internal.hosted.AptHostedFacet; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.BucketStore; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.ComponentDirector; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.transaction.UnitOfWork; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * @since 3.next + */ +@Named(AptFormat.NAME) +@Singleton +public class AptComponentDirector + extends ComponentSupport + implements ComponentDirector +{ + private final BucketStore bucketStore; + + private final RepositoryManager repositoryManager; + + @Inject + public AptComponentDirector(final BucketStore bucketStore, + final RepositoryManager repositoryManager) + { + this.bucketStore = checkNotNull(bucketStore); + this.repositoryManager = checkNotNull(repositoryManager); + } + + @Override + public boolean allowMoveTo(final Repository destination) { + return true; + } + + @Override + public boolean allowMoveTo(final Component component, final Repository destination) { + return repositoryFor(component).isPresent(); + } + + @Override + public boolean allowMoveFrom(final Repository source) { + return true; + } + + @Override + public void afterMove(final List> components, final Repository destination) { + destination.facet(StorageFacet.class).txSupplier(); + UnitOfWork.begin(destination.facet(StorageFacet.class).txSupplier()); + try { + AptHostedFacet hostedFacet = destination.facet(AptHostedFacet.class); + hostedFacet.rebuildIndexes(); + } + catch (IOException e) { + log.error("Failed to update metadata, repository: {}", destination.getName(), e); + throw new UncheckedIOException(e); + } + finally { + UnitOfWork.end(); + } + } + + private Optional repositoryFor(final Component component) { + return Optional.of(component) + .map(Component::bucketId) + .map(bucketStore::getById) + .map(Bucket::getRepositoryName) + .map(repositoryManager::get); + } +} diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptFacetImpl.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptFacetImpl.java index d7e87b73a4..d3f8aa2bb4 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptFacetImpl.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptFacetImpl.java @@ -13,7 +13,6 @@ package org.sonatype.nexus.repository.apt.internal; import java.io.IOException; -import java.util.Optional; import javax.annotation.Nullable; import javax.inject.Named; @@ -114,7 +113,7 @@ public boolean isFlat() { @Override @Nullable @TransactionalTouchBlob - public Content get(String path) throws IOException { + public Content get(final String path) throws IOException { final StorageTx tx = UnitOfWork.currentTx(); final Asset asset = tx.findAssetWithProperty(P_NAME, path, tx.findBucket(getRepository())); if (asset == null) { @@ -126,17 +125,20 @@ public Content get(String path) throws IOException { @Override @TransactionalStoreBlob - public Content put(String path, Payload content) throws IOException { + public Content put(final String path, final Payload content) throws IOException { return put(path, content, null); } @Override @TransactionalStoreBlob - public Content put(String path, Payload content, PackageInfo info) throws IOException { + public Content put(final String path, final Payload content, final PackageInfo info) throws IOException { StorageFacet storageFacet = facet(StorageFacet.class); try (final TempBlob tempBlob = storageFacet.createTempBlob(content, FacetHelper.hashAlgorithms)) { StorageTx tx = UnitOfWork.currentTx(); - Asset asset = findOrCreateAsset(tx, path, Optional.ofNullable(info), tempBlob); + Asset asset = isDebPackageContentType(path) + ? findOrCreateDebAsset(tx, path, + info != null ? info : new PackageInfo(AptPackageParser.getDebControlFile(tempBlob.getBlob()))) + : findOrCreateMetadataAsset(tx, path); AttributesMap contentAttributes = null; if (content instanceof Content) { @@ -156,29 +158,32 @@ public Content put(String path, Payload content, PackageInfo info) throws IOExce } } - private Asset findOrCreateAsset(StorageTx tx, String path, Optional info, TempBlob tempBlob) - throws IOException + @Override + public Asset findOrCreateDebAsset(final StorageTx tx, final String path, final PackageInfo packageInfo) { Bucket bucket = tx.findBucket(getRepository()); Asset asset = tx.findAssetWithProperty(P_NAME, path, bucket); if (asset == null) { - if (isDebPackageContentType(path)) { - Component component = findOrCreateComponent( - tx, - bucket, - info.orElse(new PackageInfo(AptPackageParser.parsePackage(tempBlob)))); - asset = tx.createAsset(bucket, component); - } - else { - asset = tx.createAsset(bucket, getRepository().getFormat()); - } - asset = asset.name(path); + Component component = findOrCreateComponent( + tx, + bucket, + packageInfo); + asset = tx.createAsset(bucket, component).name(path); } - return asset; + return asset; + } + + @Override + public Asset findOrCreateMetadataAsset(final StorageTx tx, final String path) { + Bucket bucket = tx.findBucket(getRepository()); + Asset asset = tx.findAssetWithProperty(P_NAME, path, bucket); + return asset != null + ? asset + : tx.createAsset(bucket, getRepository().getFormat()).name(path); } - private Component findOrCreateComponent(StorageTx tx, Bucket bucket, PackageInfo info) { + private Component findOrCreateComponent(final StorageTx tx, final Bucket bucket, final PackageInfo info) { String name = info.getPackageName(); String version = info.getVersion(); String architecture = info.getArchitecture(); @@ -206,7 +211,7 @@ private Component findOrCreateComponent(StorageTx tx, Bucket bucket, PackageInfo @Override @TransactionalDeleteBlob - public boolean delete(String path) throws IOException { + public boolean delete(final String path) throws IOException { StorageTx tx = UnitOfWork.currentTx(); Bucket bucket = tx.findBucket(getRepository()); Asset asset = tx.findAssetWithProperty(P_NAME, path, bucket); diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptPackageParser.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptPackageParser.java index 74d52a4498..69b8ec87c6 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptPackageParser.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptPackageParser.java @@ -16,6 +16,7 @@ import java.io.InputStream; import java.util.function.Supplier; +import org.sonatype.nexus.blobstore.api.Blob; import org.sonatype.nexus.repository.apt.internal.debian.ControlFile; import org.sonatype.nexus.repository.apt.internal.debian.ControlFileParser; @@ -69,4 +70,14 @@ public static ControlFile parsePackage(final Supplier supplier) thr return control; } } + + public static ControlFile getDebControlFile(final Blob blob) + throws IOException + { + final ControlFile controlFile = AptPackageParser.parsePackage(() -> blob.getInputStream()); + if (controlFile == null) { + throw new IOException("Invalid debian package: no control file"); + } + return controlFile; + } } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptRestoreFacetImpl.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptRestoreFacetImpl.java new file mode 100644 index 0000000000..ae237d97b7 --- /dev/null +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptRestoreFacetImpl.java @@ -0,0 +1,92 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.apt.internal; + +import java.io.IOException; +import java.io.InputStream; + +import javax.inject.Named; + +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.common.collect.AttributesMap; +import org.sonatype.nexus.repository.FacetSupport; +import org.sonatype.nexus.repository.apt.AptFacet; +import org.sonatype.nexus.repository.apt.AptRestoreFacet; +import org.sonatype.nexus.repository.apt.internal.debian.ControlFile; +import org.sonatype.nexus.repository.apt.internal.debian.PackageInfo; +import org.sonatype.nexus.repository.apt.internal.debian.Utils; +import org.sonatype.nexus.repository.storage.Asset; +import org.sonatype.nexus.repository.storage.AssetBlob; +import org.sonatype.nexus.repository.storage.Query; +import org.sonatype.nexus.repository.storage.StorageTx; +import org.sonatype.nexus.repository.transaction.TransactionalStoreBlob; +import org.sonatype.nexus.repository.transaction.TransactionalTouchBlob; +import org.sonatype.nexus.repository.view.Content; +import org.sonatype.nexus.transaction.UnitOfWork; + +import static org.sonatype.nexus.repository.apt.internal.debian.Utils.isDebPackageContentType; +import static org.sonatype.nexus.repository.storage.ComponentEntityAdapter.P_GROUP; +import static org.sonatype.nexus.repository.storage.ComponentEntityAdapter.P_VERSION; +import static org.sonatype.nexus.repository.storage.MetadataNodeEntityAdapter.P_NAME; + +/** + * @since 3.next + */ +@Named +public class AptRestoreFacetImpl + extends FacetSupport + implements AptRestoreFacet +{ + @Override + @TransactionalStoreBlob + public Content restore(final AssetBlob assetBlob, final String path) throws IOException { + StorageTx tx = UnitOfWork.currentTx(); + Asset asset; + AptFacet aptFacet = facet(AptFacet.class); + if (isDebPackageContentType(path)) { + ControlFile controlFile = AptPackageParser.getDebControlFile(assetBlob.getBlob()); + asset = aptFacet.findOrCreateDebAsset(tx, path, new PackageInfo(controlFile)); + } + else { + asset = aptFacet.findOrCreateMetadataAsset(tx, path); + } + + tx.attachBlob(asset, assetBlob); + Content.applyToAsset(asset, Content.maintainLastModified(asset, new AttributesMap())); + tx.saveAsset(asset); + return FacetHelper.toContent(asset, assetBlob.getBlob()); + } + + @Override + @TransactionalTouchBlob + public boolean assetExists(final String path) { + final StorageTx tx = UnitOfWork.currentTx(); + return tx.findAssetWithProperty(P_NAME, path, tx.findBucket(getRepository())) != null; + } + + @Override + public Query getComponentQuery(final Blob blob) throws IOException { + final InputStream inputStream = blob.getInputStream(); + final PackageInfo packageInfo = new PackageInfo(AptPackageParser.parsePackage(() -> inputStream)); + return Query.builder() + .where(P_NAME).eq(packageInfo.getPackageName()) + .and(P_VERSION).eq(packageInfo.getVersion()) + .and(P_GROUP).eq(packageInfo.getArchitecture()) + .build(); + } + + @Override + public boolean componentRequired(final String name) { + return Utils.isDebPackageContentType(name); + } +} diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptSecurityFacet.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptSecurityFacet.java index 55b35e10e3..64ea8299e6 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptSecurityFacet.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptSecurityFacet.java @@ -28,7 +28,7 @@ public class AptSecurityFacet { @Inject public AptSecurityFacet( - AptFormatSecurityContributor securityResource, + final AptFormatSecurityContributor securityResource, @Named("simple") final VariableResolverAdapter variableResolverAdapter, final ContentPermissionChecker contentPermissionChecker) { super(securityResource, variableResolverAdapter, contentPermissionChecker); diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptWritePolicySelector.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptWritePolicySelector.java index 4f0d4f418b..a8dec7bf36 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptWritePolicySelector.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/AptWritePolicySelector.java @@ -24,7 +24,7 @@ public class AptWritePolicySelector { @Override - public WritePolicy select(Asset asset, WritePolicy configured) { + public WritePolicy select(final Asset asset, final WritePolicy configured) { if (WritePolicy.ALLOW_ONCE == configured) { String name = asset.name(); if (name.endsWith(".deb")) { diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/FacetHelper.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/FacetHelper.java index ad968ef21c..a1b9e9b01b 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/FacetHelper.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/FacetHelper.java @@ -46,7 +46,7 @@ public class FacetHelper private static final String ASSET_PATH = "pool/%s/%s/%s"; - public static List getReleaseIndexSpecifiers(AptFacet facet) { + public static List getReleaseIndexSpecifiers(final AptFacet facet) { if (facet.isFlat()) { return Arrays.asList( new ContentSpecifier(RELEASE, SnapshotItem.Role.RELEASE_INDEX), @@ -63,7 +63,7 @@ public static List getReleaseIndexSpecifiers(AptFacet facet) { } } - public static List getReleasePackageIndexes(AptFacet facet, String component, String arch) { + public static List getReleasePackageIndexes(final AptFacet facet, final String component, final String arch) { if (facet.isFlat()) { return Arrays.asList( new ContentSpecifier(PACKAGES, SnapshotItem.Role.PACKAGE_INDEX_RAW), @@ -92,7 +92,7 @@ public static Content toContent(final Asset asset, final Blob blob) { return content; } - public static String buildAssetName(String packageName, String version, String architecture) { + public static String buildAssetName(final String packageName, final String version, final String architecture) { return packageName + "_" + version + "_" + architecture + ".deb"; } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/ControlFile.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/ControlFile.java index d9abc0b852..fb7e6a1bc3 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/ControlFile.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/ControlFile.java @@ -32,29 +32,29 @@ public static class Builder { private List paragraphs; - private Builder(List paragraphs) { + private Builder(final List paragraphs) { super(); this.paragraphs = paragraphs; } - public Builder removeParagraphs(Predicate p) { + public Builder removeParagraphs(final Predicate p) { paragraphs = paragraphs.stream().filter(p).collect(Collectors.toList()); return this; } - public Builder addParagraph(Paragraph p) { + public Builder addParagraph(final Paragraph p) { paragraphs.add(p); return this; } - public Builder replaceParagraph(Predicate predicate, Paragraph p) { + public Builder replaceParagraph(final Predicate predicate, final Paragraph p) { paragraphs = Stream .concat(paragraphs.stream().filter(predicate), Stream.of(p)) .collect(Collectors.toList()); return this; } - public Builder transformParagraphs(Predicate predicate, Function transform) { + public Builder transformParagraphs(final Predicate predicate, final Function transform) { paragraphs = paragraphs.stream() .filter(predicate) .map(transform) @@ -73,7 +73,7 @@ public static Builder newBuilder() { private final List paragraphs; - public ControlFile(List paragraphs) { + public ControlFile(final List paragraphs) { super(); this.paragraphs = new ArrayList<>(paragraphs); } @@ -86,7 +86,7 @@ public List getParagraphs() { return paragraphs; } - public Optional getField(String name) { + public Optional getField(final String name) { if (paragraphs.isEmpty()) { return Optional.empty(); } @@ -106,12 +106,12 @@ public static class Paragraph { private final List fields; - public Paragraph(List fields) { + public Paragraph(final List fields) { super(); this.fields = new ArrayList<>(fields); } - public Optional getField(String name) { + public Optional getField(final String name) { for (ControlField f : fields) { if (f.key.equals(name)) { return Optional.of(f); @@ -125,7 +125,7 @@ public List getFields() { return Collections.unmodifiableList(fields); } - public Paragraph withFields(List updateFields) { + public Paragraph withFields(final List updateFields) { Map index = updateFields.stream().collect(Collectors.toMap(f -> f.key, f -> f)); return new Paragraph(Stream .concat(fields.stream().filter(f -> !index.containsKey(f.key)), updateFields.stream()) @@ -146,7 +146,7 @@ public static class ControlField public final String value; - public ControlField(String key, String value) { + public ControlField(final String key, final String value) { super(); this.key = key; this.value = value; diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/ControlFileParser.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/ControlFileParser.java index 6d17cd8b73..5f4a3d4d18 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/ControlFileParser.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/ControlFileParser.java @@ -45,7 +45,7 @@ public class ControlFileParser private String fieldName; - public ControlFile parseControlFile(InputStream stream) throws IOException { + public ControlFile parseControlFile(final InputStream stream) throws IOException { paragraphs.clear(); fields.clear(); valueBuilder.setLength(0); @@ -97,7 +97,7 @@ private void finishField() { inField = false; } - private void beginField(String line) throws IOException { + private void beginField(final String line) throws IOException { Matcher m = FIELD_PATTERN.matcher(line); if (!m.matches()) { throw new IOException("Invalid line: " + line); diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/DebianVersion.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/DebianVersion.java index a387917f21..ab178ee770 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/DebianVersion.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/DebianVersion.java @@ -73,7 +73,7 @@ public String toString() { } @Override - public int compareTo(DebianVersion o) { + public int compareTo(final DebianVersion o) { if (this.epoch < o.epoch) { return -1; } @@ -109,7 +109,7 @@ public int hashCode() { return Objects.hash(epoch, debianRevision, upstreamVersion); } - private int parseEpoch(final String version, int colonIndex) { + private int parseEpoch(final String version, final int colonIndex) { return colonIndex > 0 ? Integer.parseInt(version.substring(0, colonIndex)) : 0; } @@ -119,11 +119,11 @@ private String parseUpstreamVersion(final String version, final int colonIndex, return version.substring(beginIndex, endIndex); } - private String parseDebianRevision(String version, int hyphenIndex) { + private String parseDebianRevision(final String version, final int hyphenIndex) { return hyphenIndex > 0 ? version.substring(hyphenIndex + 1) : ""; } - private static int compareDebianVersion(String a, String b) { + private static int compareDebianVersion(final String a, final String b) { Matcher ma = VERSION_PART.matcher(a); Matcher mb = VERSION_PART.matcher(b); @@ -164,7 +164,7 @@ private static int compareDebianVersion(String a, String b) { return 0; } - private static int compareNonNumeric(String a, String b) { + private static int compareNonNumeric(final String a, final String b) { int len = Math.max(a.length(), b.length()); for (int i = 0; i < len; i++) { int ac; @@ -199,7 +199,7 @@ else if (ac > bc) { return 0; } - private static int compareNumeric(String a, String b) { + private static int compareNumeric(final String a, final String b) { if (a.isEmpty() && !b.isEmpty()) { return -1; } @@ -213,7 +213,7 @@ else if (a.isEmpty() && b.isEmpty()) { return Long.compare(Long.parseLong(a), Long.parseLong(b)); } - private static int priorityClass(int c) { + private static int priorityClass(final int c) { return c == '~' ? -2 : c == -1 ? -1 : Character.isLetter(c) ? 0 : 1; } } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/PackageInfo.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/PackageInfo.java index 968a3426c9..1bfecd93c3 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/PackageInfo.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/PackageInfo.java @@ -25,7 +25,7 @@ public class PackageInfo private final ControlFile controlFile; - public PackageInfo(ControlFile controlFile) { + public PackageInfo(final ControlFile controlFile) { this.controlFile = controlFile; } @@ -41,7 +41,7 @@ public String getArchitecture() { return getField(ARCHITECTURE_FIELD); } - private String getField(String fieldName) { + private String getField(final String fieldName) { return controlFile.getField(fieldName).map(f -> f.value).get(); } } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/Release.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/Release.java index 6769939451..c2d6c70889 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/Release.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/Release.java @@ -23,7 +23,7 @@ public class Release { private final ControlFile index; - public Release(ControlFile index) { + public Release(final ControlFile index) { super(); this.index = index; } @@ -60,7 +60,7 @@ public Optional getDescription() { return getValue("Description"); } - private Optional getValue(String name) { + private Optional getValue(final String name) { return index.getField(name).map(e -> e.value); } } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/Utils.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/Utils.java index a72cc2eccd..fca7e65778 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/Utils.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/debian/Utils.java @@ -20,7 +20,7 @@ public class Utils private Utils(){ } - public static boolean isDebPackageContentType(String path) { + public static boolean isDebPackageContentType(final String path) { return path.endsWith(".deb") || path.endsWith(".udeb"); } } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/gpg/AptSigningFacet.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/gpg/AptSigningFacet.java index b1b1371e4e..4544e9194c 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/gpg/AptSigningFacet.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/gpg/AptSigningFacet.java @@ -102,7 +102,7 @@ public Content getPublicKey() throws IOException { return new Content(new BytesPayload(buffer.toByteArray(), AptMimeTypes.PUBLICKEY)); } - public byte[] signInline(String input) throws IOException { + public byte[] signInline(final String input) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); try { PGPSecretKey signKey = readSecretKey(); @@ -143,7 +143,7 @@ public byte[] signInline(String input) throws IOException { return buffer.toByteArray(); } - public byte[] signExternal(String input) throws IOException { + public byte[] signExternal(final String input) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); try { PGPSecretKey signKey = readSecretKey(); diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/gpg/AptSigningHandler.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/gpg/AptSigningHandler.java index ad1e2a533c..1d72718ebc 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/gpg/AptSigningHandler.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/gpg/AptSigningHandler.java @@ -35,7 +35,7 @@ public class AptSigningHandler implements Handler { @Override - public Response handle(Context context) throws Exception { + public Response handle(final Context context) throws Exception { String path = assetPath(context); String method = context.getRequest().getAction(); AptSigningFacet facet = context.getRepository().facet(AptSigningFacet.class); @@ -47,7 +47,7 @@ public Response handle(Context context) throws Exception { return context.proceed(); } - private String assetPath(Context context) { + private String assetPath(final Context context) { return context.getAttributes().require(AptSnapshotHandler.State.class).assetPath; } } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedComponentMaintenanceFacet.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedComponentMaintenanceFacet.java index a71b80ff66..d34374de89 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedComponentMaintenanceFacet.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedComponentMaintenanceFacet.java @@ -46,7 +46,7 @@ public class AptHostedComponentMaintenanceFacet { @Transactional(retryOn = ONeedRetryException.class) @Override - protected Set deleteAssetTx(EntityId assetId, boolean deleteBlobs) { + protected Set deleteAssetTx(final EntityId assetId, final boolean deleteBlobs) { StorageTx tx = UnitOfWork.currentTx(); Bucket bucket = tx.findBucket(getRepository()); Asset asset = tx.findAsset(assetId, bucket); diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedFacet.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedFacet.java index 152443f1b7..704fb62ed1 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedFacet.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedFacet.java @@ -14,9 +14,9 @@ import java.io.IOException; import java.io.Writer; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -58,6 +58,7 @@ import org.apache.commons.io.IOUtils; import org.apache.http.client.utils.DateUtils; +import static java.util.Collections.singletonList; import static org.sonatype.nexus.common.hash.HashAlgorithm.MD5; import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA1; import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA256; @@ -90,7 +91,7 @@ public class AptHostedFacet "WHERE bucket=:bucket " + "AND attributes.apt.asset_kind=:asset_kind"; - public Asset ingestAsset(Payload body) throws IOException { + public Asset ingestAsset(final Payload body) throws IOException { StorageFacet storageFacet = facet(StorageFacet.class); try (TempBlob tempBlob = storageFacet.createTempBlob(body, FacetHelper.hashAlgorithms)) { ControlFile control = AptPackageParser.parsePackage(tempBlob); @@ -102,7 +103,7 @@ public Asset ingestAsset(Payload body) throws IOException { } @TransactionalStoreBlob - protected Asset ingestAsset(ControlFile control, TempBlob body, long size, String contentType) throws IOException { + protected Asset ingestAsset(final ControlFile control, final TempBlob body, final long size, final String contentType) throws IOException { AptFacet aptFacet = getRepository().facet(AptFacet.class); StorageTx tx = UnitOfWork.currentTx(); Bucket bucket = tx.findBucket(getRepository()); @@ -129,20 +130,17 @@ protected Asset ingestAsset(ControlFile control, TempBlob body, long size, Strin asset.formatAttributes().set(P_ASSET_KIND, "DEB"); tx.saveAsset(asset); - List changes = new ArrayList<>(); - changes.add(new AssetChange(AssetAction.ADDED, asset)); - - rebuildIndexesInTransaction(tx, changes); + rebuildIndexes(singletonList(new AssetChange(AssetAction.ADDED, asset))); return asset; } - void rebuildIndexes(List changes) throws IOException { - StorageTx tx = UnitOfWork.currentTx(); - rebuildIndexesInTransaction(tx, changes); + public void rebuildIndexes() throws IOException { + rebuildIndexes(Collections.emptyList()); } @TransactionalStoreMetadata - private void rebuildIndexesInTransaction(StorageTx tx, List changes) throws IOException { + public void rebuildIndexes(final List changes) throws IOException { + StorageTx tx = UnitOfWork.currentTx(); AptFacet aptFacet = getRepository().facet(AptFacet.class); AptSigningFacet signingFacet = getRepository().facet(AptSigningFacet.class); Bucket bucket = tx.findBucket(getRepository()); @@ -182,7 +180,7 @@ private void rebuildIndexesInTransaction(StorageTx tx, List changes aptFacet.put(releaseIndexName(RELEASE_GPG), new BytesPayload(releaseGpg, AptMimeTypes.SIGNATURE)); } - private String buildReleaseFile(String distribution, Collection architectures, String md5, String sha256) { + private String buildReleaseFile(final String distribution, final Collection architectures, final String md5, final String sha256) { Paragraph p = new Paragraph(Arrays.asList( new ControlFile.ControlField("Suite", distribution), new ControlFile.ControlField("Codename", distribution), new ControlFile.ControlField("Components", "main"), @@ -192,7 +190,7 @@ private String buildReleaseFile(String distribution, Collection architec return p.toString(); } - private CompressingTempFileStore buildPackageIndexes(StorageTx tx, Bucket bucket, List changes) + private CompressingTempFileStore buildPackageIndexes(final StorageTx tx, final Bucket bucket, final List changes) throws IOException { CompressingTempFileStore result = new CompressingTempFileStore(); @@ -244,7 +242,7 @@ private CompressingTempFileStore buildPackageIndexes(StorageTx tx, Bucket bucket return result; } - private String buildIndexSection(ControlFile cf, long size, Map hashes, String assetPath) { + private String buildIndexSection(final ControlFile cf, final long size, final Map hashes, final String assetPath) { Paragraph modified = cf.getParagraphs().get(0) .withFields(Arrays.asList( new ControlFile.ControlField("Filename", assetPath), @@ -255,23 +253,23 @@ private String buildIndexSection(ControlFile cf, long size, Map hashMap = content.getAttributes().get(Content.CONTENT_HASH_CODES_MAP, Content.T_CONTENT_HASH_CODES_MAP); builder.append("\n "); @@ -293,7 +291,7 @@ public static class AssetChange public final Asset asset; - AssetChange(AssetAction action, Asset asset) { + public AssetChange(final AssetAction action, final Asset asset) { super(); this.action = action; this.asset = asset; diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedHandler.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedHandler.java index 9ee2753917..0c1bd62257 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedHandler.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedHandler.java @@ -13,7 +13,6 @@ package org.sonatype.nexus.repository.apt.internal.hosted; import java.io.IOException; -import java.util.Collections; import javax.inject.Named; import javax.inject.Singleton; @@ -41,7 +40,7 @@ public class AptHostedHandler implements Handler { @Override - public Response handle(Context context) throws Exception { + public Response handle(final Context context) throws Exception { String path = assetPath(context); String method = context.getRequest().getAction(); @@ -65,7 +64,7 @@ private Response doPost(final Context context, final AptHostedFacet hostedFacet) throws IOException { if ("rebuild-indexes".equals(path)) { - hostedFacet.rebuildIndexes(Collections.emptyList()); + hostedFacet.rebuildIndexes(); return HttpResponses.ok(); } else if ("".equals(path)) { @@ -77,7 +76,7 @@ else if ("".equals(path)) { } } - private Response doGet(String path, AptFacet aptFacet) throws IOException { + private Response doGet(final String path, final AptFacet aptFacet) throws IOException { Content content = aptFacet.get(path); if (content == null) { return HttpResponses.notFound(path); @@ -85,7 +84,7 @@ private Response doGet(String path, AptFacet aptFacet) throws IOException { return HttpResponses.ok(content); } - private String assetPath(Context context) { + private String assetPath(final Context context) { return context.getAttributes().require(AptSnapshotHandler.State.class).assetPath; } } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedRecipe.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedRecipe.java index 8de6d1c90f..9dc1bb5129 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedRecipe.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedRecipe.java @@ -20,6 +20,7 @@ import org.sonatype.nexus.repository.Format; import org.sonatype.nexus.repository.Repository; import org.sonatype.nexus.repository.Type; +import org.sonatype.nexus.repository.apt.AptRestoreFacet; import org.sonatype.nexus.repository.apt.internal.AptFacetImpl; import org.sonatype.nexus.repository.apt.internal.AptFormat; import org.sonatype.nexus.repository.apt.internal.AptRecipeSupport; @@ -69,6 +70,9 @@ public class AptHostedRecipe @Inject Provider aptFacet; + @Inject + Provider aptRestoreFacet; + @Inject Provider aptHostedFacet; @@ -124,16 +128,17 @@ public class AptHostedRecipe LastDownloadedHandler lastDownloadedHandler; @Inject - public AptHostedRecipe(@Named(HostedType.NAME) Type type, @Named(AptFormat.NAME) Format format) { + public AptHostedRecipe(@Named(HostedType.NAME) final Type type, @Named(AptFormat.NAME) final Format format) { super(type, format); } @Override - public void apply(Repository repository) throws Exception { + public void apply(final Repository repository) throws Exception { repository.attach(securityFacet.get()); repository.attach(configure(viewFacet.get())); repository.attach(storageFacet.get()); repository.attach(aptFacet.get()); + repository.attach(aptRestoreFacet.get()); repository.attach(aptHostedFacet.get()); repository.attach(aptSigningFacet.get()); repository.attach(snapshotFacet.get()); diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedSnapshotFacet.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedSnapshotFacet.java index dbdad0cfb8..d934abcc20 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedSnapshotFacet.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/AptHostedSnapshotFacet.java @@ -32,7 +32,7 @@ public class AptHostedSnapshotFacet extends AptSnapshotFacetSupport { @Override - protected List fetchSnapshotItems(List specs) throws IOException { + protected List fetchSnapshotItems(final List specs) throws IOException { List list = new ArrayList<>(); for (ContentSpecifier spec : specs) { SnapshotItem item = getItem(spec); @@ -43,7 +43,7 @@ protected List fetchSnapshotItems(List specs) th return list; } - private SnapshotItem getItem(ContentSpecifier spec) throws IOException { + private SnapshotItem getItem(final ContentSpecifier spec) throws IOException { AptFacet apt = getRepository().facet(AptFacet.class); Content content = apt.get(spec.path); if (content == null) { diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/CompressingTempFileStore.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/CompressingTempFileStore.java index 3c7d4eaa48..80c9e3bb5a 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/CompressingTempFileStore.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/hosted/CompressingTempFileStore.java @@ -44,7 +44,7 @@ class CompressingTempFileStore { private final Map holdersByKey = new HashMap<>(); - public Writer openOutput(String key) { + public Writer openOutput(final String key) { try { if (holdersByKey.containsKey(key)) { throw new IllegalStateException("Output already opened"); @@ -91,7 +91,7 @@ public static class FileMetadata { private final FileHolder holder; - private FileMetadata(FileHolder holder) { + private FileMetadata(final FileHolder holder) { this.holder = holder; } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/proxy/AptProxyFacet.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/proxy/AptProxyFacet.java index eef80c0099..f6c8146620 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/proxy/AptProxyFacet.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/proxy/AptProxyFacet.java @@ -68,17 +68,17 @@ public class AptProxyFacet extends ProxyFacetSupport { - public List getSnapshotItems(List specs) throws IOException { + public List getSnapshotItems(final List specs) throws IOException { return fetchLatest(specs); } @Override - protected Content getCachedContent(Context context) throws IOException { + protected Content getCachedContent(final Context context) throws IOException { return getAptFacet().get(assetPath(context)); } @Override - protected Content store(Context context, Content content) throws IOException { + protected Content store(final Context context, final Content content) throws IOException { if (assetPath(context).endsWith(RELEASE)) { // Whenever we fetch a new release file, make sure we get the signature // and package files that go along with it. @@ -89,28 +89,28 @@ protected Content store(Context context, Content content) throws IOException { @Transactional(retryOn = {ONeedRetryException.class}) @Override - protected void indicateVerified(Context context, Content content, CacheInfo cacheInfo) throws IOException { + protected void indicateVerified(final Context context, final Content content, final CacheInfo cacheInfo) throws IOException { doIndicateVerified(content, cacheInfo, assetPath(context)); } @Override - protected String getUrl(Context context) { + protected String getUrl(final Context context) { return assetPath(context); } @Override - protected CacheController getCacheController(Context context) { + protected CacheController getCacheController(final Context context) { if (isDebPackageContentType(assetPath(context))) { return cacheControllerHolder.getContentCacheController(); } return cacheControllerHolder.getMetadataCacheController(); } - private String assetPath(Context context) { + private String assetPath(final Context context) { return context.getAttributes().require(AptSnapshotHandler.State.class).assetPath; } - private List fetchLatest(List specs) throws IOException { + private List fetchLatest(final List specs) throws IOException { List list = new ArrayList<>(); for (ContentSpecifier spec : specs) { Optional item = fetchLatest(spec); @@ -121,7 +121,7 @@ private List fetchLatest(List specs) throws IOEx return list; } - private Optional fetchLatest(ContentSpecifier spec) throws IOException { + private Optional fetchLatest(final ContentSpecifier spec) throws IOException { AptFacet aptFacet = getRepository().facet(AptFacet.class); ProxyFacet proxyFacet = facet(ProxyFacet.class); HttpClientFacet httpClientFacet = facet(HttpClientFacet.class); @@ -162,7 +162,7 @@ private Optional fetchLatest(ContentSpecifier spec) throws IOExcep return Optional.empty(); } - private HttpGet buildFetchRequest(Content oldVersion, URI fetchUri) { + private HttpGet buildFetchRequest(final Content oldVersion, final URI fetchUri) { HttpGet getRequest = new HttpGet(fetchUri); if (oldVersion != null) { DateTime lastModified = oldVersion.getAttributes().get(Content.CONTENT_LAST_MODIFIED, DateTime.class); @@ -177,7 +177,7 @@ private HttpGet buildFetchRequest(Content oldVersion, URI fetchUri) { return getRequest; } - private DateTime getDateHeader(HttpResponse response, String name) { + private DateTime getDateHeader(final HttpResponse response, final String name) { Header h = response.getLastHeader(name); if (h != null) { try { @@ -190,7 +190,7 @@ private DateTime getDateHeader(HttpResponse response, String name) { return null; } - private String getQuotedStringHeader(HttpResponse response, String name) { + private String getQuotedStringHeader(final HttpResponse response, final String name) { Header h = response.getLastHeader(name); if (h != null) { String value = h.getValue(); @@ -207,7 +207,7 @@ private String getQuotedStringHeader(HttpResponse response, String name) { } @Transactional(retryOn = {ONeedRetryException.class}) - protected void doIndicateVerified(Content content, CacheInfo cacheInfo, String assetPath) { + protected void doIndicateVerified(final Content content, final CacheInfo cacheInfo, final String assetPath) { StorageTx tx = UnitOfWork.currentTx(); Bucket bucket = tx.findBucket(getRepository()); @@ -222,7 +222,7 @@ protected void doIndicateVerified(Content content, CacheInfo cacheInfo, String a tx.saveAsset(asset); } - private void throwProxyExceptionForStatus(HttpResponse httpResponse) { + private void throwProxyExceptionForStatus(final HttpResponse httpResponse) { final StatusLine status = httpResponse.getStatusLine(); if (HttpStatus.SC_UNAUTHORIZED == status.getStatusCode() || HttpStatus.SC_PAYMENT_REQUIRED == status.getStatusCode() diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/proxy/AptProxyRecipe.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/proxy/AptProxyRecipe.java index 387f26fa70..8c288159d4 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/proxy/AptProxyRecipe.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/proxy/AptProxyRecipe.java @@ -20,6 +20,7 @@ import org.sonatype.nexus.repository.Format; import org.sonatype.nexus.repository.Repository; import org.sonatype.nexus.repository.Type; +import org.sonatype.nexus.repository.apt.AptRestoreFacet; import org.sonatype.nexus.repository.apt.internal.AptFacetImpl; import org.sonatype.nexus.repository.apt.internal.AptFormat; import org.sonatype.nexus.repository.apt.internal.AptRecipeSupport; @@ -32,6 +33,7 @@ import org.sonatype.nexus.repository.httpclient.HttpClientFacet; import org.sonatype.nexus.repository.proxy.ProxyHandler; import org.sonatype.nexus.repository.purge.PurgeUnusedFacet; +import org.sonatype.nexus.repository.routing.RoutingRuleHandler; import org.sonatype.nexus.repository.search.SearchFacet; import org.sonatype.nexus.repository.security.SecurityHandler; import org.sonatype.nexus.repository.storage.SingleAssetComponentMaintenance; @@ -85,6 +87,9 @@ public class AptProxyRecipe @Inject Provider aptFacet; + @Inject + Provider aptRestoreFacet; + @Inject Provider storageFacet; @@ -134,12 +139,15 @@ public class AptProxyRecipe LastDownloadedHandler lastDownloadedHandler; @Inject - public AptProxyRecipe(@Named(ProxyType.NAME) Type type, @Named(AptFormat.NAME) Format format) { + RoutingRuleHandler routingRuleHandler; + + @Inject + public AptProxyRecipe(@Named(ProxyType.NAME) final Type type, @Named(AptFormat.NAME) final Format format) { super(type, format); } @Override - public void apply(Repository repository) throws Exception { + public void apply(final Repository repository) throws Exception { repository.attach(securityFacet.get()); repository.attach(configure(viewFacet.get())); repository.attach(httpClientFacet.get()); @@ -147,6 +155,7 @@ public void apply(Repository repository) throws Exception { repository.attach(proxyFacet.get()); repository.attach(proxySnapshotFacet.get()); repository.attach(aptFacet.get()); + repository.attach(aptRestoreFacet.get()); repository.attach(storageFacet.get()); repository.attach(attributesFacet.get()); repository.attach(componentMaintenance.get()); @@ -160,6 +169,7 @@ private ViewFacet configure(final ConfigurableViewFacet facet) { builder.route(new Route.Builder().matcher(new AlwaysMatcher()) .handler(timingHandler) .handler(securityHandler) + .handler(routingRuleHandler) .handler(exceptionHandler) .handler(negativeCacheHandler) .handler(conditionalRequestHandler) diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/proxy/AptProxySnapshotFacet.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/proxy/AptProxySnapshotFacet.java index eb96892f3b..7a4da6e30e 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/proxy/AptProxySnapshotFacet.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/proxy/AptProxySnapshotFacet.java @@ -31,7 +31,7 @@ public class AptProxySnapshotFacet implements AptSnapshotFacet { @Override - protected List fetchSnapshotItems(List specs) throws IOException { + protected List fetchSnapshotItems(final List specs) throws IOException { AptProxyFacet proxyFacet = getRepository().facet(AptProxyFacet.class); return proxyFacet.getSnapshotItems(specs); } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AllSnapshotComponentSelector.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AllSnapshotComponentSelector.java index e041916f63..fb1cd2b570 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AllSnapshotComponentSelector.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AllSnapshotComponentSelector.java @@ -24,12 +24,12 @@ public class AllSnapshotComponentSelector { @Override - public List getArchitectures(Release release) { + public List getArchitectures(final Release release) { return release.getArchitectures(); } @Override - public List getComponents(Release release) { + public List getComponents(final Release release) { return release.getComponents(); } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AptSnapshotFacet.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AptSnapshotFacet.java index ba3bc1775f..a9dfdd7795 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AptSnapshotFacet.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AptSnapshotFacet.java @@ -26,12 +26,12 @@ public interface AptSnapshotFacet extends Facet { - boolean isSnapshotableFile(String path); + boolean isSnapshotableFile(final String path); - void createSnapshot(String id, SnapshotComponentSelector spec) throws IOException; + void createSnapshot(final String id, final SnapshotComponentSelector spec) throws IOException; @Nullable - Content getSnapshotFile(String id, String path) throws IOException; + Content getSnapshotFile(final String id, final String path) throws IOException; - void deleteSnapshot(String id) throws IOException; + void deleteSnapshot(final String id) throws IOException; } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AptSnapshotFacetSupport.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AptSnapshotFacetSupport.java index faea160fd6..2ae4752841 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AptSnapshotFacetSupport.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AptSnapshotFacetSupport.java @@ -56,18 +56,18 @@ public abstract class AptSnapshotFacetSupport implements AptSnapshotFacet { @Override - public boolean isSnapshotableFile(String path) { + public boolean isSnapshotableFile(final String path) { return !path.endsWith(".deb") && !path.endsWith(".DEB"); } @Override - public void createSnapshot(String id, SnapshotComponentSelector selector) throws IOException { + public void createSnapshot(final String id, final SnapshotComponentSelector selector) throws IOException { Iterable snapshots = collectSnapshotItems(selector); createSnapshot(id, snapshots); } @Transactional(retryOn = {ONeedRetryException.class}) - protected void createSnapshot(String id, Iterable snapshots) throws IOException { + protected void createSnapshot(final String id, final Iterable snapshots) throws IOException { StorageTx tx = UnitOfWork.currentTx(); StorageFacet storageFacet = facet(StorageFacet.class); Bucket bucket = tx.findBucket(getRepository()); @@ -90,7 +90,7 @@ protected void createSnapshot(String id, Iterable snapshots) throw @Transactional(retryOn = {ONeedRetryException.class}) @Override @Nullable - public Content getSnapshotFile(String id, String path) throws IOException { + public Content getSnapshotFile(final String id, final String path) throws IOException { StorageTx tx = UnitOfWork.currentTx(); Bucket bucket = tx.findBucket(getRepository()); final Asset asset = tx.findAssetWithProperty(P_NAME, createAssetPath(id, path), bucket); @@ -104,7 +104,7 @@ public Content getSnapshotFile(String id, String path) throws IOException { @Transactional(retryOn = {ONeedRetryException.class}) @Override - public void deleteSnapshot(String id) throws IOException { + public void deleteSnapshot(final String id) throws IOException { String path = createAssetPath(id, ""); StorageTx tx = UnitOfWork.currentTx(); @@ -117,7 +117,7 @@ public void deleteSnapshot(String id) throws IOException { .forEachRemaining(tx::deleteAsset); } - protected Iterable collectSnapshotItems(SnapshotComponentSelector selector) throws IOException { + protected Iterable collectSnapshotItems(final SnapshotComponentSelector selector) throws IOException { AptFacet aptFacet = getRepository().facet(AptFacet.class); List result = new ArrayList<>(); @@ -195,10 +195,10 @@ public int read(byte[] b, int off, int len) throws IOException { return result; } - private String createAssetPath(String id, String path) { + private String createAssetPath(final String id, final String path) { return "snapshots/" + id + "/" + path; } - protected abstract List fetchSnapshotItems(List specs) + protected abstract List fetchSnapshotItems(final List specs) throws IOException; } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AptSnapshotHandler.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AptSnapshotHandler.java index f31928a0e1..e001ea44fe 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AptSnapshotHandler.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/AptSnapshotHandler.java @@ -49,14 +49,14 @@ public static final class State { public final String assetPath; - public State(String assetPath) { + public State(final String assetPath) { super(); this.assetPath = assetPath; } } @Override - public Response handle(Context context) throws Exception { + public Response handle(final Context context) throws Exception { String path = context.getRequest().getPath(); Matcher matcher = SNAPSHOT_PATH_PATTERN.matcher(path); if (!matcher.matches()) { @@ -75,7 +75,7 @@ public Response handle(Context context) throws Exception { } } - private Response handleSnapshotAdminRequest(Context context, String id) throws Exception { + private Response handleSnapshotAdminRequest(final Context context, final String id) throws Exception { String method = context.getRequest().getAction(); Repository repository = context.getRepository(); AptSnapshotFacet snapshotFacet = repository.facet(AptSnapshotFacet.class); @@ -112,7 +112,7 @@ private Response doDelete(final String id, final AptSnapshotFacet snapshotFacet) return HttpResponses.noContent(); } - private Response handleSnapshotFetchRequest(Context context, String id, String path) throws Exception { + private Response handleSnapshotFetchRequest(final Context context, final String id, final String path) throws Exception { Repository repository = context.getRepository(); AptSnapshotFacet snapshotFacet = repository.facet(AptSnapshotFacet.class); if (snapshotFacet.isSnapshotableFile(path)) { diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/FilteredSnapshotComponentSelector.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/FilteredSnapshotComponentSelector.java index 4105d50089..ea9a4a8000 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/FilteredSnapshotComponentSelector.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/FilteredSnapshotComponentSelector.java @@ -30,12 +30,12 @@ public class FilteredSnapshotComponentSelector private final ControlFile settings; - public FilteredSnapshotComponentSelector(ControlFile settings) { + public FilteredSnapshotComponentSelector(final ControlFile settings) { this.settings = settings; } @Override - public List getArchitectures(Release release) { + public List getArchitectures(final Release release) { Optional> settingsArchitectures = settings.getField("Architectures") .map(s -> s.listValue()) .map(l -> new HashSet<>(l)); @@ -50,7 +50,7 @@ public List getArchitectures(Release release) { } @Override - public List getComponents(Release release) { + public List getComponents(final Release release) { Optional> settingsComponents = settings.getField("Components").map(s -> s.listValue()) .map(l -> new HashSet<>(l)); if (settingsComponents.isPresent()) { diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/SnapshotComponentSelector.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/SnapshotComponentSelector.java index 410125cfba..6a70066039 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/SnapshotComponentSelector.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/SnapshotComponentSelector.java @@ -21,7 +21,7 @@ public interface SnapshotComponentSelector { - List getArchitectures(Release release); + List getArchitectures(final Release release); - List getComponents(Release release); + List getComponents(final Release release); } diff --git a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/SnapshotItem.java b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/SnapshotItem.java index 9d0a0196e7..05f47f8198 100644 --- a/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/SnapshotItem.java +++ b/plugins/nexus-repository-apt/src/main/java/org/sonatype/nexus/repository/apt/internal/snapshot/SnapshotItem.java @@ -37,7 +37,7 @@ public static enum Role private final String mimeType; - Role(String mimeType) { + Role(final String mimeType) { this.mimeType = mimeType; } @@ -52,7 +52,7 @@ public static class ContentSpecifier public final Role role; - public ContentSpecifier(String path, Role role) { + public ContentSpecifier(final String path, final Role role) { super(); this.path = path; this.role = role; @@ -63,7 +63,7 @@ public ContentSpecifier(String path, Role role) { public final Content content; - public SnapshotItem(ContentSpecifier specifier, Content content) { + public SnapshotItem(final ContentSpecifier specifier, final Content content) { super(); this.specifier = specifier; this.content = content; diff --git a/plugins/nexus-repository-apt/src/test/java/org/sonatype/nexus/repository/apt/internal/AptComponentDirectorTest.java b/plugins/nexus-repository-apt/src/test/java/org/sonatype/nexus/repository/apt/internal/AptComponentDirectorTest.java new file mode 100644 index 0000000000..052fe6ef8b --- /dev/null +++ b/plugins/nexus-repository-apt/src/test/java/org/sonatype/nexus/repository/apt/internal/AptComponentDirectorTest.java @@ -0,0 +1,108 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.apt.internal; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Collections; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.entity.EntityId; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.apt.internal.hosted.AptHostedFacet; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.storage.Bucket; +import org.sonatype.nexus.repository.storage.BucketStore; +import org.sonatype.nexus.repository.storage.Component; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.repository.storage.StorageTx; + +import org.junit.Test; +import org.mockito.Mock; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * @since 3.next + */ +public class AptComponentDirectorTest + extends TestSupport +{ + @Mock + private BucketStore bucketStore; + + @Mock + private RepositoryManager repositoryManager; + + @Mock + private Component component; + + @Mock + private Repository source; + + @Mock + private AptHostedFacet sourceFacet; + + @Mock + private Repository destination; + + @Mock + private StorageTx tx; + + @Mock + private StorageFacet storageFacet; + + @Test + public void allowMoveTest() { + AptComponentDirector director = new AptComponentDirector(bucketStore, repositoryManager); + assertTrue(director.allowMoveTo(destination)); + assertTrue(director.allowMoveFrom(source)); + + EntityId bucketId = mock(EntityId.class); + when(component.bucketId()).thenReturn(bucketId); + Bucket bucket = mock(Bucket.class); + when(bucketStore.getById(bucketId)).thenReturn(bucket); + when(bucket.getRepositoryName()).thenReturn("repo"); + when(repositoryManager.get("repo")).thenReturn(source); + + assertTrue(director.allowMoveTo(component, destination)); + } + + @Test + public void refreshMetadataAfterMoveTest() throws IOException + { + when(destination.facet(StorageFacet.class)).thenReturn(storageFacet); + when(storageFacet.txSupplier()).thenReturn(() -> tx); + when(destination.facet(AptHostedFacet.class)).thenReturn(sourceFacet); + + AptComponentDirector director = new AptComponentDirector(bucketStore, repositoryManager); + director.afterMove(Collections.emptyList(), destination); + verify(sourceFacet).rebuildIndexes(); + } + + @Test(expected = UncheckedIOException.class) + public void afterMoveHookIoExceptionTest() throws IOException + { + when(destination.facet(StorageFacet.class)).thenReturn(storageFacet); + when(storageFacet.txSupplier()).thenReturn(() -> tx); + when(destination.facet(AptHostedFacet.class)).thenReturn(sourceFacet); + + AptComponentDirector director = new AptComponentDirector(bucketStore, repositoryManager); + doThrow(new IOException()).when(sourceFacet).rebuildIndexes(); + director.afterMove(Collections.emptyList(), destination); + } +} diff --git a/plugins/nexus-repository-npm/src/main/java/org/sonatype/nexus/repository/npm/internal/NpmStreamPayload.java b/plugins/nexus-repository-npm/src/main/java/org/sonatype/nexus/repository/npm/internal/NpmStreamPayload.java index 38ea0a1a7d..b42c33b7f5 100644 --- a/plugins/nexus-repository-npm/src/main/java/org/sonatype/nexus/repository/npm/internal/NpmStreamPayload.java +++ b/plugins/nexus-repository-npm/src/main/java/org/sonatype/nexus/repository/npm/internal/NpmStreamPayload.java @@ -17,10 +17,14 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.List; +import java.util.Objects; import org.sonatype.nexus.repository.storage.MissingAssetBlobException; import org.sonatype.nexus.repository.view.payloads.StreamPayload; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import static com.fasterxml.jackson.core.JsonGenerator.Feature.AUTO_CLOSE_TARGET; import static com.fasterxml.jackson.databind.SerializationFeature.FLUSH_AFTER_WRITE_VALUE; import static java.util.Collections.emptyList; @@ -37,6 +41,8 @@ public class NpmStreamPayload extends StreamPayload { + private static final Logger log = LoggerFactory.getLogger(NpmStreamPayload.class); + private String revId; private String packageId; @@ -78,8 +84,16 @@ public InputStream openInputStream() throws IOException { return super.openInputStream(); } catch (MissingAssetBlobException e) { // NOSONAR - return nonNull(missingBlobInputStreamSupplier) ? - missingBlobInputStreamSupplier.apply(e) : errorInputStream("Missing blob and no handler set to recover."); + if (nonNull(missingBlobInputStreamSupplier)) { + return missingBlobInputStreamSupplier.apply(e); + } + + // NEXUS-20040 - add logging detailing the missing handler and missing blob. + log.warn("Missing blob for asset {} and no handler set to recover, for package '{}' and rev '{}'", + e.getAsset(), Objects.toString(packageId, "unknown"), Objects.toString(revId, "unknown"), + log.isDebugEnabled() ? e : null); + + return errorInputStream("Missing blob and no handler set to recover."); } } diff --git a/plugins/nexus-restore-apt/pom.xml b/plugins/nexus-restore-apt/pom.xml new file mode 100644 index 0000000000..0e598619d5 --- /dev/null +++ b/plugins/nexus-restore-apt/pom.xml @@ -0,0 +1,66 @@ + + + + + nexus-plugins + org.sonatype.nexus.plugins + 3.17.0-SNAPSHOT + + 4.0.0 + + + nexus-restore-apt + ${project.groupId}:${project.artifactId} + bundle + + + + org.sonatype.nexus.plugins + nexus-blobstore-tasks + provided + + + + org.sonatype.nexus + nexus-repository + provided + + + + org.sonatype.nexus.plugins + nexus-repository-apt + provided + + + + org.sonatype.goodies + goodies-testsupport + test + + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + + + + + diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/model/LicenseUser.js b/plugins/nexus-restore-apt/src/main/java/org/sonatype/nexus/blobstore/restore/apt/internal/AptRestoreBlobData.java similarity index 64% rename from plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/model/LicenseUser.js rename to plugins/nexus-restore-apt/src/main/java/org/sonatype/nexus/blobstore/restore/apt/internal/AptRestoreBlobData.java index aed136f20c..966cd649b8 100644 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/model/LicenseUser.js +++ b/plugins/nexus-restore-apt/src/main/java/org/sonatype/nexus/blobstore/restore/apt/internal/AptRestoreBlobData.java @@ -10,19 +10,18 @@ * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the * Eclipse Foundation. All other trademarks are the property of their respective owners. */ -/*global Ext, NX*/ +package org.sonatype.nexus.blobstore.restore.apt.internal; + +import org.sonatype.nexus.blobstore.restore.RestoreBlobData; +import org.sonatype.nexus.blobstore.restore.RestoreBlobDataSupport; /** - * License User model. - * - * @since 3.0 + * @since 3.next */ -Ext.define('NX.coreui.model.LicenseUser', { - extend: 'Ext.data.Model', - fields: [ - {name: 'ip', type: 'string', sortType: 'asUCText'}, - {name: 'userId', type: 'string', sortType: 'asUCText', convert: Ext.htmlEncode }, - {name: 'userAgent', type: 'string', sortType: 'asUCText', convert: Ext.htmlEncode }, - {name: 'timestamp', type: 'date', dateFormat: 'c' } - ] -}); +public class AptRestoreBlobData + extends RestoreBlobDataSupport +{ + public AptRestoreBlobData(final RestoreBlobData blobData) { + super(blobData); + } +} diff --git a/plugins/nexus-restore-apt/src/main/java/org/sonatype/nexus/blobstore/restore/apt/internal/AptRestoreBlobStrategy.java b/plugins/nexus-restore-apt/src/main/java/org/sonatype/nexus/blobstore/restore/apt/internal/AptRestoreBlobStrategy.java new file mode 100644 index 0000000000..9b90c0da7b --- /dev/null +++ b/plugins/nexus-restore-apt/src/main/java/org/sonatype/nexus/blobstore/restore/apt/internal/AptRestoreBlobStrategy.java @@ -0,0 +1,124 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.restore.apt.internal; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; + +import javax.annotation.Nonnull; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.sonatype.nexus.blobstore.api.BlobStoreManager; +import org.sonatype.nexus.blobstore.restore.BaseRestoreBlobStrategy; +import org.sonatype.nexus.blobstore.restore.RestoreBlobData; +import org.sonatype.nexus.common.hash.HashAlgorithm; +import org.sonatype.nexus.common.log.DryRunPrefix; +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.apt.AptRestoreFacet; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.storage.AssetBlob; +import org.sonatype.nexus.repository.storage.Query; + +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Lists.newArrayList; +import static org.eclipse.aether.util.StringUtils.isEmpty; +import static org.sonatype.nexus.common.hash.HashAlgorithm.MD5; +import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA1; +import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA256; + +/** + * @since 3.next + */ +@Named("apt") +@Singleton +public class AptRestoreBlobStrategy + extends BaseRestoreBlobStrategy +{ + @Inject + public AptRestoreBlobStrategy(final NodeAccess nodeAccess, + final RepositoryManager repositoryManager, + final BlobStoreManager blobStoreManager, + final DryRunPrefix dryRunPrefix) + { + super(nodeAccess, repositoryManager, blobStoreManager, dryRunPrefix); + } + + @Override + protected AptRestoreBlobData createRestoreData(final RestoreBlobData blobData) { + checkState(!isEmpty(blobData.getBlobName()), "Blob name cannot be empty"); + return new AptRestoreBlobData(blobData); + } + + @Override + protected boolean canAttemptRestore(@Nonnull final AptRestoreBlobData data) { + Repository repository = data.getBlobData().getRepository(); + Optional aptRestoreFacetFacet = repository.optionalFacet(AptRestoreFacet.class); + + if (!aptRestoreFacetFacet.isPresent()) { + log.warn("Skipping as APT Restore Facet not found on repository: {}", repository.getName()); + return false; + } + return true; + } + + @Override + protected String getAssetPath(@Nonnull final AptRestoreBlobData data) { + return data.getBlobData().getBlobName(); + } + + @Override + protected boolean assetExists(@Nonnull final AptRestoreBlobData data) { + AptRestoreFacet facet = data.getBlobData().getRepository().facet(AptRestoreFacet.class); + return facet.assetExists(data.getBlobData().getBlobName()); + } + + @Override + protected void createAssetFromBlob(@Nonnull final AssetBlob assetBlob, @Nonnull final AptRestoreBlobData data) + throws IOException + { + final Repository repository = data.getBlobData().getRepository(); + AptRestoreFacet facet = repository.facet(AptRestoreFacet.class); + final String path = data.getBlobData().getBlobName(); + + facet.restore(assetBlob, path); + } + + @Override + protected boolean componentRequired(final AptRestoreBlobData data) { + final Repository repository = data.getBlobData().getRepository(); + final AptRestoreFacet facet = repository.facet(AptRestoreFacet.class); + return facet.componentRequired(data.getBlobData().getBlobName()); + } + + @Override + protected Repository getRepository(@Nonnull final AptRestoreBlobData data) { + return data.getBlobData().getRepository(); + } + + @Override + protected Query getComponentQuery(final AptRestoreBlobData data) throws IOException { + final Repository repository = data.getBlobData().getRepository(); + final AptRestoreFacet facet = repository.facet(AptRestoreFacet.class); + return facet.getComponentQuery(data.getBlobData().getBlob()); + } + + @Nonnull + @Override + protected List getHashAlgorithms() { + return newArrayList(MD5, SHA1, SHA256); + } +} diff --git a/plugins/nexus-restore-apt/src/test/java/org/sonatype/nexus/blobstore/restore/apt/internal/AptRestoreBlobStrategyTest.java b/plugins/nexus-restore-apt/src/test/java/org/sonatype/nexus/blobstore/restore/apt/internal/AptRestoreBlobStrategyTest.java new file mode 100644 index 0000000000..d83ff8fc66 --- /dev/null +++ b/plugins/nexus-restore-apt/src/test/java/org/sonatype/nexus/blobstore/restore/apt/internal/AptRestoreBlobStrategyTest.java @@ -0,0 +1,179 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.restore.apt.internal; + +import java.io.ByteArrayInputStream; +import java.util.Optional; +import java.util.Properties; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.blobstore.api.BlobStoreManager; +import org.sonatype.nexus.blobstore.restore.RestoreBlobData; +import org.sonatype.nexus.common.log.DryRunPrefix; +import org.sonatype.nexus.common.node.NodeAccess; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.apt.AptRestoreFacet; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.storage.AssetBlob; +import org.sonatype.nexus.repository.storage.StorageFacet; +import org.sonatype.nexus.repository.storage.StorageTx; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.common.hash.HashAlgorithm.MD5; +import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA1; +import static org.sonatype.nexus.common.hash.HashAlgorithm.SHA256; + +public class AptRestoreBlobStrategyTest + extends TestSupport +{ + AptRestoreBlobStrategy underTest; + + private static final String TEST_BLOB_STORE_NAME = "test"; + + private static final String PACKAGE_PATH = "pool/main/n/nano/nano_2.9.3-2_amd64.deb"; + + private static final String RELEASE_FILE_PATH = "metadata/Release"; + + @Mock + RepositoryManager repositoryManager; + + @Mock + NodeAccess nodeAccess; + + @Mock + BlobStoreManager blobStoreManager; + + @Mock + Repository repository; + + @Mock + StorageFacet storageFacet; + + @Mock + AptRestoreFacet aptRestoreFacet; + + @Mock + AptRestoreBlobData aptRestoreBlobData; + + @Mock + private RestoreBlobData restoreBlobData; + + @Mock + Blob blob; + + @Mock + BlobStore blobStore; + + @Mock + StorageTx storageTx; + + byte[] blobBytes = "blobbytes".getBytes(); + + Properties properties = new Properties(); + + @Before + public void setup() { + underTest = new AptRestoreBlobStrategy(nodeAccess, repositoryManager, blobStoreManager, new DryRunPrefix("dryrun")); + + when(repositoryManager.get(anyString())).thenReturn(repository); + when(repository.facet(AptRestoreFacet.class)).thenReturn(aptRestoreFacet); + when(repository.optionalFacet(AptRestoreFacet.class)).thenReturn(Optional.of(aptRestoreFacet)); + when(repository.optionalFacet(StorageFacet.class)).thenReturn(Optional.of(storageFacet)); + when(aptRestoreBlobData.getBlobData()).thenReturn(restoreBlobData); + when(aptRestoreBlobData.getBlobData().getBlobName()).thenReturn(PACKAGE_PATH); + when(storageFacet.txSupplier()).thenReturn(() -> storageTx); + when(blob.getInputStream()).thenReturn(new ByteArrayInputStream(blobBytes)); + when(blobStoreManager.get(TEST_BLOB_STORE_NAME)).thenReturn(blobStore); + when(restoreBlobData.getRepository()).thenReturn(repository); + + properties.setProperty("@BlobStore.created-by", "anonymous"); + properties.setProperty("size", "1330"); + properties.setProperty("@Bucket.repo-name", "apt-proxy"); + properties.setProperty("creationTime", "1533220387218"); + properties.setProperty("@BlobStore.created-by-ip", "127.0.0.1"); + properties.setProperty("@BlobStore.content-type", "text/html"); + properties.setProperty("@BlobStore.blob-name", PACKAGE_PATH); + properties.setProperty("sha1", "0088eb478752a810f48f04d3cf9f46d2924e334a"); + } + + @Test + public void testBlobDataIsCreated() { + assertThat(underTest.createRestoreData(restoreBlobData).getBlobData(), is(restoreBlobData)); + } + + @Test(expected = IllegalStateException.class) + public void testIfBlobDataNameIsEmpty_ExceptionIsThrown() { + when(aptRestoreBlobData.getBlobData().getBlobName()).thenReturn(""); + underTest.createRestoreData(restoreBlobData); + } + + @Test + public void testCorrectHashAlgorithmsAreSupported() { + assertThat(underTest.getHashAlgorithms(), containsInAnyOrder(SHA1, SHA256, MD5)); + } + + @Test + public void testAppropriatePathIsReturned() { + assertThat(underTest.getAssetPath(aptRestoreBlobData), is(PACKAGE_PATH)); + } + + @Test + public void testPackageIsRestored() throws Exception { + underTest.restore(properties, blob, TEST_BLOB_STORE_NAME, false); + verify(aptRestoreFacet).assetExists(PACKAGE_PATH); + verify(aptRestoreFacet).restore(any(AssetBlob.class), eq(PACKAGE_PATH)); + verifyNoMoreInteractions(aptRestoreFacet); + } + + @Test + public void testRestoreIsSkip_IfPackageExists() { + when(aptRestoreFacet.assetExists(PACKAGE_PATH)).thenReturn(true); + underTest.restore(properties, blob, TEST_BLOB_STORE_NAME, false); + + verify(aptRestoreFacet).assetExists(PACKAGE_PATH); + verify(aptRestoreFacet).componentRequired(PACKAGE_PATH); + verifyNoMoreInteractions(aptRestoreFacet); + } + + @Test + public void testComponentIsRequiredForDeb() { + boolean expected = true; + when(aptRestoreFacet.componentRequired(PACKAGE_PATH)).thenReturn(expected); + assertThat(underTest.componentRequired(aptRestoreBlobData), is(expected)); + verify(aptRestoreFacet).componentRequired(PACKAGE_PATH); + verifyNoMoreInteractions(aptRestoreFacet); + } + + @Test + public void testComponentIsNotRequiredForRelease() { + boolean expected = false; + when(aptRestoreFacet.componentRequired(RELEASE_FILE_PATH)).thenReturn(expected); + assertThat(underTest.componentRequired(aptRestoreBlobData), is(expected)); + verify(aptRestoreFacet).componentRequired(PACKAGE_PATH); + verifyNoMoreInteractions(aptRestoreFacet); + } +} diff --git a/plugins/pom.xml b/plugins/pom.xml index 4f32a6f7a4..48dfc44ce9 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -40,6 +40,7 @@ nexus-repository-apt nexus-repository-pypi nexus-repository-raw + nexus-restore-apt nexus-restore-maven nexus-restore-npm nexus-restore-pypi @@ -236,6 +237,20 @@ 3.17.0-SNAPSHOT
+ + org.sonatype.nexus.plugins + nexus-restore-apt + 3.17.0-SNAPSHOT + + + + org.sonatype.nexus.plugins + nexus-restore-apt + 3.17.0-SNAPSHOT + features + xml + + org.sonatype.nexus.plugins nexus-repository-apt diff --git a/revision.txt b/revision.txt index 82e1b5cb87..7a4aa3ce14 100644 --- a/revision.txt +++ b/revision.txt @@ -1 +1 @@ -b=master,r=7bea9085eca0d09f0a17e2ca40477e16e8d23c19,t=2019-05-31-1703-25512 \ No newline at end of file +b=master,r=d6a18e2c3d6731d5c7d56ecde7710a69ea4d0c6f,t=2019-06-07-1023-08730 \ No newline at end of file