Skip to content

Commit

Permalink
Use AuthMode enum
Browse files Browse the repository at this point in the history
  • Loading branch information
gosar committed Aug 16, 2023
1 parent 73d24e7 commit 5cb8e80
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import software.amazon.smithy.model.traits.ProtocolDefinitionTrait;
import software.amazon.smithy.model.traits.Trait;
import software.amazon.smithy.model.traits.synthetic.NoAuthTrait;
import software.amazon.smithy.utils.MapUtils;

/**
* An index that resolves service protocols and auth schemes.
Expand Down Expand Up @@ -65,6 +66,22 @@ public static ServiceIndex of(Model model) {
return model.getKnowledge(ServiceIndex.class, ServiceIndex::new);
}

/**
* Defines the type for auth schemes returned by {@link #getEffectiveAuthSchemes}.
*/
public enum AuthMode {

/**
* Use only the modeled auth schemes.
*/
DEFAULT,

/**
* Use the modeled auth schemes, as well as the synthetic {@link NoAuthTrait} where applicable.
*/
NO_AUTH_AWARE;
}

/**
* Get all protocol traits attached to a service.
*
Expand Down Expand Up @@ -156,6 +173,30 @@ public Map<ShapeId, Trait> getEffectiveAuthSchemes(ToShapeId service) {
.orElse(Collections.emptyMap());
}

/**
* Gets a list of effective authentication schemes applied to a service, based on the AuthMode.
*
* <p>If AuthMode is {@code DEFAULT}, the behavior is same as
* {@link #getEffectiveAuthSchemes(ToShapeId)}.
*
* <p>If AuthMode is {@code NO_AUTH_AWARE}, the behavior is same, except that if the service has no effective
* auth schemes, instead of an empty map, it returns the {@code smithy.api#noAuth} auth scheme. It avoids having to
* special case handling an empty result. The returned map will always contain at least 1 entry.
*
* @param service Service to get the effective authentication schemes of.
* @param authMode AuthMode to determine which authentication schemes to include.
* @return Returns a map of the trait shape ID to the auth trait itself.
*/
public Map<ShapeId, Trait> getEffectiveAuthSchemes(ToShapeId service, AuthMode authMode) {
Map<ShapeId, Trait> authSchemes = getEffectiveAuthSchemes(service);
if (authMode == AuthMode.NO_AUTH_AWARE) {
if (authSchemes.isEmpty()) {
authSchemes = MapUtils.of(NoAuthTrait.ID, new NoAuthTrait());
}
}
return authSchemes;
}

/**
* Gets a list of effective authentication schemes applied to an operation
* bound within a service.
Expand Down Expand Up @@ -201,29 +242,34 @@ public Map<ShapeId, Trait> getEffectiveAuthSchemes(ToShapeId service, ToShapeId
}

/**
* Gets a list of effective authentication schemes applied to an operation bound within a service, that also
* includes the {@code smithy.api#noAuth} scheme when no authentication is to be used.
* Gets a list of effective authentication schemes applied to an operation
* bound within a service, based on the AuthMode.
*
* <p>If AuthMode is {@code DEFAULT}, the behavior is same as
* {@link #getEffectiveAuthSchemes(ToShapeId, ToShapeId)}.
*
* <p>This behaves the same as {@link #getEffectiveAuthSchemes(ToShapeId, ToShapeId)} with the following
* differences:
* <p>If AuthMode is {@code NO_AUTH_AWARE}, the behavior is same, with the following differences:
* If the operation has no effective auth schemes, instead of an empty map, it returns the {@code smithy.api#noAuth}
* auth scheme.
* If the operation has the {@code smithy.api#optionalAuth} trait, it adds {@code smithy.api#noAuth} to the end.
*
* <p>This is a convenience wrapper around getEffectiveAuthSchemes, that accounts for
* {@code smithy.api#optionalAuth} and avoids having to special case handling empty result. The returned map will
* always contain at least 1 entry.
* <p>Using {@code NO_AUTH_AWARE} accounts for {@code smithy.api#optionalAuth} and avoids having to special case
* handling an empty result. The returned map will always contain at least 1 entry.
*
* <p>The {@code smithy.api#noAuth} scheme, if present, is always the last scheme.
*
* @param service Service the operation is within.
* @param operation Operation to get the effective authentication schemes of.
* @param authMode AuthMode to determine which authentication schemes to include.
* @return Returns a map of the trait shape ID to the auth trait itself.
*/
public Map<ShapeId, Trait> getNoAuthAwareEffectiveAuthSchemes(ToShapeId service, ToShapeId operation) {
LinkedHashMap<ShapeId, Trait> authSchemes = new LinkedHashMap<>(getEffectiveAuthSchemes(service, operation));
if (authSchemes.isEmpty() || hasOptionalAuth(operation)) {
authSchemes.put(NoAuthTrait.ID, new NoAuthTrait());
public Map<ShapeId, Trait> getEffectiveAuthSchemes(ToShapeId service, ToShapeId operation, AuthMode authMode) {
Map<ShapeId, Trait> authSchemes = getEffectiveAuthSchemes(service, operation);
if (authMode == AuthMode.NO_AUTH_AWARE) {
if (authSchemes.isEmpty() || hasOptionalAuth(operation)) {
authSchemes = new LinkedHashMap<>(authSchemes);
authSchemes.put(NoAuthTrait.ID, new NoAuthTrait());
}
}
return authSchemes;
}
Expand All @@ -235,7 +281,7 @@ private boolean hasOptionalAuth(ToShapeId operation) {
.isPresent();
}

private Map<ShapeId, Trait> getAuthTraitValues(Shape service, Shape subject) {
private static Map<ShapeId, Trait> getAuthTraitValues(Shape service, Shape subject) {
if (!subject.hasTrait(AuthTrait.class)) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@
import software.amazon.smithy.model.knowledge.ServiceIndex;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.shapes.ToShapeId;
import software.amazon.smithy.model.traits.AnnotationTrait;

/**
* An auth scheme trait for {@code smithy.api#noAuth} which indicates no authentication. This is not a real trait
* in the semantic model, but a valid auth scheme for use in
* {@link ServiceIndex#getNoAuthAwareEffectiveAuthSchemes(ToShapeId, ToShapeId)}.
* in the semantic model, but a valid auth scheme for use in {@link ServiceIndex#getEffectiveAuthSchemes}.
*/
public final class NoAuthTrait extends AnnotationTrait {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.hasSize;
import static software.amazon.smithy.model.knowledge.ServiceIndex.AuthMode.NO_AUTH_AWARE;

import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -79,25 +80,49 @@ public void authSchemesOfService() {
@Test
public void authSchemesOfServiceWithoutAuthTrait() {
ServiceIndex serviceIndex = ServiceIndex.of(model);
Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(
ShapeId.from("smithy.example#ServiceWithoutAuthTrait"));
ShapeId service = ShapeId.from("smithy.example#ServiceWithoutAuthTrait");

Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(service);
assertAuthSchemes(auth, HttpBasicAuthTrait.ID, HttpBearerAuthTrait.ID, HttpDigestAuthTrait.ID, CUSTOM_AUTH_ID);

auth = serviceIndex.getEffectiveAuthSchemes(service, NO_AUTH_AWARE);
assertAuthSchemes(auth, HttpBasicAuthTrait.ID, HttpBearerAuthTrait.ID, HttpDigestAuthTrait.ID, CUSTOM_AUTH_ID);
}

@Test
public void authSchemesOfServiceWithAuthTrait() {
ServiceIndex serviceIndex = ServiceIndex.of(model);
Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(
ShapeId.from("smithy.example#ServiceWithAuthTrait"));
ShapeId service = ShapeId.from("smithy.example#ServiceWithAuthTrait");

Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(service);
assertAuthSchemes(auth, HttpBasicAuthTrait.ID, HttpDigestAuthTrait.ID);

auth = serviceIndex.getEffectiveAuthSchemes(service, NO_AUTH_AWARE);
assertAuthSchemes(auth, HttpBasicAuthTrait.ID, HttpDigestAuthTrait.ID);
}

@Test
public void authSchemesOfServiceWithEmptyAuthTrait() {
ServiceIndex serviceIndex = ServiceIndex.of(model);
Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(
ShapeId.from("smithy.example#ServiceWithEmptyAuthTrait"));
ShapeId service = ShapeId.from("smithy.example#ServiceWithEmptyAuthTrait");

Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(service);
assertAuthSchemes(auth);

auth = serviceIndex.getEffectiveAuthSchemes(service, NO_AUTH_AWARE);
assertAuthSchemes(auth, NoAuthTrait.ID);
}

@Test
public void authSchemesOfServiceWithoutAuthDefinitionTraits() {
ServiceIndex serviceIndex = ServiceIndex.of(model);
ShapeId service = ShapeId.from("smithy.example#ServiceWithoutAuthDefinitionTraits");

Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(service);
assertAuthSchemes(auth);

auth = serviceIndex.getEffectiveAuthSchemes(service, NO_AUTH_AWARE);
assertAuthSchemes(auth, NoAuthTrait.ID);
}

@Test
Expand All @@ -109,7 +134,7 @@ public void authSchemesOfOperationWithoutAuthTraitAndServiceWithoutAuthTrait() {
Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(service, operation);
assertAuthSchemes(auth, HttpBasicAuthTrait.ID, HttpBearerAuthTrait.ID, HttpDigestAuthTrait.ID, CUSTOM_AUTH_ID);

auth = serviceIndex.getNoAuthAwareEffectiveAuthSchemes(service, operation);
auth = serviceIndex.getEffectiveAuthSchemes(service, operation, NO_AUTH_AWARE);
assertAuthSchemes(auth, HttpBasicAuthTrait.ID, HttpBearerAuthTrait.ID, HttpDigestAuthTrait.ID, CUSTOM_AUTH_ID);
}

Expand All @@ -122,7 +147,7 @@ public void authSchemesOfOperationWithoutAuthTraitAndServiceWithAuthTrait() {
Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(service, operation);
assertAuthSchemes(auth, HttpBasicAuthTrait.ID, HttpDigestAuthTrait.ID);

auth = serviceIndex.getNoAuthAwareEffectiveAuthSchemes(service, operation);
auth = serviceIndex.getEffectiveAuthSchemes(service, operation, NO_AUTH_AWARE);
assertAuthSchemes(auth, HttpBasicAuthTrait.ID, HttpDigestAuthTrait.ID);
}

Expand All @@ -135,7 +160,7 @@ public void authSchemesOfOperationWithoutAuthTraitAndServiceWithEmptyAuthTrait()
Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(service, operation);
assertAuthSchemes(auth);

auth = serviceIndex.getNoAuthAwareEffectiveAuthSchemes(service, operation);
auth = serviceIndex.getEffectiveAuthSchemes(service, operation, NO_AUTH_AWARE);
assertAuthSchemes(auth, NoAuthTrait.ID);
}

Expand All @@ -157,7 +182,7 @@ public void authSchemesOfOperationWithEmptyAuthTrait() {
Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(service, operation);
assertAuthSchemes(auth);

auth = serviceIndex.getNoAuthAwareEffectiveAuthSchemes(service, operation);
auth = serviceIndex.getEffectiveAuthSchemes(service, operation, NO_AUTH_AWARE);
assertAuthSchemes(auth, NoAuthTrait.ID);
}

Expand All @@ -170,7 +195,7 @@ public void authSchemesOfOperationWithOptionalAuthTrait() {
Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(service, operation);
assertAuthSchemes(auth, HttpBasicAuthTrait.ID, HttpDigestAuthTrait.ID);

auth = serviceIndex.getNoAuthAwareEffectiveAuthSchemes(service, operation);
auth = serviceIndex.getEffectiveAuthSchemes(service, operation, NO_AUTH_AWARE);
assertAuthSchemes(auth, HttpBasicAuthTrait.ID, HttpDigestAuthTrait.ID, NoAuthTrait.ID);
}

Expand All @@ -186,7 +211,7 @@ public void authSchemesOfOperationWithOptionalAuthTraitAndServiceWithoutAuthTrai
Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(service, operation);
assertAuthSchemes(auth, HttpBasicAuthTrait.ID, HttpBearerAuthTrait.ID, HttpDigestAuthTrait.ID, CUSTOM_AUTH_ID);

auth = serviceIndex.getNoAuthAwareEffectiveAuthSchemes(service, operation);
auth = serviceIndex.getEffectiveAuthSchemes(service, operation, NO_AUTH_AWARE);
assertAuthSchemes(auth, HttpBasicAuthTrait.ID, HttpBearerAuthTrait.ID, HttpDigestAuthTrait.ID, CUSTOM_AUTH_ID,
NoAuthTrait.ID);
}
Expand All @@ -198,6 +223,9 @@ public void authSchemesOfInvalidService() {

Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(service);
assertAuthSchemes(auth);

auth = serviceIndex.getEffectiveAuthSchemes(service, NO_AUTH_AWARE);
assertAuthSchemes(auth, NoAuthTrait.ID);
}

@Test
Expand All @@ -209,7 +237,7 @@ public void authSchemesOfInvalidServiceWithInvalidOperation() {
Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(service, operation);
assertAuthSchemes(auth);

auth = serviceIndex.getNoAuthAwareEffectiveAuthSchemes(service, operation);
auth = serviceIndex.getEffectiveAuthSchemes(service, operation, NO_AUTH_AWARE);
assertAuthSchemes(auth, NoAuthTrait.ID);
}

Expand All @@ -222,7 +250,7 @@ public void authSchemesOfServiceWithInvalidOperation() {
Map<ShapeId, Trait> auth = serviceIndex.getEffectiveAuthSchemes(service, operation);
assertAuthSchemes(auth);

auth = serviceIndex.getNoAuthAwareEffectiveAuthSchemes(service, operation);
auth = serviceIndex.getEffectiveAuthSchemes(service, operation, NO_AUTH_AWARE);
assertAuthSchemes(auth, NoAuthTrait.ID);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ service ServiceWithEmptyAuthTrait {
]
}

service ServiceWithoutAuthDefinitionTraits {
version: "2020-01-29",
operations: [
OperationWithoutAuthTrait,
OperationWithEmptyAuthTrait,
OperationWithOptionalAuthTrait
]
}

operation OperationWithoutAuthTrait {}

@auth([httpDigestAuth])
Expand Down

0 comments on commit 5cb8e80

Please sign in to comment.