From 8bf5159c0dd4cdac336e357b0c340c4a2c995ab3 Mon Sep 17 00:00:00 2001 From: Guillaume Rams Date: Wed, 3 Oct 2018 18:52:53 +0200 Subject: [PATCH] Add possibility to assumeRole and canned ACL choice --- .../java/hudson/plugins/s3/ClientHelper.java | 34 +++++++++++---- src/main/java/hudson/plugins/s3/Entry.java | 21 +++++++-- .../hudson/plugins/s3/S3BucketPublisher.java | 43 ++++++++++++++++++- .../java/hudson/plugins/s3/S3Profile.java | 20 ++++++--- src/main/java/hudson/plugins/s3/Uploads.java | 7 ++- .../s3/callable/S3BaseUploadCallable.java | 16 +++++-- .../plugins/s3/callable/S3Callable.java | 8 ++-- .../s3/callable/S3DownloadCallable.java | 4 +- .../plugins/s3/callable/S3GzipCallable.java | 8 ++-- .../plugins/s3/callable/S3UploadCallable.java | 8 ++-- .../hudson/plugins/s3/Entry/config.jelly | 3 ++ .../plugins/s3/Entry/help-cannedACL.html | 5 +++ .../plugins/s3/S3BucketPublisher/global.jelly | 5 +++ src/main/webapp/help-assumeRoleArn.html | 5 +++ src/test/java/hudson/plugins/s3/S3Test.java | 5 ++- 15 files changed, 154 insertions(+), 38 deletions(-) create mode 100644 src/main/resources/hudson/plugins/s3/Entry/help-cannedACL.html create mode 100644 src/main/webapp/help-assumeRoleArn.html diff --git a/src/main/java/hudson/plugins/s3/ClientHelper.java b/src/main/java/hudson/plugins/s3/ClientHelper.java index 8251d2d2..1717dab5 100644 --- a/src/main/java/hudson/plugins/s3/ClientHelper.java +++ b/src/main/java/hudson/plugins/s3/ClientHelper.java @@ -1,7 +1,11 @@ package hudson.plugins.s3; import com.amazonaws.ClientConfiguration; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.auth.InstanceProfileCredentialsProvider; +import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider; import com.amazonaws.regions.Region; import com.amazonaws.regions.RegionUtils; import com.amazonaws.regions.Regions; @@ -18,20 +22,32 @@ public class ClientHelper { public final static String DEFAULT_AMAZON_S3_REGION_NAME = System.getProperty( "hudson.plugins.s3.DEFAULT_AMAZON_S3_REGION", com.amazonaws.services.s3.model.Region.US_Standard.toAWSRegion().getName()); + /** + * An identifier for the assumed role session. + */ + public final static String DEFAULT_ROLE_SESSION_NAME = "jenkins-s3-plugin"; - public static AmazonS3Client createClient(String accessKey, String secretKey, boolean useRole, String region, ProxyConfiguration proxy) - { + public static AmazonS3Client createClient(String accessKey, String secretKey, boolean useRole, String assumeRoleArn, String region, ProxyConfiguration proxy) { Region awsRegion = getRegionFromString(region); ClientConfiguration clientConfiguration = getClientConfiguration(proxy, awsRegion); - final AmazonS3Client client; - if (useRole) { - client = new AmazonS3Client(clientConfiguration); + final AWSCredentialsProvider credentials; + if (assumeRoleArn.isEmpty()) { + if (useRole) { + credentials = InstanceProfileCredentialsProvider.getInstance(); + } else { + credentials = new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey)); + } } else { - client = new AmazonS3Client(new BasicAWSCredentials(accessKey, secretKey), clientConfiguration); + if (useRole) { + credentials = new STSAssumeRoleSessionCredentialsProvider(assumeRoleArn, DEFAULT_ROLE_SESSION_NAME); + } else { + credentials = new STSAssumeRoleSessionCredentialsProvider(new BasicAWSCredentials(accessKey, secretKey), assumeRoleArn, DEFAULT_ROLE_SESSION_NAME); + } } + final AmazonS3Client client = new AmazonS3Client(credentials, clientConfiguration); client.setRegion(awsRegion); return client; @@ -89,13 +105,13 @@ public static ClientConfiguration getClientConfiguration(@Nonnull ProxyConfigura } private static boolean shouldUseProxy(ProxyConfiguration proxy, String hostname) { - if(proxy == null) { + if (proxy == null) { return false; } boolean shouldProxy = true; - for(Pattern p : proxy.getNoProxyHostPatterns()) { - if(p.matcher(hostname).matches()) { + for (Pattern p : proxy.getNoProxyHostPatterns()) { + if (p.matcher(hostname).matches()) { shouldProxy = false; break; } diff --git a/src/main/java/hudson/plugins/s3/Entry.java b/src/main/java/hudson/plugins/s3/Entry.java index 54d0c746..b910a008 100644 --- a/src/main/java/hudson/plugins/s3/Entry.java +++ b/src/main/java/hudson/plugins/s3/Entry.java @@ -3,6 +3,7 @@ import com.amazonaws.regions.Region; import com.amazonaws.regions.RegionUtils; import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.CannedAccessControlList; import hudson.Extension; import hudson.model.Describable; import hudson.model.Descriptor; @@ -43,7 +44,7 @@ public final class Entry implements Describable { * Stores the Region Value */ public String selectedRegion; - + /** * Do not publish the artifacts when build fails */ @@ -58,12 +59,17 @@ public final class Entry implements Describable { * Let Jenkins manage the S3 uploaded artifacts */ public boolean managedArtifacts; - + /** * Use S3 server side encryption when uploading the artifacts */ public boolean useServerSideEncryption; + /** + * Use this canned ACL when uploading + */ + public String cannedACL; + /** * Flatten directories */ @@ -93,7 +99,7 @@ public final class Entry implements Describable { @DataBoundConstructor public Entry(String bucket, String sourceFile, String excludedFile, String storageClass, String selectedRegion, boolean noUploadOnFailure, boolean uploadFromSlave, boolean managedArtifacts, - boolean useServerSideEncryption, boolean flatten, boolean gzipFiles, boolean keepForever, + boolean useServerSideEncryption, String cannedACL, boolean flatten, boolean gzipFiles, boolean keepForever, boolean showDirectlyInBrowser, List userMetadata) { this.bucket = bucket; this.sourceFile = sourceFile; @@ -104,6 +110,7 @@ public Entry(String bucket, String sourceFile, String excludedFile, String stora this.uploadFromSlave = uploadFromSlave; this.managedArtifacts = managedArtifacts; this.useServerSideEncryption = useServerSideEncryption; + this.cannedACL = cannedACL; this.flatten = flatten; this.gzipFiles = gzipFiles; this.keepForever = keepForever; @@ -134,6 +141,14 @@ public ListBoxModel doFillStorageClassItems() { return model; } + public ListBoxModel doFillCannedACLItems() { + final ListBoxModel model = new ListBoxModel(); + for (CannedAccessControlList cacl : CannedAccessControlList.values()) { + model.add(cacl.toString(), cacl.name()); + } + return model; + } + public ListBoxModel doFillSelectedRegionItems() { final ListBoxModel model = new ListBoxModel(); for (Region r : regions) { diff --git a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java index e2a1000b..3586eed1 100644 --- a/src/main/java/hudson/plugins/s3/S3BucketPublisher.java +++ b/src/main/java/hudson/plugins/s3/S3BucketPublisher.java @@ -33,6 +33,7 @@ import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Pattern; public final class S3BucketPublisher extends Recorder implements SimpleBuildStep { @@ -262,7 +263,7 @@ public void perform(@Nonnull Run run, @Nonnull FilePath ws, @Nonnull Launc final Map escapedMetadata = buildMetadata(envVars, entry); final List records = Lists.newArrayList(); - final List fingerprints = profile.upload(run, bucket, paths, filenames, escapedMetadata, storageClass, selRegion, entry.uploadFromSlave, entry.managedArtifacts, entry.useServerSideEncryption, entry.gzipFiles); + final List fingerprints = profile.upload(run, bucket, paths, filenames, escapedMetadata, storageClass, selRegion, entry.uploadFromSlave, entry.managedArtifacts, entry.useServerSideEncryption, entry.cannedACL, entry.gzipFiles); for (FingerprintRecord fingerprintRecord : fingerprints) { records.add(fingerprintRecord); @@ -502,7 +503,45 @@ public FormValidation doLoginCheck(final StaplerRequest req, StaplerResponse rsp final String defaultRegion = ClientHelper.DEFAULT_AMAZON_S3_REGION_NAME; final AmazonS3Client client = ClientHelper.createClient( - accessKey, secretKey, useRole, defaultRegion, Jenkins.getActiveInstance().proxy); + accessKey, secretKey, useRole, "", defaultRegion, Jenkins.getActiveInstance().proxy); + + try { + client.listBuckets(); + } catch (AmazonClientException e) { + LOGGER.log(Level.SEVERE, e.getMessage(), e); + return FormValidation.error("Can't connect to S3 service: " + e.getMessage()); + } + return FormValidation.ok("Check passed!"); + } + + @SuppressWarnings("unused") + public FormValidation doAssumeRoleArnCheck(final StaplerRequest req, StaplerResponse rsp) { + final String assumeRoleArn = Util.fixNull(req.getParameter("assumeRoleArn")); + if (assumeRoleArn.isEmpty()) { + return FormValidation.ok(); + } + if (!Pattern.matches("(?i)arn:aws:iam::\\d+:role/.+", assumeRoleArn.toLowerCase())) { + return FormValidation.error("Does not look like a ARN"); + } + + final String useIAMCredential = Util.fixNull(req.getParameter("useRole")); + final boolean useRole = Boolean.parseBoolean(useIAMCredential); + if (useRole) { + // Won't be able to validate this because IAM role on master could be different on slave(s) + return FormValidation.ok(); + } + + final String accessKey = Util.fixNull(req.getParameter("accessKey")); + if (accessKey.isEmpty()) + return FormValidation.ok("Please, enter accessKey"); + + final String secretKey = Util.fixNull(req.getParameter("secretKey")); + if (secretKey.isEmpty()) + return FormValidation.ok("Please, enter secretKey"); + + final String defaultRegion = ClientHelper.DEFAULT_AMAZON_S3_REGION_NAME; + final AmazonS3Client client = ClientHelper.createClient( + accessKey, secretKey, useRole, assumeRoleArn, defaultRegion, Jenkins.getActiveInstance().proxy); try { client.listBuckets(); diff --git a/src/main/java/hudson/plugins/s3/S3Profile.java b/src/main/java/hudson/plugins/s3/S3Profile.java index 95074592..5d783861 100644 --- a/src/main/java/hudson/plugins/s3/S3Profile.java +++ b/src/main/java/hudson/plugins/s3/S3Profile.java @@ -30,6 +30,7 @@ public class S3Profile { private final String name; private final String accessKey; private final Secret secretKey; + private final String assumeRoleArn; private final int maxUploadRetries; private final int uploadRetryTime; private final int maxDownloadRetries; @@ -40,9 +41,10 @@ public class S3Profile { private final int signedUrlExpirySeconds; @DataBoundConstructor - public S3Profile(String name, String accessKey, String secretKey, boolean useRole, int signedUrlExpirySeconds, String maxUploadRetries, String uploadRetryTime, String maxDownloadRetries, String downloadRetryTime, boolean keepStructure) { + public S3Profile(String name, String accessKey, String secretKey, boolean useRole, String assumeRoleArn, int signedUrlExpirySeconds, String maxUploadRetries, String uploadRetryTime, String maxDownloadRetries, String downloadRetryTime, boolean keepStructure) { this.name = name; this.useRole = useRole; + this.assumeRoleArn = assumeRoleArn; this.maxUploadRetries = parseWithDefault(maxUploadRetries, 5); this.uploadRetryTime = parseWithDefault(uploadRetryTime, 5); this.maxDownloadRetries = parseWithDefault(maxDownloadRetries, 5); @@ -83,6 +85,8 @@ public final String getAccessKey() { return accessKey; } + public final String getAssumeRoleArn() { return assumeRoleArn; } + public final Secret getSecretKey() { return secretKey; } @@ -112,7 +116,7 @@ public int getSignedUrlExpirySeconds() { } public AmazonS3Client getClient(String region) { - return ClientHelper.createClient(accessKey, Secret.toString(secretKey), useRole, region, getProxy()); + return ClientHelper.createClient(accessKey, Secret.toString(secretKey), useRole, assumeRoleArn, region, getProxy()); } public List upload(Run run, @@ -125,6 +129,7 @@ public List upload(Run run, final boolean uploadFromSlave, final boolean managedArtifacts, final boolean useServerSideEncryption, + final String cannedACL, final boolean gzipFiles) throws IOException, InterruptedException { final List fingerprints = new ArrayList<>(fileNames.size()); @@ -145,11 +150,11 @@ public List upload(Run run, final MasterSlaveCallable upload; if (gzipFiles) { - upload = new S3GzipCallable(accessKey, secretKey, useRole, dest, userMetadata, - storageClass, selregion, useServerSideEncryption, getProxy()); + upload = new S3GzipCallable(accessKey, secretKey, useRole, assumeRoleArn, dest, userMetadata, + storageClass, selregion, useServerSideEncryption, cannedACL, getProxy()); } else { - upload = new S3UploadCallable(accessKey, secretKey, useRole, dest, userMetadata, - storageClass, selregion, useServerSideEncryption, getProxy()); + upload = new S3UploadCallable(accessKey, secretKey, useRole, assumeRoleArn, dest, userMetadata, + storageClass, selregion, useServerSideEncryption, cannedACL, getProxy()); } final FingerprintRecord fingerprintRecord = repeat(maxUploadRetries, uploadRetryTime, dest, new Callable() { @@ -241,7 +246,7 @@ public List downloadAll(Run build, fingerprints.add(repeat(maxDownloadRetries, downloadRetryTime, dest, new Callable() { @Override public FingerprintRecord call() throws IOException, InterruptedException { - final String md5 = target.act(new S3DownloadCallable(accessKey, secretKey, useRole, dest, artifact.getRegion(), getProxy())); + final String md5 = target.act(new S3DownloadCallable(accessKey, secretKey, useRole, assumeRoleArn, dest, artifact.getRegion(), getProxy())); return new FingerprintRecord(true, dest.bucketName, target.getName(), artifact.getRegion(), md5); } })); @@ -292,6 +297,7 @@ public String toString() { ", accessKey='" + accessKey + '\'' + ", secretKey=" + secretKey + ", useRole=" + useRole + + ", assumeRoleArn=" + assumeRoleArn + '}'; } diff --git a/src/main/java/hudson/plugins/s3/Uploads.java b/src/main/java/hudson/plugins/s3/Uploads.java index c7d6a02a..69233fb6 100644 --- a/src/main/java/hudson/plugins/s3/Uploads.java +++ b/src/main/java/hudson/plugins/s3/Uploads.java @@ -1,6 +1,7 @@ package hudson.plugins.s3; import com.amazonaws.AmazonServiceException; +import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.model.PutObjectRequest; import com.amazonaws.services.s3.transfer.TransferManager; @@ -21,9 +22,13 @@ private Uploads() {} private final transient HashMap startedUploads = new HashMap<>(); private final transient HashMap openedStreams = new HashMap<>(); - public Upload startUploading(TransferManager manager, FilePath file, InputStream inputsStream, String bucketName, String objectName, ObjectMetadata metadata) throws AmazonServiceException { + public Upload startUploading(TransferManager manager, FilePath file, InputStream inputsStream, String bucketName, String objectName, ObjectMetadata metadata, CannedAccessControlList cannedAcl) throws AmazonServiceException { final PutObjectRequest request = new PutObjectRequest(bucketName, objectName, inputsStream, metadata); + if (!cannedAcl.equals(CannedAccessControlList.Private)) { + request.setCannedAcl(cannedAcl); + } + // Set the buffer size (ReadLimit) equal to the multipart upload size, // allowing us to resend data if the connection breaks. request.getRequestClientOptions().setReadLimit(MULTIPART_UPLOAD_THRESHOLD); diff --git a/src/main/java/hudson/plugins/s3/callable/S3BaseUploadCallable.java b/src/main/java/hudson/plugins/s3/callable/S3BaseUploadCallable.java index 76e72cc5..41692c60 100644 --- a/src/main/java/hudson/plugins/s3/callable/S3BaseUploadCallable.java +++ b/src/main/java/hudson/plugins/s3/callable/S3BaseUploadCallable.java @@ -1,6 +1,7 @@ package hudson.plugins.s3.callable; import com.amazonaws.services.s3.internal.Mimetypes; +import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.ObjectMetadata; import hudson.FilePath; import hudson.ProxyConfiguration; @@ -21,16 +22,18 @@ public abstract class S3BaseUploadCallable extends S3Callable { private final String storageClass; private final Map userMetadata; private final boolean useServerSideEncryption; + private final String cannedACLName; - public S3BaseUploadCallable(String accessKey, Secret secretKey, boolean useRole, + public S3BaseUploadCallable(String accessKey, Secret secretKey, boolean useRole, String assumeRoleArn, Destination dest, Map userMetadata, String storageClass, String selregion, - boolean useServerSideEncryption, ProxyConfiguration proxy) { - super(accessKey, secretKey, useRole, selregion, proxy); + boolean useServerSideEncryption, String cannedACLName, ProxyConfiguration proxy) { + super(accessKey, secretKey, useRole, assumeRoleArn, selregion, proxy); this.dest = dest; this.storageClass = storageClass; this.userMetadata = userMetadata; this.useServerSideEncryption = useServerSideEncryption; + this.cannedACLName = cannedACLName; } /** @@ -89,4 +92,11 @@ protected ObjectMetadata buildMetadata(FilePath filePath) throws IOException, In public Destination getDest() { return dest; } + + public CannedAccessControlList getCannedAcl() { + if (cannedACLName == null) { + return CannedAccessControlList.Private; + } + return CannedAccessControlList.valueOf(cannedACLName); + } } diff --git a/src/main/java/hudson/plugins/s3/callable/S3Callable.java b/src/main/java/hudson/plugins/s3/callable/S3Callable.java index 0916366a..ab40e4a1 100644 --- a/src/main/java/hudson/plugins/s3/callable/S3Callable.java +++ b/src/main/java/hudson/plugins/s3/callable/S3Callable.java @@ -16,15 +16,17 @@ abstract class S3Callable implements FileCallable { private final String accessKey; private final Secret secretKey; private final boolean useRole; + private final String assumeRoleArn; private final String region; private final ProxyConfiguration proxy; private static transient HashMap transferManagers = new HashMap<>(); - S3Callable(String accessKey, Secret secretKey, boolean useRole, String region, ProxyConfiguration proxy) { + S3Callable(String accessKey, Secret secretKey, boolean useRole, String assumeRoleArn, String region, ProxyConfiguration proxy) { this.accessKey = accessKey; this.secretKey = secretKey; this.useRole = useRole; + this.assumeRoleArn = assumeRoleArn; this.region = region; this.proxy = proxy; } @@ -32,7 +34,7 @@ abstract class S3Callable implements FileCallable { protected synchronized TransferManager getTransferManager() { final String uniqueKey = getUniqueKey(); if (transferManagers.get(uniqueKey) == null) { - final AmazonS3 client = ClientHelper.createClient(accessKey, Secret.toString(secretKey), useRole, region, proxy); + final AmazonS3 client = ClientHelper.createClient(accessKey, Secret.toString(secretKey), useRole, assumeRoleArn, region, proxy); transferManagers.put(uniqueKey, new TransferManager(client)); } @@ -45,6 +47,6 @@ public void checkRoles(RoleChecker roleChecker) throws SecurityException { } private String getUniqueKey() { - return region + '_' + secretKey + '_' + accessKey + '_' + useRole; + return region + '_' + secretKey + '_' + accessKey + '_' + useRole + '_' + assumeRoleArn; } } \ No newline at end of file diff --git a/src/main/java/hudson/plugins/s3/callable/S3DownloadCallable.java b/src/main/java/hudson/plugins/s3/callable/S3DownloadCallable.java index 82964882..d5cf9f3e 100644 --- a/src/main/java/hudson/plugins/s3/callable/S3DownloadCallable.java +++ b/src/main/java/hudson/plugins/s3/callable/S3DownloadCallable.java @@ -16,9 +16,9 @@ public final class S3DownloadCallable extends S3Callable private static final long serialVersionUID = 1L; private final Destination dest; - public S3DownloadCallable(String accessKey, Secret secretKey, boolean useRole, Destination dest, String region, ProxyConfiguration proxy) + public S3DownloadCallable(String accessKey, Secret secretKey, boolean useRole, String assumeRoleArn, Destination dest, String region, ProxyConfiguration proxy) { - super(accessKey, secretKey, useRole, region, proxy); + super(accessKey, secretKey, useRole, assumeRoleArn, region, proxy); this.dest = dest; } diff --git a/src/main/java/hudson/plugins/s3/callable/S3GzipCallable.java b/src/main/java/hudson/plugins/s3/callable/S3GzipCallable.java index 735831e0..b0d87081 100644 --- a/src/main/java/hudson/plugins/s3/callable/S3GzipCallable.java +++ b/src/main/java/hudson/plugins/s3/callable/S3GzipCallable.java @@ -1,5 +1,6 @@ package hudson.plugins.s3.callable; +import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.ObjectMetadata; import com.amazonaws.services.s3.transfer.Upload; import com.amazonaws.event.ProgressListener; @@ -17,8 +18,8 @@ import java.util.zip.GZIPOutputStream; public final class S3GzipCallable extends S3BaseUploadCallable implements MasterSlaveCallable { - public S3GzipCallable(String accessKey, Secret secretKey, boolean useRole, Destination dest, Map userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, ProxyConfiguration proxy) { - super(accessKey, secretKey, useRole, dest, userMetadata, storageClass, selregion, useServerSideEncryption, proxy); + public S3GzipCallable(String accessKey, Secret secretKey, boolean useRole, String assumeRoleArn, Destination dest, Map userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, String cannedACL, ProxyConfiguration proxy) { + super(accessKey, secretKey, useRole, assumeRoleArn, dest, userMetadata, storageClass, selregion, useServerSideEncryption, cannedACL, proxy); } // Return a File containing the gzipped contents of the input file. @@ -67,8 +68,9 @@ public String invoke(FilePath file) throws IOException, InterruptedException { final ObjectMetadata metadata = buildMetadata(file); metadata.setContentEncoding("gzip"); metadata.setContentLength(localFile.length()); + final CannedAccessControlList cannedAcl = getCannedAcl(); - upload = Uploads.getInstance().startUploading(getTransferManager(), file, gzipedStream, getDest().bucketName, getDest().objectName, metadata); + upload = Uploads.getInstance().startUploading(getTransferManager(), file, gzipedStream, getDest().bucketName, getDest().objectName, metadata, cannedAcl); String md5 = MD5.generateFromFile(localFile); diff --git a/src/main/java/hudson/plugins/s3/callable/S3UploadCallable.java b/src/main/java/hudson/plugins/s3/callable/S3UploadCallable.java index 064aea57..b3fdcc12 100644 --- a/src/main/java/hudson/plugins/s3/callable/S3UploadCallable.java +++ b/src/main/java/hudson/plugins/s3/callable/S3UploadCallable.java @@ -1,5 +1,6 @@ package hudson.plugins.s3.callable; +import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.ObjectMetadata; import hudson.FilePath; import hudson.ProxyConfiguration; @@ -14,8 +15,8 @@ public final class S3UploadCallable extends S3BaseUploadCallable implements MasterSlaveCallable { private static final long serialVersionUID = 1L; - public S3UploadCallable(String accessKey, Secret secretKey, boolean useRole, Destination dest, Map userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, ProxyConfiguration proxy) { - super(accessKey, secretKey, useRole, dest, userMetadata, storageClass, selregion, useServerSideEncryption, proxy); + public S3UploadCallable(String accessKey, Secret secretKey, boolean useRole, String assumeRoleArn, Destination dest, Map userMetadata, String storageClass, String selregion, boolean useServerSideEncryption, String cannedACL, ProxyConfiguration proxy) { + super(accessKey, secretKey, useRole, assumeRoleArn, dest, userMetadata, storageClass, selregion, useServerSideEncryption, cannedACL, proxy); } /** @@ -24,8 +25,9 @@ public S3UploadCallable(String accessKey, Secret secretKey, boolean useRole, Des @Override public String invoke(FilePath file) throws IOException, InterruptedException { final ObjectMetadata metadata = buildMetadata(file); + final CannedAccessControlList cannedAcl = getCannedAcl(); - Uploads.getInstance().startUploading(getTransferManager(), file, file.read(), getDest().bucketName, getDest().objectName, metadata); + Uploads.getInstance().startUploading(getTransferManager(), file, file.read(), getDest().bucketName, getDest().objectName, metadata, cannedAcl); return MD5.generateFromFile(file); } diff --git a/src/main/resources/hudson/plugins/s3/Entry/config.jelly b/src/main/resources/hudson/plugins/s3/Entry/config.jelly index 7046a7ed..2755be8c 100644 --- a/src/main/resources/hudson/plugins/s3/Entry/config.jelly +++ b/src/main/resources/hudson/plugins/s3/Entry/config.jelly @@ -28,6 +28,9 @@ + + + diff --git a/src/main/resources/hudson/plugins/s3/Entry/help-cannedACL.html b/src/main/resources/hudson/plugins/s3/Entry/help-cannedACL.html new file mode 100644 index 00000000..559f2d85 --- /dev/null +++ b/src/main/resources/hudson/plugins/s3/Entry/help-cannedACL.html @@ -0,0 +1,5 @@ +
+ Choose a canned ACL for uploaded objects. See possible values here. AWS default value + for new objects is private. Setting any other value requires to be granted PutObjectAcl. +
diff --git a/src/main/resources/hudson/plugins/s3/S3BucketPublisher/global.jelly b/src/main/resources/hudson/plugins/s3/S3BucketPublisher/global.jelly index 86dcfb42..98393889 100644 --- a/src/main/resources/hudson/plugins/s3/S3BucketPublisher/global.jelly +++ b/src/main/resources/hudson/plugins/s3/S3BucketPublisher/global.jelly @@ -23,6 +23,11 @@ onchange="Form.findMatchingInput(this,'s3.accessKey').onchange()" /> + + + diff --git a/src/main/webapp/help-assumeRoleArn.html b/src/main/webapp/help-assumeRoleArn.html new file mode 100644 index 00000000..82fa141a --- /dev/null +++ b/src/main/webapp/help-assumeRoleArn.html @@ -0,0 +1,5 @@ +
If defined, ARN of an IAM role to assume to access S3. +

Use given credentials above (Access key and Secret key or instance IAM role) to get temporary credentials from + STS, assuming this role to access S3. This can be used to access cross-account buckets, by assuming a role + defined in another account. +

diff --git a/src/test/java/hudson/plugins/s3/S3Test.java b/src/test/java/hudson/plugins/s3/S3Test.java index 7700edde..ec5a7105 100644 --- a/src/test/java/hudson/plugins/s3/S3Test.java +++ b/src/test/java/hudson/plugins/s3/S3Test.java @@ -38,7 +38,7 @@ public void testConfigExists() throws Exception { @Test public void testConfigContainsProfiles() throws Exception { - final S3Profile profile = new S3Profile("S3 profile random name", null, null, true, 0, "0", "0", "0", "0", true); + final S3Profile profile = new S3Profile("S3 profile random name", null, null, true, null,0, "0", "0", "0", "0", true); replaceS3PluginProfile(profile); @@ -72,7 +72,7 @@ public void multiplePublishersUseExistingActions() throws Exception { } private Entry entryForFile(String fileName) { - return new Entry("bucket", fileName, "", "", "", false, false, true, false, false, false, false, false, null); + return new Entry("bucket", fileName, "", "", "", false, false, true, false, "", false, false, false, false, null); } private Builder stepCreatingFile(String fileName) { @@ -100,6 +100,7 @@ private S3Profile mockS3Profile(String profileName) throws IOException, Interrup Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyString(), Mockito.anyBoolean() )).thenReturn(newArrayList(new FingerprintRecord(true, "bucket", "path", "eu-west-1", "xxxx"))); return profile;