Skip to content

Commit

Permalink
Add Overload to Sanitize AzureSasCredential Signature (#23033)
Browse files Browse the repository at this point in the history
Add Signature Encoding Overload to AzureSasCredential
  • Loading branch information
alzimmermsft authored Jul 19, 2021
1 parent fdb0e62 commit 7c9ac27
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 7 deletions.
5 changes: 0 additions & 5 deletions sdk/core/azure-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -250,24 +250,19 @@
<argLine>
--add-exports com.azure.core/com.azure.core.implementation.http=ALL-UNNAMED
--add-exports com.azure.core/com.azure.core.implementation.serializer=ALL-UNNAMED
--add-exports com.azure.core/com.azure.core.implementation.serializer.jackson=ALL-UNNAMED
--add-exports com.azure.core/com.azure.core.implementation.util=ALL-UNNAMED

--add-opens com.azure.core/com.azure.core=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.credential=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.http=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.http.policy=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.http.rest=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.implementation=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.implementation.entities=com.fasterxml.jackson.databind
--add-opens com.azure.core/com.azure.core.implementation.entities=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.implementation.http=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.implementation.models.jsonflatten=com.fasterxml.jackson.databind
--add-opens com.azure.core/com.azure.core.implementation.models.jsonflatten=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.implementation.serializer=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.models=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.util=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.util.jsonpatch=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.util.logging=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.util.polling=ALL-UNNAMED
--add-opens com.azure.core/com.azure.core.util.serializer=ALL-UNNAMED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,51 @@
import com.azure.core.util.logging.ClientLogger;

import java.util.Objects;
import java.util.function.Function;

/**
* Represents a credential that uses a shared access signature to authenticate to an Azure Service.
*/
public final class AzureSasCredential {
private final ClientLogger logger = new ClientLogger(AzureSasCredential.class);
private final Function<String, String> signatureEncoder;

private volatile String signature;

/**
* Creates a credential that authorizes request with the given shared access signature.
* <p>
* The {@code signature} passed is assumed to be encoded. This constructor is effectively the same as calling {@link
* #AzureSasCredential(String, Function) new AzureSasCredential(signature, null))}.
*
* @param signature The shared access signature used to authorize requests.
* @throws NullPointerException If {@code signature} is {@code null}.
* @throws IllegalArgumentException If {@code signature} is an empty string.
*/
public AzureSasCredential(String signature) {
this(signature, null);
}

/**
* Creates a credential that authorizes request within the given shared access signature.
* <p>
* If {@code signatureEncoder} is non-null the {@code signature}, and all {@link #update(String) updated
* signatures}, will be encoded using the function. {@code signatureEncoder} should be as idempotent as possible to
* reduce the chance of double encoding errors.
*
* @param signature The shared access signature used to authorize requests.
* @param signatureEncoder An optional function which encodes the {@code signature}.
* @throws NullPointerException If {@code signature} is {@code null}.
* @throws IllegalArgumentException If {@code signature} is an empty string.
*/
public AzureSasCredential(String signature, Function<String, String> signatureEncoder) {
Objects.requireNonNull(signature, "'signature' cannot be null.");
if (signature.isEmpty()) {
throw logger.logExceptionAsError(new IllegalArgumentException("'signature' cannot be empty."));
}

this.signature = signature;
this.signatureEncoder = signatureEncoder;
this.signature = (signatureEncoder == null) ? signature : signatureEncoder.apply(signature);
}

/**
Expand All @@ -53,7 +76,7 @@ public AzureSasCredential update(String signature) {
throw logger.logExceptionAsError(new IllegalArgumentException("'signature' cannot be empty."));
}

this.signature = signature;
this.signature = (signatureEncoder == null) ? signature : signatureEncoder.apply(signature);
return this;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.core.credential;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

/**
* Tests {@link AzureSasCredential}.
*/
public class AzureSasCredentialTests {
@ParameterizedTest
@MethodSource("invalidConstructorParametersSupplier")
public void invalidConstructorParameters(String signature, Class<? extends Throwable> expectedException) {
assertThrows(expectedException, () -> new AzureSasCredential(signature));
assertThrows(expectedException, () -> new AzureSasCredential(signature, String::toString));
}

private static Stream<Arguments> invalidConstructorParametersSupplier() {
return Stream.of(
Arguments.of(null, NullPointerException.class),
Arguments.of("", IllegalArgumentException.class)
);
}

@Test
public void baseConstructorReturnsSignatureAsIs() {
final String signature = "sas=a bc";

AzureSasCredential credential = new AzureSasCredential(signature);
assertEquals(signature, credential.getSignature());
}

@Test
public void constructorWithoutEncodingFunctionReturnsSignatureAsIs() {
final String signature = "sas=a bc";

AzureSasCredential credential = new AzureSasCredential(signature, null);
assertEquals(signature, credential.getSignature());
}

@Test
public void constructorEncodesSignature() {
final String signature = "sas=a bc";
final String expectedSignature = "sas=a%20bc";

AzureSasCredential credential = new AzureSasCredential(signature, sig -> sig.replaceAll(" ", "%20"));
assertEquals(expectedSignature, credential.getSignature());
}

@Test
public void credentialWithoutEncodingFunctionDoesNotEncodeUpdates() {
final String signature = "sas=a bc";
final String updatedSignature = "sas=a b c";

AzureSasCredential credential = new AzureSasCredential(signature).update(updatedSignature);
assertEquals(updatedSignature, credential.getSignature());
}

@Test
public void credentialWithEncodingFunctionEncodesSignatureUpdates() {
final String signature = "sas=a bc";
final String updatedSignature = "sas=a b c";
final String expectedSignature = "sas=a%20b%20c";

AzureSasCredential credential = new AzureSasCredential(signature, sig -> sig.replaceAll(" ", "%20"))
.update(updatedSignature);
assertEquals(expectedSignature, credential.getSignature());
}
}

0 comments on commit 7c9ac27

Please sign in to comment.