All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sap.hana.datalake.files.directaccess.s3.S3SignedUrl Maven / Gradle / Ivy

Go to download

An implementation of org.apache.hadoop.fs.FileSystem targeting SAP HANA Data Lake Files.

There is a newer version: 3.0.27
Show newest version
// © 2024 SAP SE or an SAP affiliate company. All rights reserved.
package com.sap.hana.datalake.files.directaccess.s3;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.sap.hana.datalake.files.HdlfsConstants;
import com.sap.hana.datalake.files.HdlfsFileSystemCapabilities;
import com.sap.hana.datalake.files.directaccess.BaseSignedUrl;

/* package-private */ class S3SignedUrl extends BaseSignedUrl {

  // https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
  private static final Pattern PARAMETER_X_AMZ_DATE_PATTERN = Pattern.compile("[&?]X-Amz-Date=(\\d{8}T\\d{6}Z)", Pattern.CASE_INSENSITIVE);
  private static final Pattern PARAMETER_X_AMZ_EXPIRES_PATTERN = Pattern.compile("[&?]X-Amz-Expires=(\\d+)", Pattern.CASE_INSENSITIVE);
  private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'").withZone(ZoneOffset.UTC);

  private final Instant expirationTime;

  public static S3SignedUrl from(final HdlfsFileSystemCapabilities.DirectAccessSignedUrl directAccessSignedUrl, final int expirationSafetyMargin) {
    return new S3SignedUrl(directAccessSignedUrl, expirationSafetyMargin);
  }

  /* package-private */ S3SignedUrl(final HdlfsFileSystemCapabilities.DirectAccessSignedUrl directAccessSignedUrl,
          final int expirationSafetyMarginSeconds) {
    super(directAccessSignedUrl, expirationSafetyMarginSeconds);
    this.expirationTime = this.getExpirationTimeFromSignedUrl(this.url);
  }

  public Instant getExpirationTime() {
    return this.expirationTime;
  }

  @Override
  public boolean isExpired() {
    return this.expirationTime.isBefore(Instant.now());
  }

  private Instant getExpirationTimeFromSignedUrl(final String signedUrl) {
    final String decodedUrl;

    try {
      decodedUrl = URLDecoder.decode(signedUrl, String.valueOf(HdlfsConstants.DEFAULT_CHARSET));
    } catch (final UnsupportedEncodingException ex) {
      throw new RuntimeException("Error occurred while decoding the signed url", ex);
    }

    final Matcher creationDateMatcher = PARAMETER_X_AMZ_DATE_PATTERN.matcher(decodedUrl);
    final Matcher expirationPeriodMatcher = PARAMETER_X_AMZ_EXPIRES_PATTERN.matcher(decodedUrl);
    final boolean bothFound = creationDateMatcher.find() && expirationPeriodMatcher.find();

    if (!bothFound) {
      throw new IllegalArgumentException("Signed URL does not contain an expiration time");
    }

    final TemporalAccessor creationDate = DATE_FORMATTER.parse(creationDateMatcher.group(1));
    final int timeBeforeExpiration = Integer.parseInt(expirationPeriodMatcher.group(1));

    final Instant expirationTime = Instant.from(creationDate).plusSeconds(timeBeforeExpiration);
    final Instant expirationTimeWithSafetyMargin = expirationTime.minusSeconds(this.expirationSafetyMarginSeconds);

    // do not apply safety margin if that means the URL would not be valid for at least one second
    if (expirationTimeWithSafetyMargin.isAfter(Instant.now().plusSeconds(1))) {
      return expirationTimeWithSafetyMargin;
    } else {
      return expirationTime;
    }
  }

}

// © 2024 SAP SE or an SAP affiliate company. All rights reserved.




© 2015 - 2025 Weber Informatics LLC | Privacy Policy