Create a presigned URL for Amazon S3

0
Hello,  I need to pass in a REST call a path for uploaded file. Searching the forum, I saw that Medix File object doesn't  share path. As an alternative, the client suggested that I use "Create a presigned URL for Amazon S3" to create a path and pass it via REST. Does some one use this in there application and can share the code? Or maybe suggest a simple solution.
asked
5 answers
1

Cant you make your own Java Action for this?

AWS Documentation shows a quite simple implementation of generating Signed URL which can later be consumed REST API to which mendix UI Can put files. https://docs.aws.amazon.com/AmazonS3/latest/userguide/example_s3_Scenario_PresignedUrl_section.html

 

1 trick can be used like, you can take look at the already generated java action files which mendix uses (for e.g.: if you integration Amazon S3 connector module you will get all those needed Java Actions) and then see how it used the AWS Credentials (your java action may need to employ the same idea)

 

Then from the documentation you can implement the java code to get the pre-signed URL.

 

Which is a REST URL that you can first check what is its signature on the request body and response body. so that as soon as Java action is triggered and url is obtained, you can consume it in mendix MF and put a file to it.

 

Seems doable :)

 

EDIT: below is the Java action that I made where it uses a simple way to generate the signed URL. once you get it in the MF, you use it just like any REST API to PUT an object to your S3 bucket.

 

// This file was generated by Mendix Studio Pro.
//
// WARNING: Only the following code will be retained when actions are regenerated:
// - the import list
// - the code between BEGIN USER CODE and END USER CODE
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
// Other code you write will be lost the next time you deploy the project.
// Special characters, e.g., é, ö, à, etc. are supported in comments.

package myfirstmodule.actions;

import java.net.URL;
import java.util.Date;
import com.amazonaws.HttpMethod;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import com.mendix.systemwideinterfaces.core.IContext;
import com.mendix.systemwideinterfaces.core.IMendixObject;
import com.mendix.webui.CustomJavaAction;
import amazons3connector.impl.MxLogger;

public class JA_GeneratePresignedS3URL extends CustomJavaAction<java.lang.String>
{
	private java.lang.String BucketName;
	private java.lang.String ObjectKey;
	private IMendixObject __Credentials;
	private awsauthentication.proxies.Credentials Credentials;

	public JA_GeneratePresignedS3URL(IContext context, java.lang.String BucketName, java.lang.String ObjectKey, IMendixObject Credentials)
	{
		super(context);
		this.BucketName = BucketName;
		this.ObjectKey = ObjectKey;
		this.__Credentials = Credentials;
	}

	@java.lang.Override
	public java.lang.String executeAction() throws Exception
	{
		this.Credentials = this.__Credentials == null ? null : awsauthentication.proxies.Credentials.initialize(getContext(), __Credentials);

		// BEGIN USER CODE
		AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
				.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(this.Credentials.getAccessKey(), this.Credentials.getSecretAccessKey())))
				.withRegion("ap-south-1").build();
		final int expirationSeconds = 12 * 3600;
		final Date expiresAt = new Date(System.currentTimeMillis() + expirationSeconds * 1000);

		GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(this.BucketName, this.ObjectKey)
				.withMethod(HttpMethod.PUT).withExpiration(expiresAt);

		URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);
		return url.toExternalForm();

		// END USER CODE
	}

	/**
	 * Returns a string representation of this action
	 * @return a string representation of this action
	 */
	@java.lang.Override
	public java.lang.String toString()
	{
		return "JA_GeneratePresignedS3URL";
	}

	// BEGIN EXTRA CODE
	private static final MxLogger LOGGER = new MxLogger(JA_GeneratePresignedS3URL.class);
	// END EXTRA CODE
}

 

answered
1

We're planning to add this functionality to our S3 connector, in 1 or 2 month.

answered
0

Hi Lital, 

thanks for reaching out. Unfortunately, this is currently not part of the S3 connector. 
Can you elaborate a bit more about your use case? We have this on our radar and this can help us understand if this is something we should prioritize to integrate in the connector. Thanks!

answered
0

Good day, 

Is the pre-signed URL still not a feature in the AWS S3 connector?

If not, is their an alternative method to do this in Mendix using S3?

answered
0

Hi Lital,

I'm not sure if you are still looking for a solution, but I was able to figure it out recently.

 

The criteria for the following code is:

  • File must exist on S3 already. The code generates a presigned URL for that object.
// This file was generated by Mendix Studio Pro.
//
// WARNING: Only the following code will be retained when actions are regenerated:
// - the import list
// - the code between BEGIN USER CODE and END USER CODE
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
// Other code you write will be lost the next time you deploy the project.
// Special characters, e.g., é, ö, à, etc. are supported in comments.

package myfirstmodule.actions;

import java.time.Duration;
import com.mendix.systemwideinterfaces.core.IContext;
import com.mendix.webui.CustomJavaAction;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;

public class Java_GeneratePresignedS3URL_2 extends CustomJavaAction<java.lang.String>
{
	private java.lang.String BucketName;
	private java.lang.String ObjectKey;
	private java.lang.String AccessKeyId;
	private java.lang.String SecretAccessKey;
	private java.lang.String region;
	private java.lang.String sessionToken;
	private java.lang.Long ExpirationSeconds;
	private java.lang.Boolean useSessionCredentials;

	public Java_GeneratePresignedS3URL_2(IContext context, java.lang.String BucketName, java.lang.String ObjectKey, java.lang.String AccessKeyId, java.lang.String SecretAccessKey, java.lang.String region, java.lang.String sessionToken, java.lang.Long ExpirationSeconds, java.lang.Boolean useSessionCredentials)
	{
		super(context);
		this.BucketName = BucketName;
		this.ObjectKey = ObjectKey;
		this.AccessKeyId = AccessKeyId;
		this.SecretAccessKey = SecretAccessKey;
		this.region = region;
		this.sessionToken = sessionToken;
		this.ExpirationSeconds = ExpirationSeconds;
		this.useSessionCredentials = useSessionCredentials;
	}

	@java.lang.Override
	public java.lang.String executeAction() throws Exception
	{
		// BEGIN USER CODE
        // Create an AwsSessionCredentials or AwsBasicCredentials object using the provided credentials
        StaticCredentialsProvider credentialsProvider;
        if (useSessionCredentials) {
            AwsSessionCredentials awsSessionCredentials = AwsSessionCredentials.create(
                    AccessKeyId, SecretAccessKey, sessionToken);
            credentialsProvider = StaticCredentialsProvider.create(awsSessionCredentials);
        } else {
            AwsBasicCredentials awsBasicCredentials = AwsBasicCredentials.create(
                    AccessKeyId, SecretAccessKey);
            credentialsProvider = StaticCredentialsProvider.create(awsBasicCredentials);
        }

        // Set the AWS region
        Region awsRegion = Region.of(region);

        // Create an S3Presigner using the provided credentials
        S3Presigner presigner = S3Presigner.builder()
                                           .region(awsRegion)
                                           .credentialsProvider(credentialsProvider)
                                           .build();

        // Create a GetObjectRequest
        GetObjectRequest getObjectRequest = GetObjectRequest.builder()
                                                            .bucket(BucketName)
                                                            .key(ObjectKey)
                                                            .build();

        // Create a GetObjectPresignRequest with an expiration time of 10 minutes
        GetObjectPresignRequest getObjectPresignRequest = GetObjectPresignRequest.builder()
                                                                                 .signatureDuration(Duration.ofSeconds(ExpirationSeconds))
                                                                                 .getObjectRequest(getObjectRequest)
                                                                                 .build();

        // Generate the presigned URL
        String PresignedURL = presigner.presignGetObject(getObjectPresignRequest).url().toString();

        // Close the presigner
        presigner.close();

        return PresignedURL;
		// END USER CODE
	}

	/**
	 * Returns a string representation of this action
	 * @return a string representation of this action
	 */
	@java.lang.Override
	public java.lang.String toString()
	{
		return "Java_GeneratePresignedS3URL_2";
	}

	// BEGIN EXTRA CODE
	// END EXTRA CODE
}

 

You can create a java action with the required parameters, and then drop the code between "// BEGIN USER CODE" and "// END USER CODE" into the java code.

 

Hope this helps.

answered