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

org.apache.hadoop.fs.azurebfs.utils.CachedSASToken Maven / Gradle / Ivy

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.fs.azurebfs.utils; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import org.apache.hadoop.thirdparty.com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.hadoop.fs.azurebfs.constants.FileSystemConfigurations.DEFAULT_SAS_TOKEN_RENEW_PERIOD_FOR_STREAMS_IN_SECONDS; import static java.time.temporal.ChronoUnit.SECONDS; /** * CachedSASToken provides simple utility for managing renewal * of SAS tokens used by Input/OutputStream. This enables SAS re-use * and reduces calls to the SASTokenProvider. */ public final class CachedSASToken { public static final Logger LOG = LoggerFactory.getLogger(CachedSASToken.class); private final long minExpirationInSeconds; private String sasToken; private OffsetDateTime sasExpiry; /** * Create instance with default minimum expiration. SAS tokens are * automatically renewed when their expiration is within this period. */ public CachedSASToken() { this(DEFAULT_SAS_TOKEN_RENEW_PERIOD_FOR_STREAMS_IN_SECONDS); } /** * Create instance with specified minimum expiration. SAS tokens are * automatically renewed when their expiration is within this period. * @param minExpirationInSeconds */ public CachedSASToken(long minExpirationInSeconds) { this.minExpirationInSeconds = minExpirationInSeconds; } /** * Checks if the SAS token is expired or near expiration. * @param expiry * @param minExpiryInSeconds * @return true if the SAS is near sasExpiry; otherwise false */ private static boolean isNearExpiry(OffsetDateTime expiry, long minExpiryInSeconds) { if (expiry == OffsetDateTime.MIN) { return true; } OffsetDateTime utcNow = OffsetDateTime.now(ZoneOffset.UTC); return utcNow.until(expiry, SECONDS) <= minExpiryInSeconds; } /** * Parse the sasExpiry from the SAS token. The sasExpiry is the minimum * of the ske and se parameters. The se parameter is required and the * ske parameter is optional. * @param token an Azure Storage SAS token * @return the sasExpiry or OffsetDateTime.MIN if invalid. */ private static OffsetDateTime getExpiry(String token) { // return MIN for all invalid input, including a null token if (token == null) { return OffsetDateTime.MIN; } String signedExpiry = "se="; int signedExpiryLen = 3; int start = token.indexOf(signedExpiry); // return MIN if the required se parameter is absent if (start == -1) { return OffsetDateTime.MIN; } start += signedExpiryLen; // extract the value of se parameter int end = token.indexOf("&", start); String seValue = (end == -1) ? token.substring(start) : token.substring(start, end); try { seValue = URLDecoder.decode(seValue, "utf-8"); } catch (UnsupportedEncodingException ex) { LOG.error("Error decoding se query parameter ({}) from SAS.", seValue, ex); return OffsetDateTime.MIN; } // parse the ISO 8601 date value; return MIN if invalid OffsetDateTime seDate = OffsetDateTime.MIN; try { seDate = OffsetDateTime.parse(seValue, DateTimeFormatter.ISO_DATE_TIME); } catch (DateTimeParseException ex) { LOG.error("Error parsing se query parameter ({}) from SAS.", seValue, ex); } String signedKeyExpiry = "ske="; int signedKeyExpiryLen = 4; // if ske is present, the sasExpiry is the minimum of ske and se start = token.indexOf(signedKeyExpiry); // return seDate if ske is absent if (start == -1) { return seDate; } start += signedKeyExpiryLen; // extract the value of ske parameter end = token.indexOf("&", start); String skeValue = (end == -1) ? token.substring(start) : token.substring(start, end); try { skeValue = URLDecoder.decode(skeValue, "utf-8"); } catch (UnsupportedEncodingException ex) { LOG.error("Error decoding ske query parameter ({}) from SAS.", skeValue, ex); return OffsetDateTime.MIN; } // parse the ISO 8601 date value; return MIN if invalid OffsetDateTime skeDate = OffsetDateTime.MIN; try { skeDate = OffsetDateTime.parse(skeValue, DateTimeFormatter.ISO_DATE_TIME); } catch (DateTimeParseException ex) { LOG.error("Error parsing ske query parameter ({}) from SAS.", skeValue, ex); return OffsetDateTime.MIN; } return skeDate.isBefore(seDate) ? skeDate : seDate; } /** * Updates the cached SAS token and expiry. If the token is invalid, the cached value * is cleared by setting it to null and the expiry to MIN. * @param token an Azure Storage SAS token */ public void update(String token) { // quickly return if token and cached sasToken are the same reference // Note: use of operator == is intentional if (token == sasToken) { return; } OffsetDateTime newExpiry = getExpiry(token); boolean isInvalid = isNearExpiry(newExpiry, minExpirationInSeconds); synchronized (this) { if (isInvalid) { sasToken = null; sasExpiry = OffsetDateTime.MIN; } else { sasToken = token; sasExpiry = newExpiry; } } } /** * Gets the token if still valid. * @return the token or null if it is expired or near sasExpiry. */ public String get() { // quickly return null if not set if (sasToken == null) { return null; } String token; OffsetDateTime exp; synchronized (this) { token = sasToken; exp = sasExpiry; } boolean isInvalid = isNearExpiry(exp, minExpirationInSeconds); return isInvalid ? null : token; } @VisibleForTesting void setForTesting(String token, OffsetDateTime expiry) { synchronized (this) { sasToken = token; sasExpiry = expiry; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy