Skip to content

Commit

Permalink
upstream: b=main,r=46438642abd802c3c81f8fa04ea6cec54a55aab5,t=2024-01…
Browse files Browse the repository at this point in the history
…-26-1343-01840
  • Loading branch information
sonatype-zion committed Jan 26, 2024
1 parent 8279a25 commit 51396c2
Show file tree
Hide file tree
Showing 26 changed files with 607 additions and 448 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,6 @@ public interface FeatureFlags

String REACT_ROLES_MODAL_NAMED = "${nexus.react.roles.modal.enabled:-false}";

String BLOBSTORE_OWNERSHIP_CHECK_DISABLED_NAMED = "${nexus.blobstore.s3.ownership.check.disabled:-false}";

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;

import javax.annotation.Nullable;

import org.sonatype.nexus.common.collect.NestedAttributesMap;
Expand Down Expand Up @@ -145,4 +146,9 @@ public boolean delete() {
public Component unwrap() {
return component;
}

@Override
public String toString() {
return toStringExternal();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,13 @@ export default Machine(
{
actions: {
showResetSuccess: () => {
ExtJS.showSuccessMessage(UIStrings.USER_TOKEN.MESSAGES.RESET_SUCCESS)
console.error('showResetSuccess function must be overridden in order to show the appropriate message');
},
showResetError: () => {
ExtJS.showErrorMessage(UIStrings.USER_TOKEN.MESSAGES.RESET_ERROR)
console.error('showResetError function must be overridden in order to show the appropriate message');
},
showAccessError: () => {
ExtJS.showErrorMessage(UIStrings.USER_TOKEN.MESSAGES.ACCESS_ERROR);
console.error('showAccessError function must be overridden in order to show the appropriate message');
},
setToken: assign({
token: (_, event) => event.data.data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,6 @@ export default {

SAVE_SUCCESS: 'The form was saved successfully',

USER_TOKEN: {
MESSAGES: {
ACCESS_ERROR: 'You must authenticate successfully to access your token',
RESET_SUCCESS: 'Your user token has been reset',
RESET_ERROR: 'You must authenticate successfully to reset your token'
}
},

MULTI_SELECT: {
FROM_LABEL: 'Available',
TO_LABEL: 'Selected',
Expand Down
6 changes: 6 additions & 0 deletions plugins/nexus-blobstore-s3/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>pl.pragmatists</groupId>
<artifactId>JUnitParams</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
package org.sonatype.nexus.blobstore.s3.internal;

import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;

import org.sonatype.goodies.common.ComponentSupport;
Expand All @@ -31,7 +31,9 @@
import com.amazonaws.services.s3.model.lifecycle.LifecycleFilterPredicate;
import com.amazonaws.services.s3.model.lifecycle.LifecyclePrefixPredicate;
import com.amazonaws.services.s3.model.lifecycle.LifecycleTagPredicate;
import com.google.common.annotations.VisibleForTesting;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;
Expand All @@ -41,10 +43,12 @@
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreConfigurationHelper.getConfiguredExpirationInDays;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.ACCESS_DENIED_CODE;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.INVALID_ACCESS_KEY_ID_CODE;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.METHOD_NOT_ALLOWED_CODE;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.SIGNATURE_DOES_NOT_MATCH_CODE;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.bucketOwnershipError;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.buildException;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.insufficientCreatePermissionsError;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.invalidIdentityError;
import static org.sonatype.nexus.blobstore.s3.internal.S3BlobStoreException.unexpectedError;

/**
Expand All @@ -63,6 +67,13 @@ public class BucketManager

private AmazonS3 s3;

private final BucketOwnershipCheckFeatureFlag ownershipCheckFeatureFlag;

@Inject
public BucketManager(BucketOwnershipCheckFeatureFlag featureFlag) {
this.ownershipCheckFeatureFlag = checkNotNull(featureFlag);
}

public void setS3(final AmazonS3 s3) {
this.s3 = s3;
}
Expand Down Expand Up @@ -114,7 +125,8 @@ public void deleteStorageLocation(final BlobStoreConfiguration blobStoreConfigur
}
}

private boolean isExpirationLifecycleConfigurationPresent(final BucketLifecycleConfiguration lifecycleConfiguration,
@VisibleForTesting
boolean isExpirationLifecycleConfigurationPresent(final BucketLifecycleConfiguration lifecycleConfiguration,
final BlobStoreConfiguration blobStoreConfiguration) {
String bucketPrefix = getBucketPrefix(blobStoreConfiguration);
int expirationInDays = getConfiguredExpirationInDays(blobStoreConfiguration);
Expand Down Expand Up @@ -217,7 +229,7 @@ private boolean isBucketPrefixPredicate(final LifecycleFilterPredicate filterPre

private void checkPermissions(final String bucket) {
checkCredentials(bucket);
if (s3.doesBucketExistV2(bucket)) {
if (!ownershipCheckFeatureFlag.isDisabled() && s3.doesBucketExistV2(bucket)) {
checkBucketOwner(bucket);
}
}
Expand All @@ -239,14 +251,20 @@ private void checkCredentials(final String bucket) {

private void checkBucketOwner(final String bucket) {
try {
s3.getBucketAcl(bucket);
s3.getBucketPolicy(bucket);
}
catch (AmazonS3Exception e) {
if (ACCESS_DENIED_CODE.equals(e.getErrorCode())) {
log.debug("Exception thrown checking bucket owner.", e);
String errorCode = e.getErrorCode();
String logMessage = String.format("Exception thrown checking ownership of \"%s\" bucket.", bucket);
if (ACCESS_DENIED_CODE.equals(errorCode)) {
log.debug(logMessage, e);
throw bucketOwnershipError();
}
log.info("Exception thrown checking bucket owner.", e);
if (METHOD_NOT_ALLOWED_CODE.equals(errorCode)) {
log.debug(logMessage, e);
throw invalidIdentityError();
}
log.info(logMessage, log.isDebugEnabled() ? e : e.getMessage());
throw unexpectedError("checking bucket ownership");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
package org.sonatype.nexus.repository.firewall.event;
package org.sonatype.nexus.blobstore.s3.internal;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.sonatype.goodies.packageurl.PackageUrl;
import org.sonatype.nexus.common.app.FeatureFlags;

/**
* @since 3.29
*/
public class ComponentVersionsRequest
@Named
@Singleton
public class BucketOwnershipCheckFeatureFlag
{
private final CompletableFuture<List<String>> result = new CompletableFuture<>();

private final PackageUrl packageUrl;

public ComponentVersionsRequest(final PackageUrl packageUrl) {
this.packageUrl = packageUrl;
}
private final boolean isDisabled;

public CompletableFuture<List<String>> getResult() {
return result;
@Inject
public BucketOwnershipCheckFeatureFlag(
@Named(FeatureFlags.BLOBSTORE_OWNERSHIP_CHECK_DISABLED_NAMED) final Boolean isDisabled)
{
this.isDisabled = Boolean.TRUE.equals(isDisabled);
}

public PackageUrl getPackageUrl() {
return packageUrl;
public boolean isDisabled() {
return isDisabled;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,15 @@ public class S3BlobStoreException

public static final String BUCKET_OWNERSHIP_ERR_MSG = "Bucket exists but is not owned by you.";

// If you have the correct permissions, but you're not using an identity
// that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not Allowed error.
// https://docs.aws.amazon.com/cli/latest/reference/s3api/get-bucket-policy.html#description
public static final String INVALID_IDENTITY_ERR_MSG = "The identity used does not belong to the bucket owner's account.";

public static final String ACCESS_DENIED_CODE = "AccessDenied";

public static final String METHOD_NOT_ALLOWED_CODE = "MethodNotAllowed";

public static final String INVALID_ACCESS_KEY_ID_CODE = "InvalidAccessKeyId";

public static final String SIGNATURE_DOES_NOT_MATCH_CODE = "SignatureDoesNotMatch";
Expand Down Expand Up @@ -83,6 +90,10 @@ public static S3BlobStoreException bucketOwnershipError() {
return new S3BlobStoreException(BUCKET_OWNERSHIP_ERR_MSG);
}

public static S3BlobStoreException invalidIdentityError() {
return new S3BlobStoreException(INVALID_IDENTITY_ERR_MSG);
}

@Override
public String getMessage() {
return message;
Expand Down
Loading

0 comments on commit 51396c2

Please sign in to comment.