diff --git a/aws-qbusiness-application/aws-qbusiness-application.json b/aws-qbusiness-application/aws-qbusiness-application.json
index 58b8536..97fcb15 100755
--- a/aws-qbusiness-application/aws-qbusiness-application.json
+++ b/aws-qbusiness-application/aws-qbusiness-application.json
@@ -31,6 +31,28 @@
"DISABLED"
]
},
+ "AutoSubscriptionConfiguration": {
+ "type": "object",
+ "properties": {
+ "AutoSubscribe": {
+ "$ref": "#/definitions/AutoSubscriptionStatus"
+ },
+ "DefaultSubscriptionType": {
+ "$ref": "#/definitions/SubscriptionType"
+ }
+ },
+ "required": [
+ "AutoSubscribe"
+ ],
+ "additionalProperties": false
+ },
+ "AutoSubscriptionStatus": {
+ "type": "string",
+ "enum": [
+ "ENABLED",
+ "DISABLED"
+ ]
+ },
"EncryptionConfiguration": {
"type": "object",
"properties": {
@@ -42,6 +64,14 @@
},
"additionalProperties": false
},
+ "IdentityType": {
+ "type": "string",
+ "enum": [
+ "AWS_IAM_IDP_SAML",
+ "AWS_IAM_IDP_OIDC",
+ "AWS_IAM_IDC"
+ ]
+ },
"QAppsConfiguration": {
"type": "object",
"properties": {
@@ -61,6 +91,32 @@
"DISABLED"
]
},
+ "SubscriptionType": {
+ "type": "string",
+ "enum": [
+ "Q_LITE",
+ "Q_BUSINESS"
+ ]
+ },
+ "PersonalizationConfiguration": {
+ "type": "object",
+ "properties": {
+ "PersonalizationControlMode": {
+ "$ref": "#/definitions/PersonalizationControlMode"
+ }
+ },
+ "required": [
+ "PersonalizationControlMode"
+ ],
+ "additionalProperties": false
+ },
+ "PersonalizationControlMode": {
+ "type": "string",
+ "enum": [
+ "ENABLED",
+ "DISABLED"
+ ]
+ },
"Tag": {
"type": "object",
"properties": {
@@ -98,6 +154,19 @@
"AttachmentsConfiguration": {
"$ref": "#/definitions/AttachmentsConfiguration"
},
+ "AutoSubscriptionConfiguration": {
+ "$ref": "#/definitions/AutoSubscriptionConfiguration"
+ },
+ "ClientIdsForOIDC": {
+ "type": "array",
+ "insertionOrder": false,
+ "items": {
+ "type": "string",
+ "maxLength": 255,
+ "minLength": 1,
+ "pattern": "^[a-zA-Z0-9_.:/()*?=-]*$"
+ }
+ },
"CreatedAt": {
"type": "string",
"format": "date-time"
@@ -117,6 +186,12 @@
"EncryptionConfiguration": {
"$ref": "#/definitions/EncryptionConfiguration"
},
+ "IamIdentityProviderArn": {
+ "type": "string",
+ "maxLength": 2048,
+ "minLength": 20,
+ "pattern": "^arn:aws:iam::\\d{12}:(oidc-provider|saml-provider)/[a-zA-Z0-9_\\.\\/@\\-]+$"
+ },
"IdentityCenterApplicationArn": {
"type": "string",
"maxLength": 1224,
@@ -126,12 +201,18 @@
"QAppsConfiguration": {
"$ref": "#/definitions/QAppsConfiguration"
},
+ "PersonalizationConfiguration": {
+ "$ref": "#/definitions/PersonalizationConfiguration"
+ },
"IdentityCenterInstanceArn": {
"type": "string",
"maxLength": 1224,
"minLength": 10,
"pattern": "^arn:(aws|aws-us-gov|aws-cn|aws-iso|aws-iso-b):sso:::instance/(sso)?ins-[a-zA-Z0-9-.]{16}$"
},
+ "IdentityType": {
+ "$ref": "#/definitions/IdentityType"
+ },
"RoleArn": {
"type": "string",
"maxLength": 1284,
@@ -170,7 +251,10 @@
"/properties/IdentityCenterInstanceArn"
],
"createOnlyProperties": [
- "/properties/EncryptionConfiguration"
+ "/properties/ClientIdsForOIDC",
+ "/properties/EncryptionConfiguration",
+ "/properties/IamIdentityProviderArn",
+ "/properties/IdentityType"
],
"primaryIdentifier": [
"/properties/ApplicationId"
@@ -178,15 +262,18 @@
"handlers": {
"create": {
"permissions": [
+ "iam:GetSAMLProvider",
"iam:PassRole",
"kms:CreateGrant",
"kms:DescribeKey",
"qbusiness:CreateApplication",
"qbusiness:GetApplication",
+ "qbusiness:UpdateApplication",
"qbusiness:ListTagsForResource",
"qbusiness:TagResource",
"sso:CreateApplication",
"sso:DeleteApplication",
+ "sso:DescribeInstance",
"sso:PutApplicationAccessScope",
"sso:PutApplicationAuthenticationMethod",
"sso:PutApplicationGrant"
@@ -208,6 +295,7 @@
"qbusiness:UpdateApplication",
"sso:CreateApplication",
"sso:DeleteApplication",
+ "sso:DescribeInstance",
"sso:PutApplicationAccessScope",
"sso:PutApplicationAuthenticationMethod",
"sso:PutApplicationGrant"
diff --git a/aws-qbusiness-application/pom.xml b/aws-qbusiness-application/pom.xml
index 1152ce9..454691d 100644
--- a/aws-qbusiness-application/pom.xml
+++ b/aws-qbusiness-application/pom.xml
@@ -55,7 +55,7 @@
software.amazon.awssdk
qbusiness
- 2.26.14
+ 2.27.17
diff --git a/aws-qbusiness-application/resource-role.yaml b/aws-qbusiness-application/resource-role.yaml
index 4a3e59f..9068825 100644
--- a/aws-qbusiness-application/resource-role.yaml
+++ b/aws-qbusiness-application/resource-role.yaml
@@ -30,6 +30,7 @@ Resources:
Statement:
- Effect: Allow
Action:
+ - "iam:GetSAMLProvider"
- "iam:PassRole"
- "kms:CreateGrant"
- "kms:DescribeKey"
@@ -44,6 +45,7 @@ Resources:
- "qbusiness:UpdateApplication"
- "sso:CreateApplication"
- "sso:DeleteApplication"
+ - "sso:DescribeInstance"
- "sso:PutApplicationAccessScope"
- "sso:PutApplicationAuthenticationMethod"
- "sso:PutApplicationGrant"
diff --git a/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/BaseHandlerStd.java b/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/BaseHandlerStd.java
index 1c1c9a9..0bd5226 100644
--- a/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/BaseHandlerStd.java
+++ b/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/BaseHandlerStd.java
@@ -90,7 +90,7 @@ protected ProgressEvent handleError(
if (error instanceof ResourceNotFoundException) {
cfnException = new CfnNotFoundException(ResourceModel.TYPE_NAME, primaryIdentifier, error);
- } else if (error instanceof ValidationException) {
+ } else if (error instanceof ValidationException || error instanceof CfnInvalidRequestException) {
cfnException = new CfnInvalidRequestException(error);
} else if (error instanceof ThrottlingException) {
cfnException = new CfnThrottlingException(apiName, error);
diff --git a/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/Constants.java b/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/Constants.java
index cc9f69c..7590b15 100644
--- a/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/Constants.java
+++ b/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/Constants.java
@@ -11,6 +11,9 @@ public final class Constants {
public static final String SERVICE_NAME_LOWER = SERVICE_NAME.toLowerCase(Locale.ENGLISH);
public static final String ENV_AWS_REGION = "AWS_REGION";
+ public static final String AUTOSUBSCRIBE_FIELD_VALIDATION_ERROR =
+ "AutoSubscriptionConfiguration must be ENABLED with a default subscription tier for %s identity type.";
+
private Constants() {
}
}
diff --git a/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/CreateHandler.java b/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/CreateHandler.java
index bd91620..a1b5b79 100644
--- a/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/CreateHandler.java
+++ b/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/CreateHandler.java
@@ -1,16 +1,24 @@
package software.amazon.qbusiness.application;
import static software.amazon.qbusiness.application.Constants.API_CREATE_APPLICATION;
+import static software.amazon.qbusiness.application.Constants.API_UPDATE_APPLICATION;
+import static software.amazon.qbusiness.application.Constants.AUTOSUBSCRIBE_FIELD_VALIDATION_ERROR;
import java.time.Duration;
import java.util.Objects;
import software.amazon.awssdk.services.qbusiness.QBusinessClient;
import software.amazon.awssdk.services.qbusiness.model.ApplicationStatus;
+import software.amazon.awssdk.services.qbusiness.model.AutoSubscriptionStatus;
import software.amazon.awssdk.services.qbusiness.model.CreateApplicationRequest;
import software.amazon.awssdk.services.qbusiness.model.CreateApplicationResponse;
import software.amazon.awssdk.services.qbusiness.model.GetApplicationResponse;
+import software.amazon.awssdk.services.qbusiness.model.IdentityType;
+import software.amazon.awssdk.services.qbusiness.model.SubscriptionType;
+import software.amazon.awssdk.services.qbusiness.model.UpdateApplicationRequest;
+import software.amazon.awssdk.services.qbusiness.model.UpdateApplicationResponse;
import software.amazon.awssdk.utils.StringUtils;
+import software.amazon.cloudformation.exceptions.CfnInvalidRequestException;
import software.amazon.cloudformation.exceptions.CfnNotStabilizedException;
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
import software.amazon.cloudformation.proxy.Logger;
@@ -58,11 +66,41 @@ protected ProgressEvent handleRequest(
.handleError((createReq, error, client, model, context) ->
handleError(createReq, model, error, context, logger, API_CREATE_APPLICATION))
.progress()
+ ).then(progress -> {
+ if (!isIAMFederatedApp(IdentityType.fromValue(request.getDesiredResourceState().getIdentityType()))) {
+ return progress;
+ }
+ // Immediately update the application to add auto-subscribe configuration to it.
+ // TODO: Remove after AutoSubscribeConfiguration is added to the CreateApplication API.
+ return proxy.initiate("AWS-QBusiness-Application::Update", proxyClient, progress.getResourceModel(), progress.getCallbackContext())
+ .translateToServiceRequest(model -> Translator.translateToPostCreateUpdateRequest(model))
+ .backoffDelay(backOffStrategy)
+ .makeServiceCall((awsRequest, clientProxyClient) -> callUpdateApplication(awsRequest, clientProxyClient))
+ .stabilize((awsReq, response, clientProxyClient, model, context) -> isStabilized(clientProxyClient, model, logger))
+ .handleError((updateReq, error, client, model, context) ->
+ handleError(updateReq, model, error, context, logger, API_UPDATE_APPLICATION))
+ .progress();
+ }
).then(progress ->
new ReadHandler().handleRequest(proxy, request, callbackContext, proxyClient, logger)
);
}
+ private void validateAutoSubscriptionConfiguration(ResourceModel desiredResourceState) {
+ if (isIAMFederatedApp(IdentityType.fromValue(desiredResourceState.getIdentityType()))) {
+ AutoSubscriptionConfiguration config = desiredResourceState.getAutoSubscriptionConfiguration();
+ if (config != null && AutoSubscriptionStatus.ENABLED.toString().equals(config.getAutoSubscribe()) &&
+ config.getDefaultSubscriptionType()!= null) {
+ return;
+ }
+ throw new CfnInvalidRequestException(String.format(AUTOSUBSCRIBE_FIELD_VALIDATION_ERROR, desiredResourceState.getIdentityType()));
+ }
+ }
+
+ private boolean isIAMFederatedApp(IdentityType identityType) {
+ return IdentityType.AWS_IAM_IDP_OIDC.equals(identityType) || IdentityType.AWS_IAM_IDP_SAML.equals(identityType);
+ }
+
private boolean isStabilized(
ProxyClient proxyClient,
ResourceModel model,
@@ -93,11 +131,18 @@ private boolean isStabilized(
}
private CreateApplicationResponse callCreateApplication(CreateApplicationRequest request,
- ProxyClient proxyClient,
- ResourceModel model) {
+ ProxyClient proxyClient,
+ ResourceModel model) {
+ validateAutoSubscriptionConfiguration(model);
var client = proxyClient.client();
CreateApplicationResponse response = proxyClient.injectCredentialsAndInvokeV2(request, client::createApplication);
model.setApplicationId(response.applicationId());
return response;
}
+
+ private UpdateApplicationResponse callUpdateApplication(UpdateApplicationRequest request,
+ ProxyClient proxyClient) {
+ var client = proxyClient.client();
+ return proxyClient.injectCredentialsAndInvokeV2(request, client::updateApplication);
+ }
}
diff --git a/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/Translator.java b/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/Translator.java
index 3887c81..385ddcb 100644
--- a/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/Translator.java
+++ b/aws-qbusiness-application/src/main/java/software/amazon/qbusiness/application/Translator.java
@@ -6,6 +6,8 @@
import java.util.Optional;
import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+
import software.amazon.awssdk.services.qbusiness.model.CreateApplicationRequest;
import software.amazon.awssdk.services.qbusiness.model.DeleteApplicationRequest;
import software.amazon.awssdk.services.qbusiness.model.GetApplicationRequest;
@@ -41,12 +43,16 @@ static CreateApplicationRequest translateToCreateRequest(final String idempotent
.clientToken(idempotentToken)
.displayName(model.getDisplayName())
.roleArn(model.getRoleArn())
+ .identityType(model.getIdentityType())
+ .iamIdentityProviderArn(model.getIamIdentityProviderArn())
+ .clientIdsForOIDC(model.getClientIdsForOIDC())
.identityCenterInstanceArn(model.getIdentityCenterInstanceArn())
.description(model.getDescription())
.encryptionConfiguration(toServiceEncryptionConfig(model.getEncryptionConfiguration()))
.attachmentsConfiguration(toServiceAttachmentConfiguration(model.getAttachmentsConfiguration()))
.tags(TagHelper.serviceTagsFromCfnTags(model.getTags()))
.qAppsConfiguration(toServiceQAppsConfiguration(model.getQAppsConfiguration()))
+ .personalizationConfiguration(toServicePersonalizationConfiguration(model.getPersonalizationConfiguration()))
.build();
}
@@ -77,11 +83,14 @@ static ListTagsForResourceRequest translateToListTagsRequest(final ResourceHandl
* @return model resource model
*/
static ResourceModel translateFromReadResponse(final GetApplicationResponse awsResponse) {
- return ResourceModel.builder()
+ var response = ResourceModel.builder()
.displayName(awsResponse.displayName())
.applicationId(awsResponse.applicationId())
.applicationArn(awsResponse.applicationArn())
.roleArn(awsResponse.roleArn())
+ .identityType(awsResponse.identityTypeAsString())
+ .iamIdentityProviderArn(awsResponse.iamIdentityProviderArn())
+ .clientIdsForOIDC(awsResponse.clientIdsForOIDC())
.identityCenterApplicationArn(awsResponse.identityCenterApplicationArn())
.status(awsResponse.statusAsString())
.description(awsResponse.description())
@@ -90,7 +99,16 @@ static ResourceModel translateFromReadResponse(final GetApplicationResponse awsR
.encryptionConfiguration(fromServiceEncryptionConfig(awsResponse.encryptionConfiguration()))
.attachmentsConfiguration(fromServiceAttachmentConfiguration(awsResponse.attachmentsConfiguration()))
.qAppsConfiguration(fromServiceQAppsConfiguration(awsResponse.qAppsConfiguration()))
+ .personalizationConfiguration(fromServicePersonalizationConfiguration(awsResponse.personalizationConfiguration()))
+ .autoSubscriptionConfiguration(fromServiceAutoSubscriptionConfiguration(awsResponse.autoSubscriptionConfiguration()))
.build();
+ // TODO: Workaround. This is a readonly field. But it is only returned if customer is using IDC
+ // When that's not the case, let's fill it in with N/A
+ // Contract test require readonly fields are returned.
+ if (StringUtils.isEmpty(response.getIdentityCenterApplicationArn())) {
+ response.setIdentityCenterApplicationArn("N/A");
+ }
+ return response;
}
static String instantToString(Instant instant) {
@@ -171,6 +189,60 @@ static software.amazon.awssdk.services.qbusiness.model.QAppsConfiguration toServ
.build();
}
+ static PersonalizationConfiguration fromServicePersonalizationConfiguration(
+ software.amazon.awssdk.services.qbusiness.model.PersonalizationConfiguration serviceConfig
+ ) {
+ if (serviceConfig == null) {
+ return null;
+ }
+
+ return PersonalizationConfiguration.builder()
+ .personalizationControlMode(serviceConfig.personalizationControlModeAsString())
+ .build();
+ }
+
+ static software.amazon.awssdk.services.qbusiness.model.PersonalizationConfiguration toServicePersonalizationConfiguration(
+ PersonalizationConfiguration modelConfig
+ ) {
+ if (modelConfig == null) {
+ return null;
+ }
+
+ return software.amazon.awssdk.services.qbusiness.model.PersonalizationConfiguration.builder()
+ .personalizationControlMode(modelConfig.getPersonalizationControlMode())
+ .build();
+ }
+
+ static AutoSubscriptionConfiguration fromServiceAutoSubscriptionConfiguration(
+ software.amazon.awssdk.services.qbusiness.model.AutoSubscriptionConfiguration serviceConfig
+ ) {
+ if (serviceConfig == null) {
+ return null;
+ }
+
+ if (serviceConfig.autoSubscribe() == null && serviceConfig.defaultSubscriptionType() == null) {
+ return null;
+ }
+
+ return AutoSubscriptionConfiguration.builder()
+ .autoSubscribe(serviceConfig.autoSubscribeAsString())
+ .defaultSubscriptionType(serviceConfig.defaultSubscriptionTypeAsString())
+ .build();
+ }
+
+ static software.amazon.awssdk.services.qbusiness.model.AutoSubscriptionConfiguration toServiceAutoSubscriptionConfiguration(
+ AutoSubscriptionConfiguration modelConfig
+ ) {
+ if (modelConfig == null) {
+ return null;
+ }
+
+ return software.amazon.awssdk.services.qbusiness.model.AutoSubscriptionConfiguration.builder()
+ .autoSubscribe(modelConfig.getAutoSubscribe())
+ .defaultSubscriptionType(modelConfig.getDefaultSubscriptionType())
+ .build();
+ }
+
static ResourceModel translateFromReadResponseWithTags(final ListTagsForResourceResponse listTagsResponse, final ResourceModel model) {
if (listTagsResponse == null || !listTagsResponse.hasTags()) {
return model;
@@ -208,9 +280,18 @@ static UpdateApplicationRequest translateToUpdateRequest(final ResourceModel mod
.identityCenterInstanceArn(model.getIdentityCenterInstanceArn())
.attachmentsConfiguration(toServiceAttachmentConfiguration(model.getAttachmentsConfiguration()))
.qAppsConfiguration(toServiceQAppsConfiguration(model.getQAppsConfiguration()))
+ .personalizationConfiguration(toServicePersonalizationConfiguration(model.getPersonalizationConfiguration()))
+ .autoSubscriptionConfiguration(toServiceAutoSubscriptionConfiguration(model.getAutoSubscriptionConfiguration()))
.build();
}
+ static UpdateApplicationRequest translateToPostCreateUpdateRequest(final ResourceModel model) {
+ return UpdateApplicationRequest.builder()
+ .applicationId(model.getApplicationId())
+ .autoSubscriptionConfiguration(toServiceAutoSubscriptionConfiguration(model.getAutoSubscriptionConfiguration()))
+ .build();
+ }
+
/**
* Request to list resources
*
@@ -238,6 +319,7 @@ static List translateFromListResponse(final ListApplicationsRespo
.createdAt(instantToString(application.createdAt()))
.updatedAt(instantToString(application.updatedAt()))
.status(application.statusAsString())
+ .identityType(application.identityTypeAsString())
.build()
)
.toList();
diff --git a/aws-qbusiness-application/src/test/java/software/amazon/qbusiness/application/CreateHandlerTest.java b/aws-qbusiness-application/src/test/java/software/amazon/qbusiness/application/CreateHandlerTest.java
index 3a3e4a5..68523a5 100644
--- a/aws-qbusiness-application/src/test/java/software/amazon/qbusiness/application/CreateHandlerTest.java
+++ b/aws-qbusiness-application/src/test/java/software/amazon/qbusiness/application/CreateHandlerTest.java
@@ -12,6 +12,7 @@
import static org.mockito.Mockito.when;
import java.time.Duration;
+import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
@@ -42,6 +43,9 @@
import software.amazon.awssdk.services.qbusiness.model.ServiceQuotaExceededException;
import software.amazon.awssdk.services.qbusiness.model.ThrottlingException;
import software.amazon.awssdk.services.qbusiness.model.ValidationException;
+import software.amazon.awssdk.services.qbusiness.model.UpdateApplicationRequest;
+import software.amazon.awssdk.services.qbusiness.model.AutoSubscriptionStatus;
+import software.amazon.awssdk.services.qbusiness.model.SubscriptionType;
import software.amazon.cloudformation.exceptions.CfnNotStabilizedException;
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
import software.amazon.cloudformation.proxy.HandlerErrorCode;
@@ -91,6 +95,13 @@ public void setup() {
.description("A Description")
.roleArn("such role, very arn")
.identityCenterInstanceArn("arn:aws:sso:::instance/ssoins")
+ .identityType("AWS_IAM_IDP_OIDC")
+ .autoSubscriptionConfiguration(AutoSubscriptionConfiguration.builder()
+ .autoSubscribe(AutoSubscriptionStatus.ENABLED.toString())
+ .defaultSubscriptionType(SubscriptionType.Q_BUSINESS.toString())
+ .build())
+ .iamIdentityProviderArn("arn:aws:iam::123456:oidc-provider/trial-123456.okta.com")
+ .clientIdsForOIDC(List.of("0oaglq4vdnaWau7hW697"))
.build();
testRequest = ResourceHandlerRequest.builder()
@@ -145,9 +156,12 @@ proxy, testRequest, new CallbackContext(), proxyClient, logger
assertThat(model.getStatus()).isEqualTo(ApplicationStatus.ACTIVE.toString());
verify(sdkClient).createApplication(any(CreateApplicationRequest.class));
- verify(sdkClient, times(2)).getApplication(
+ verify(sdkClient, times(3)).getApplication(
argThat((ArgumentMatcher) t -> t.applicationId().equals(APP_ID))
);
+ verify(sdkClient, times(1)).updateApplication(
+ argThat((ArgumentMatcher) t -> t.applicationId().equals(APP_ID))
+ );
verify(sdkClient).listTagsForResource(any(ListTagsForResourceRequest.class));
}
@@ -180,9 +194,12 @@ proxy, testRequest, new CallbackContext(), proxyClient, logger
assertThat(resultProgress).isNotNull();
assertThat(resultProgress.isSuccess()).isTrue();
verify(sdkClient).createApplication(any(CreateApplicationRequest.class));
- verify(sdkClient, times(3)).getApplication(
+ verify(sdkClient, times(4)).getApplication(
argThat((ArgumentMatcher) t -> t.applicationId().equals(APP_ID))
);
+ verify(sdkClient, times(1)).updateApplication(
+ argThat((ArgumentMatcher) t -> t.applicationId().equals(APP_ID))
+ );
verify(sdkClient).listTagsForResource(any(ListTagsForResourceRequest.class));
}
diff --git a/aws-qbusiness-application/src/test/java/software/amazon/qbusiness/application/ReadHandlerTest.java b/aws-qbusiness-application/src/test/java/software/amazon/qbusiness/application/ReadHandlerTest.java
index bdf09bc..3248447 100644
--- a/aws-qbusiness-application/src/test/java/software/amazon/qbusiness/application/ReadHandlerTest.java
+++ b/aws-qbusiness-application/src/test/java/software/amazon/qbusiness/application/ReadHandlerTest.java
@@ -28,6 +28,9 @@
import software.amazon.awssdk.services.qbusiness.model.ApplicationStatus;
import software.amazon.awssdk.services.qbusiness.model.AppliedAttachmentsConfiguration;
import software.amazon.awssdk.services.qbusiness.model.AttachmentsControlMode;
+import software.amazon.awssdk.services.qbusiness.model.AutoSubscriptionConfiguration;
+import software.amazon.awssdk.services.qbusiness.model.AutoSubscriptionStatus;
+import software.amazon.awssdk.services.qbusiness.model.SubscriptionType;
import software.amazon.awssdk.services.qbusiness.model.EncryptionConfiguration;
import software.amazon.awssdk.services.qbusiness.model.QBusinessException;
import software.amazon.awssdk.services.qbusiness.model.GetApplicationRequest;
@@ -107,6 +110,9 @@ public void handleRequest_SimpleSuccess() {
.description("this is a description, there are many like it but this one is mine.")
.displayName("Foobar")
.identityCenterApplicationArn("arn:aws:sso::123456789012:application/ssoins/apl")
+ .identityType("AWS_IAM_IDP_OIDC")
+ .iamIdentityProviderArn("arn:aws:iam::123456:oidc-provider/trial-123456.okta.com")
+ .clientIdsForOIDC(List.of("0oaglq4vdnaWau7hW697"))
.status(ApplicationStatus.ACTIVE)
.encryptionConfiguration(EncryptionConfiguration.builder()
.kmsKeyId("keyblade")
@@ -114,6 +120,10 @@ public void handleRequest_SimpleSuccess() {
.attachmentsConfiguration(AppliedAttachmentsConfiguration.builder()
.attachmentsControlMode(AttachmentsControlMode.ENABLED)
.build())
+ .autoSubscriptionConfiguration(AutoSubscriptionConfiguration.builder()
+ .autoSubscribe(AutoSubscriptionStatus.ENABLED.toString())
+ .defaultSubscriptionType(SubscriptionType.Q_BUSINESS.toString())
+ .build())
.build());
when(proxyClient.client().listTagsForResource(any(ListTagsForResourceRequest.class)))
.thenReturn(ListTagsForResourceResponse.builder()
@@ -149,6 +159,9 @@ proxy, testRequest, new CallbackContext(), proxyClient, logger
assertThat(resultModel.getStatus()).isEqualTo(ApplicationStatus.ACTIVE.toString());
assertThat(resultModel.getEncryptionConfiguration().getKmsKeyId()).isEqualTo("keyblade");
assertThat(resultModel.getAttachmentsConfiguration().getAttachmentsControlMode()).isEqualTo(AttachmentsControlMode.ENABLED.toString());
+ assertThat(resultModel.getAutoSubscriptionConfiguration().getAutoSubscribe()).isEqualTo(AutoSubscriptionStatus.ENABLED.toString());
+ assertThat(resultModel.getAutoSubscriptionConfiguration().getDefaultSubscriptionType()).isEqualTo(SubscriptionType.Q_BUSINESS.toString());
+
var tags = resultModel.getTags().stream().map(tag -> Map.entry(tag.getKey(), tag.getValue())).toList();
assertThat(tags).isEqualTo(List.of(
diff --git a/aws-qbusiness-datasource/pom.xml b/aws-qbusiness-datasource/pom.xml
index 914334e..25d88f8 100644
--- a/aws-qbusiness-datasource/pom.xml
+++ b/aws-qbusiness-datasource/pom.xml
@@ -30,7 +30,7 @@
software.amazon.awssdk
qbusiness
- 2.26.14
+ 2.27.17
diff --git a/aws-qbusiness-index/pom.xml b/aws-qbusiness-index/pom.xml
index dc9a38d..ade4958 100644
--- a/aws-qbusiness-index/pom.xml
+++ b/aws-qbusiness-index/pom.xml
@@ -37,7 +37,7 @@
software.amazon.awssdk
qbusiness
- 2.26.14
+ 2.27.17
diff --git a/aws-qbusiness-plugin/pom.xml b/aws-qbusiness-plugin/pom.xml
index 29e83d6..1e74c63 100644
--- a/aws-qbusiness-plugin/pom.xml
+++ b/aws-qbusiness-plugin/pom.xml
@@ -43,7 +43,7 @@
software.amazon.awssdk
qbusiness
- 2.26.14
+ 2.27.17
diff --git a/aws-qbusiness-retriever/pom.xml b/aws-qbusiness-retriever/pom.xml
index f659ef7..3557d54 100644
--- a/aws-qbusiness-retriever/pom.xml
+++ b/aws-qbusiness-retriever/pom.xml
@@ -37,7 +37,7 @@
software.amazon.awssdk
qbusiness
- 2.26.14
+ 2.27.17
diff --git a/aws-qbusiness-webexperience/aws-qbusiness-webexperience.json b/aws-qbusiness-webexperience/aws-qbusiness-webexperience.json
index 7a5d244..91d00ae 100755
--- a/aws-qbusiness-webexperience/aws-qbusiness-webexperience.json
+++ b/aws-qbusiness-webexperience/aws-qbusiness-webexperience.json
@@ -2,6 +2,73 @@
"typeName": "AWS::QBusiness::WebExperience",
"description": "Definition of AWS::QBusiness::WebExperience Resource Type",
"definitions": {
+ "IdentityProviderConfiguration": {
+ "oneOf": [
+ {
+ "type": "object",
+ "title": "SamlConfiguration",
+ "properties": {
+ "SamlConfiguration": {
+ "$ref": "#/definitions/SamlProviderConfiguration"
+ }
+ },
+ "required": [
+ "SamlConfiguration"
+ ],
+ "additionalProperties": false
+ },
+ {
+ "type": "object",
+ "title": "OpenIDConnectConfiguration",
+ "properties": {
+ "OpenIDConnectConfiguration": {
+ "$ref": "#/definitions/OpenIDConnectProviderConfiguration"
+ }
+ },
+ "required": [
+ "OpenIDConnectConfiguration"
+ ],
+ "additionalProperties": false
+ }
+ ]
+ },
+ "OpenIDConnectProviderConfiguration": {
+ "type": "object",
+ "properties": {
+ "SecretsArn": {
+ "type": "string",
+ "maxLength": 1284,
+ "minLength": 0,
+ "pattern": "^arn:[a-z0-9-\\.]{1,63}:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[^/].{0,1023}$"
+ },
+ "SecretsRole": {
+ "type": "string",
+ "maxLength": 1284,
+ "minLength": 0,
+ "pattern": "^arn:[a-z0-9-\\.]{1,63}:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[^/].{0,1023}$"
+ }
+ },
+ "required": [
+ "SecretsArn",
+ "SecretsRole"
+ ],
+ "additionalProperties": false
+ },
+ "SamlProviderConfiguration": {
+ "type": "object",
+ "properties": {
+ "AuthenticationUrl": {
+ "type": "string",
+ "maxLength": 1284,
+ "minLength": 1,
+ "pattern": "^https://.*$"
+ }
+ },
+ "required": [
+ "AuthenticationUrl"
+ ],
+ "additionalProperties": false
+ },
"Tag": {
"type": "object",
"properties": {
@@ -57,6 +124,9 @@
"minLength": 1,
"pattern": "^(https?|ftp|file)://([^\\s]*)$"
},
+ "IdentityProviderConfiguration": {
+ "$ref": "#/definitions/IdentityProviderConfiguration"
+ },
"RoleArn": {
"type": "string",
"maxLength": 1284,
diff --git a/aws-qbusiness-webexperience/pom.xml b/aws-qbusiness-webexperience/pom.xml
index 51eef7e..dfea136 100644
--- a/aws-qbusiness-webexperience/pom.xml
+++ b/aws-qbusiness-webexperience/pom.xml
@@ -37,7 +37,7 @@
software.amazon.awssdk
qbusiness
- 2.26.14
+ 2.27.17
diff --git a/aws-qbusiness-webexperience/src/main/java/software/amazon/qbusiness/webexperience/Translator.java b/aws-qbusiness-webexperience/src/main/java/software/amazon/qbusiness/webexperience/Translator.java
index 200d8b1..eda5095 100644
--- a/aws-qbusiness-webexperience/src/main/java/software/amazon/qbusiness/webexperience/Translator.java
+++ b/aws-qbusiness-webexperience/src/main/java/software/amazon/qbusiness/webexperience/Translator.java
@@ -44,6 +44,7 @@ static CreateWebExperienceRequest translateToCreateRequest(final String idempote
.clientToken(idempotentToken)
.applicationId(model.getApplicationId())
.roleArn(model.getRoleArn())
+ .identityProviderConfiguration(toIdentityProviderConfiguration(model.getIdentityProviderConfiguration()))
.title(model.getTitle())
.subtitle(model.getSubtitle())
.welcomeMessage(model.getWelcomeMessage())
@@ -96,6 +97,7 @@ static ResourceModel translateFromReadResponse(final GetWebExperienceResponse aw
.welcomeMessage(awsResponse.welcomeMessage())
.samplePromptsControlMode(awsResponse.samplePromptsControlModeAsString())
.roleArn(awsResponse.roleArn())
+ .identityProviderConfiguration(fromIdentityProviderConfiguration(awsResponse.identityProviderConfiguration()))
.defaultEndpoint(awsResponse.defaultEndpoint())
.createdAt(instantToString(awsResponse.createdAt()))
.updatedAt(instantToString(awsResponse.updatedAt()))
@@ -145,9 +147,87 @@ static UpdateWebExperienceRequest translateToUpdateRequest(final ResourceModel m
.title(model.getTitle())
.subtitle(model.getSubtitle())
.roleArn(model.getRoleArn())
+ .identityProviderConfiguration(toIdentityProviderConfiguration(model.getIdentityProviderConfiguration()))
.build();
}
+ static IdentityProviderConfiguration fromIdentityProviderConfiguration(
+ software.amazon.awssdk.services.qbusiness.model.IdentityProviderConfiguration serviceConfig
+ ) {
+ if (serviceConfig == null) {
+ return null;
+ }
+
+ return IdentityProviderConfiguration.builder()
+ .openIDConnectConfiguration(fromOpenIDConnectProviderConfiguration(serviceConfig.openIDConnectConfiguration()))
+ .samlConfiguration(fromSamlProviderConfiguration(serviceConfig.samlConfiguration()))
+ .build();
+ }
+
+ static OpenIDConnectProviderConfiguration fromOpenIDConnectProviderConfiguration(
+ software.amazon.awssdk.services.qbusiness.model.OpenIDConnectProviderConfiguration serviceConfig
+ ) {
+ if (serviceConfig == null) {
+ return null;
+ }
+
+ return OpenIDConnectProviderConfiguration.builder()
+ .secretsArn(serviceConfig.secretsArn())
+ .secretsRole(serviceConfig.secretsRole())
+ .build();
+ }
+
+ static SamlProviderConfiguration fromSamlProviderConfiguration(
+ software.amazon.awssdk.services.qbusiness.model.SamlProviderConfiguration serviceConfig
+ ) {
+ if (serviceConfig == null) {
+ return null;
+ }
+
+ return SamlProviderConfiguration.builder()
+ .authenticationUrl(serviceConfig.authenticationUrl())
+ .build();
+ }
+
+ static software.amazon.awssdk.services.qbusiness.model.OpenIDConnectProviderConfiguration toOpenIDConnectProviderConfiguration(
+ OpenIDConnectProviderConfiguration modelConfig
+ ) {
+ if (modelConfig == null) {
+ return null;
+ }
+
+ return software.amazon.awssdk.services.qbusiness.model.OpenIDConnectProviderConfiguration.builder()
+ .secretsArn(modelConfig.getSecretsArn())
+ .secretsRole(modelConfig.getSecretsRole())
+ .build();
+ }
+
+
+ static software.amazon.awssdk.services.qbusiness.model.SamlProviderConfiguration toSamlProviderConfiguration(
+ SamlProviderConfiguration modelConfig
+ ) {
+ if (modelConfig == null) {
+ return null;
+ }
+
+ return software.amazon.awssdk.services.qbusiness.model.SamlProviderConfiguration.builder()
+ .authenticationUrl(modelConfig.getAuthenticationUrl())
+ .build();
+ }
+
+ static software.amazon.awssdk.services.qbusiness.model.IdentityProviderConfiguration toIdentityProviderConfiguration(
+ IdentityProviderConfiguration modelConfig
+ ) {
+ if (modelConfig == null) {
+ return null;
+ }
+
+ return software.amazon.awssdk.services.qbusiness.model.IdentityProviderConfiguration.builder()
+ .samlConfiguration(toSamlProviderConfiguration(modelConfig.getSamlConfiguration()))
+ .openIDConnectConfiguration(toOpenIDConnectProviderConfiguration(modelConfig.getOpenIDConnectConfiguration()))
+ .build();
+ }
+
/**
* Request to update properties of a previously created resource
*
diff --git a/aws-qbusiness-webexperience/src/test/java/software/amazon/qbusiness/webexperience/CreateHandlerTest.java b/aws-qbusiness-webexperience/src/test/java/software/amazon/qbusiness/webexperience/CreateHandlerTest.java
index f0df558..5033389 100644
--- a/aws-qbusiness-webexperience/src/test/java/software/amazon/qbusiness/webexperience/CreateHandlerTest.java
+++ b/aws-qbusiness-webexperience/src/test/java/software/amazon/qbusiness/webexperience/CreateHandlerTest.java
@@ -65,6 +65,7 @@ public class CreateHandlerTest extends AbstractTestBase {
private Constant testBackOff;
private CreateHandler underTest;
+ private IdentityProviderConfiguration identityProviderConfiguration;
private AutoCloseable testMocks;
@@ -84,11 +85,16 @@ public void setup() {
underTest = new CreateHandler(testBackOff);
+ identityProviderConfiguration = IdentityProviderConfiguration.builder()
+ .samlConfiguration(SamlProviderConfiguration.builder().authenticationUrl("https://someTestAuthenticationUrl").build())
+ .build();
+
createModel = ResourceModel.builder()
.applicationId(APP_ID)
.title("This is a title of the web experience.")
.subtitle("This is a subtitle of the web experience.")
.roleArn("RoleArn")
+ .identityProviderConfiguration(identityProviderConfiguration)
.build();
testRequest = ResourceHandlerRequest.builder()