diff --git a/.projen/deps.json b/.projen/deps.json
index 3332a2a..8afdaa3 100644
--- a/.projen/deps.json
+++ b/.projen/deps.json
@@ -104,7 +104,7 @@
},
{
"name": "aws-cdk-lib",
- "version": "^2.139.0",
+ "version": "^2.148.0",
"type": "peer"
},
{
diff --git a/.projenrc.ts b/.projenrc.ts
index 91de150..5929623 100644
--- a/.projenrc.ts
+++ b/.projenrc.ts
@@ -4,7 +4,7 @@ const project = new CdklabsConstructLibrary({
authorAddress: 'aws-avp-cdk-dev@amazon.com',
description: 'L2 AWS CDK Constructs for Amazon Verified Permissions',
keywords: ['cdk', 'aws-cdk', 'awscdk', 'aws', 'verified-permissions', 'authorization', 'verifiedpermissions'],
- cdkVersion: '2.139.0',
+ cdkVersion: '2.148.0',
defaultReleaseBranch: 'main',
devDeps: ['cdklabs-projen-project-types'],
bundledDeps: ['@cedar-policy/cedar-wasm@3.2.3'],
diff --git a/API.md b/API.md
index 96c6a15..c519aea 100644
--- a/API.md
+++ b/API.md
@@ -46,7 +46,9 @@ new IdentitySource(scope: Construct, id: string, props: IdentitySourceProps)
| --- | --- |
| toString
| Returns a string representation of this construct. |
| applyRemovalPolicy
| Apply the given removal policy to this resource. |
-| addUserPoolClient
| Add a User Pool Client. |
+| addAudience
| Add an audience to the list. |
+| addClientId
| Add a clientId to the list The method can be called only when the Identity Source is configured with one of these configs: - Cognito auth provider - OIDC auth provider and ID Token Selection mode. |
+| addUserPoolClient
| Add a User Pool Client The method can be called only when the Identity Source is configured with Cognito auth provider. |
---
@@ -80,13 +82,47 @@ account for data recovery and cleanup later (`RemovalPolicy.RETAIN`).
---
+##### `addAudience`
+
+```typescript
+public addAudience(audience: string): void
+```
+
+Add an audience to the list.
+
+The method can be called only when the Identity Source is configured with OIDC auth provider and Access Token Selection mode
+
+###### `audience`Required
+
+- *Type:* string
+
+the audience to be added.
+
+---
+
+##### `addClientId`
+
+```typescript
+public addClientId(clientId: string): void
+```
+
+Add a clientId to the list The method can be called only when the Identity Source is configured with one of these configs: - Cognito auth provider - OIDC auth provider and ID Token Selection mode.
+
+###### `clientId`Required
+
+- *Type:* string
+
+The clientId to be added.
+
+---
+
##### `addUserPoolClient`
```typescript
public addUserPoolClient(userPoolClient: IUserPoolClient): void
```
-Add a User Pool Client.
+Add a User Pool Client The method can be called only when the Identity Source is configured with Cognito auth provider.
###### `userPoolClient`Required
@@ -233,13 +269,16 @@ The Identity Source identifier.
| node
| constructs.Node
| The tree node. |
| env
| aws-cdk-lib.ResourceEnvironment
| The environment this resource belongs to. |
| stack
| aws-cdk-lib.Stack
| The stack in which this resource is defined. |
+| audiencesOIDC
| string[]
| *No description.* |
| clientIds
| string[]
| *No description.* |
-| discoveryUrl
| string
| *No description.* |
| identitySourceId
| string
| Identity Source identifier. |
-| openIdIssuer
| string
| *No description.* |
+| issuer
| string
| *No description.* |
| policyStore
| IPolicyStore
| *No description.* |
-| userPoolArn
| string
| *No description.* |
| cognitoGroupEntityType
| string
| *No description.* |
+| groupConfigGroupClaimOIDC
| string
| *No description.* |
+| groupConfigGroupEntityTypeOIDC
| string
| *No description.* |
+| principalIdClaimOIDC
| string
| *No description.* |
+| userPoolArn
| string
| *No description.* |
---
@@ -286,23 +325,23 @@ The stack in which this resource is defined.
---
-##### `clientIds`Required
+##### `audiencesOIDC`Required
```typescript
-public readonly clientIds: string[];
+public readonly audiencesOIDC: string[];
```
- *Type:* string[]
---
-##### `discoveryUrl`Required
+##### `clientIds`Required
```typescript
-public readonly discoveryUrl: string;
+public readonly clientIds: string[];
```
-- *Type:* string
+- *Type:* string[]
---
@@ -318,10 +357,10 @@ Identity Source identifier.
---
-##### `openIdIssuer`Required
+##### `issuer`Required
```typescript
-public readonly openIdIssuer: string;
+public readonly issuer: string;
```
- *Type:* string
@@ -338,20 +377,50 @@ public readonly policyStore: IPolicyStore;
---
-##### `userPoolArn`Required
+##### `cognitoGroupEntityType`Optional
```typescript
-public readonly userPoolArn: string;
+public readonly cognitoGroupEntityType: string;
```
- *Type:* string
---
-##### `cognitoGroupEntityType`Optional
+##### `groupConfigGroupClaimOIDC`Optional
```typescript
-public readonly cognitoGroupEntityType: string;
+public readonly groupConfigGroupClaimOIDC: string;
+```
+
+- *Type:* string
+
+---
+
+##### `groupConfigGroupEntityTypeOIDC`Optional
+
+```typescript
+public readonly groupConfigGroupEntityTypeOIDC: string;
+```
+
+- *Type:* string
+
+---
+
+##### `principalIdClaimOIDC`Optional
+
+```typescript
+public readonly principalIdClaimOIDC: string;
+```
+
+- *Type:* string
+
+---
+
+##### `userPoolArn`Optional
+
+```typescript
+public readonly userPoolArn: string;
```
- *Type:* string
@@ -1749,7 +1818,7 @@ const identitySourceAttributes: IdentitySourceAttributes = { ... }
| **Name** | **Type** | **Description** |
| --- | --- | --- |
-| identitySourceId
| string
| The identity Source identifier. |
+| identitySourceId
| string
| *No description.* |
---
@@ -1761,8 +1830,6 @@ public readonly identitySourceId: string;
- *Type:* string
-The identity Source identifier.
-
---
### IdentitySourceConfiguration
@@ -1780,21 +1847,36 @@ const identitySourceConfiguration: IdentitySourceConfiguration = { ... }
| **Name** | **Type** | **Description** |
| --- | --- | --- |
| cognitoUserPoolConfiguration
| CognitoUserPoolConfiguration
| Cognito User Pool Configuration. |
+| openIdConnectConfiguration
| OpenIdConnectConfiguration
| OpenID Connect Idp configuration. |
---
-##### `cognitoUserPoolConfiguration`Required
+##### `cognitoUserPoolConfiguration`Optional
```typescript
public readonly cognitoUserPoolConfiguration: CognitoUserPoolConfiguration;
```
- *Type:* CognitoUserPoolConfiguration
+- *Default:* no Cognito User Pool Config
Cognito User Pool Configuration.
---
+##### `openIdConnectConfiguration`Optional
+
+```typescript
+public readonly openIdConnectConfiguration: OpenIdConnectConfiguration;
+```
+
+- *Type:* OpenIdConnectConfiguration
+- *Default:* no OpenID Provider config
+
+OpenID Connect Idp configuration.
+
+---
+
### IdentitySourceProps
#### Initializer
@@ -1852,6 +1934,227 @@ Principal entity type.
---
+### OpenIdConnectAccessTokenConfiguration
+
+#### Initializer
+
+```typescript
+import { OpenIdConnectAccessTokenConfiguration } from '@cdklabs/cdk-verified-permissions'
+
+const openIdConnectAccessTokenConfiguration: OpenIdConnectAccessTokenConfiguration = { ... }
+```
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| audiences
| string[]
| The access token aud claim values that you want to accept in your policy store. |
+| principalIdClaim
| string
| The claim that determines the principal in OIDC access tokens. |
+
+---
+
+##### `audiences`Optional
+
+```typescript
+public readonly audiences: string[];
+```
+
+- *Type:* string[]
+- *Default:* no audiences
+
+The access token aud claim values that you want to accept in your policy store.
+
+---
+
+##### `principalIdClaim`Optional
+
+```typescript
+public readonly principalIdClaim: string;
+```
+
+- *Type:* string
+- *Default:* no principal claim
+
+The claim that determines the principal in OIDC access tokens.
+
+---
+
+### OpenIdConnectConfiguration
+
+#### Initializer
+
+```typescript
+import { OpenIdConnectConfiguration } from '@cdklabs/cdk-verified-permissions'
+
+const openIdConnectConfiguration: OpenIdConnectConfiguration = { ... }
+```
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| issuer
| string
| The issuer URL of an OIDC identity provider. |
+| accessTokenOnly
| OpenIdConnectAccessTokenConfiguration
| The configuration for processing access tokens from your OIDC identity provider Exactly one between accessTokenOnly and identityTokenOnly must be defined. |
+| entityIdPrefix
| string
| A descriptive string that you want to prefix to user entities from your OIDC identity provider. |
+| groupConfiguration
| OpenIdConnectGroupConfiguration
| The claim in OIDC identity provider tokens that indicates a user's group membership, and the entity type that you want to map it to. |
+| identityTokenOnly
| OpenIdConnectIdentityTokenConfiguration
| The configuration for processing identity (ID) tokens from your OIDC identity provider Exactly one between accessTokenOnly and identityTokenOnly must be defined. |
+
+---
+
+##### `issuer`Required
+
+```typescript
+public readonly issuer: string;
+```
+
+- *Type:* string
+
+The issuer URL of an OIDC identity provider.
+
+This URL must have an OIDC discovery endpoint at the path .well-known/openid-configuration
+
+---
+
+##### `accessTokenOnly`Optional
+
+```typescript
+public readonly accessTokenOnly: OpenIdConnectAccessTokenConfiguration;
+```
+
+- *Type:* OpenIdConnectAccessTokenConfiguration
+- *Default:* no Access Token Config
+
+The configuration for processing access tokens from your OIDC identity provider Exactly one between accessTokenOnly and identityTokenOnly must be defined.
+
+---
+
+##### `entityIdPrefix`Optional
+
+```typescript
+public readonly entityIdPrefix: string;
+```
+
+- *Type:* string
+- *Default:* no Entity ID Prefix
+
+A descriptive string that you want to prefix to user entities from your OIDC identity provider.
+
+---
+
+##### `groupConfiguration`Optional
+
+```typescript
+public readonly groupConfiguration: OpenIdConnectGroupConfiguration;
+```
+
+- *Type:* OpenIdConnectGroupConfiguration
+- *Default:* no Group Config
+
+The claim in OIDC identity provider tokens that indicates a user's group membership, and the entity type that you want to map it to.
+
+---
+
+##### `identityTokenOnly`Optional
+
+```typescript
+public readonly identityTokenOnly: OpenIdConnectIdentityTokenConfiguration;
+```
+
+- *Type:* OpenIdConnectIdentityTokenConfiguration
+- *Default:* no ID Token Config
+
+The configuration for processing identity (ID) tokens from your OIDC identity provider Exactly one between accessTokenOnly and identityTokenOnly must be defined.
+
+---
+
+### OpenIdConnectGroupConfiguration
+
+#### Initializer
+
+```typescript
+import { OpenIdConnectGroupConfiguration } from '@cdklabs/cdk-verified-permissions'
+
+const openIdConnectGroupConfiguration: OpenIdConnectGroupConfiguration = { ... }
+```
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| groupClaim
| string
| The token claim that you want Verified Permissions to interpret as group membership. |
+| groupEntityType
| string
| The policy store entity type that you want to map your users' group claim to. |
+
+---
+
+##### `groupClaim`Required
+
+```typescript
+public readonly groupClaim: string;
+```
+
+- *Type:* string
+
+The token claim that you want Verified Permissions to interpret as group membership.
+
+---
+
+##### `groupEntityType`Required
+
+```typescript
+public readonly groupEntityType: string;
+```
+
+- *Type:* string
+
+The policy store entity type that you want to map your users' group claim to.
+
+---
+
+### OpenIdConnectIdentityTokenConfiguration
+
+#### Initializer
+
+```typescript
+import { OpenIdConnectIdentityTokenConfiguration } from '@cdklabs/cdk-verified-permissions'
+
+const openIdConnectIdentityTokenConfiguration: OpenIdConnectIdentityTokenConfiguration = { ... }
+```
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| clientIds
| string[]
| The ID token audience, or client ID, claim values that you want to accept in your policy store from an OIDC identity provider. |
+| principalIdClaim
| string
| The claim that determines the principal in OIDC access tokens. |
+
+---
+
+##### `clientIds`Optional
+
+```typescript
+public readonly clientIds: string[];
+```
+
+- *Type:* string[]
+- *Default:* no client IDs
+
+The ID token audience, or client ID, claim values that you want to accept in your policy store from an OIDC identity provider.
+
+---
+
+##### `principalIdClaim`Optional
+
+```typescript
+public readonly principalIdClaim: string;
+```
+
+- *Type:* string
+- *Default:* no principal claim
+
+The claim that determines the principal in OIDC access tokens.
+
+---
+
### PolicyAttributes
#### Initializer
diff --git a/README.md b/README.md
index 45078e9..e46f51f 100644
--- a/README.md
+++ b/README.md
@@ -61,7 +61,7 @@ const policyStore = new PolicyStore(scope, "PolicyStore", {
## Schemas
-If you want to have type safety when defining a schema, you can accomplish this in typescript. Simply use the `Schema` type exported by the `@cedar-policy/cedar-wasm`.
+If you want to have type safety when defining a schema, you can accomplish this **only** in typescript. Simply use the `Schema` type exported by the `@cedar-policy/cedar-wasm`.
You can also generate a simple schema from a swagger file using the static function `schemaFromOpenApiSpec` in the PolicyStore construct. This functionality replicates what you can find in the AWS Verified Permissions console.
@@ -85,7 +85,7 @@ const policyStore = new PolicyStore(scope, "PolicyStore", {
## Identity Source
-Define Identity Source with required properties:
+Define Identity Source with Cognito Configuration and required properties:
```ts
const userPool = new UserPool(scope, "UserPool"); // Creating a new Cognito UserPool
@@ -125,7 +125,7 @@ new IdentitySource(scope, "IdentitySource", {
});
```
-Define Identity Source with all the properties:
+Define Identity Source with Cognito Configuration and all properties:
```ts
const validationSettingsStrict = {
@@ -171,6 +171,111 @@ new IdentitySource(scope, "IdentitySource", {
});
```
+Define Identity Source with OIDC Configuration and Access Token selection config:
+```ts
+const validationSettingsStrict = {
+ mode: ValidationSettingsMode.STRICT,
+};
+const cedarJsonSchema = {
+ PhotoApp: {
+ entityTypes: {
+ User: {},
+ Photo: {},
+ },
+ actions: {
+ viewPhoto: {
+ appliesTo: {
+ principalTypes: ["User"],
+ resourceTypes: ["Photo"],
+ },
+ },
+ },
+ },
+};
+const cedarSchema = {
+ cedarJson: JSON.stringify(cedarJsonSchema),
+};
+const policyStore = new PolicyStore(scope, "PolicyStore", {
+ schema: cedarSchema,
+ validationSettings: validationSettingsStrict,
+});
+const issuer = 'https://iamanidp.com';
+const principalIdClaim = 'sub';
+const entityIdPrefix = 'prefix';
+const groupClaim = 'group';
+const groupEntityType = 'GroupType';
+new IdentitySource(scope, 'IdentitySource', {
+ configuration: {
+ openIdConnectConfiguration: {
+ issuer: issuer,
+ entityIdPrefix: entityIdPrefix,
+ groupConfiguration: {
+ groupClaim: groupClaim,
+ groupEntityType: groupEntityType,
+ },
+ accessTokenOnly: {
+ audiences: ['testAudience'],
+ principalIdClaim: principalIdClaim,
+ },
+ },
+ },
+ policyStore: policyStore,
+ principalEntityType: 'TestType',
+});
+```
+
+Define Identity Source with OIDC Configuration and Identity Token selection config:
+```ts
+const validationSettingsStrict = {
+ mode: ValidationSettingsMode.STRICT,
+};
+const cedarJsonSchema = {
+ PhotoApp: {
+ entityTypes: {
+ User: {},
+ Photo: {},
+ },
+ actions: {
+ viewPhoto: {
+ appliesTo: {
+ principalTypes: ["User"],
+ resourceTypes: ["Photo"],
+ },
+ },
+ },
+ },
+};
+const cedarSchema = {
+ cedarJson: JSON.stringify(cedarJsonSchema),
+};
+const policyStore = new PolicyStore(scope, "PolicyStore", {
+ schema: cedarSchema,
+ validationSettings: validationSettingsStrict,
+});
+const issuer = 'https://iamanidp.com';
+const entityIdPrefix = 'prefix';
+const groupClaim = 'group';
+const groupEntityType = 'UserGroup';
+const principalIdClaim = 'sub';
+new IdentitySource(scope, 'IdentitySource', {
+ configuration: {
+ openIdConnectConfiguration: {
+ issuer: issuer,
+ entityIdPrefix: entityIdPrefix,
+ groupConfiguration: {
+ groupClaim: groupClaim,
+ groupEntityType: groupEntityType,
+ },
+ identityTokenOnly: {
+ clientIds: [],
+ principalIdClaim: principalIdClaim,
+ },
+ },
+ },
+ policyStore: policyStore,
+});
+```
+
## Policy
Load all the `.cedar` files in a given folder and define Policy objects for each of them. All policies will be associated with the same policy store.
diff --git a/package.json b/package.json
index b46764c..8ecf730 100644
--- a/package.json
+++ b/package.json
@@ -49,7 +49,7 @@
"@types/node": "^18",
"@typescript-eslint/eslint-plugin": "^7",
"@typescript-eslint/parser": "^7",
- "aws-cdk-lib": "2.139.0",
+ "aws-cdk-lib": "2.148.0",
"cdklabs-projen-project-types": "^0.1.199",
"constructs": "10.0.5",
"eslint": "^8",
@@ -69,7 +69,7 @@
"typescript": "^5.5.3"
},
"peerDependencies": {
- "aws-cdk-lib": "^2.139.0",
+ "aws-cdk-lib": "^2.148.0",
"constructs": "^10.0.5"
},
"dependencies": {
diff --git a/src/identity-source.ts b/src/identity-source.ts
index ee9d5b1..d85f37a 100644
--- a/src/identity-source.ts
+++ b/src/identity-source.ts
@@ -4,6 +4,11 @@ import { IResource, Lazy, Resource } from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
import { IPolicyStore } from './policy-store';
+enum ConfigurationMode {
+ COGNITO = 'COGNITO',
+ OIDC_ACCESS_TOKEN = 'OIDC_ACCESS_TOKEN',
+ OIDC_ID_TOKEN = 'OIDC_ID_TOKEN'
+}
export interface CognitoGroupConfiguration {
/**
@@ -35,38 +40,112 @@ export interface CognitoUserPoolConfiguration {
readonly userPool: IUserPool;
}
-export interface IdentitySourceConfiguration {
+export interface OpenIdConnectGroupConfiguration {
/**
- * Cognito User Pool Configuration.
+ * The token claim that you want Verified Permissions to interpret as group membership
+ *
+ */
+ readonly groupClaim: string;
+
+ /**
+ * The policy store entity type that you want to map your users' group claim to
*
- * @attribute
*/
- readonly cognitoUserPoolConfiguration: CognitoUserPoolConfiguration;
+ readonly groupEntityType: string;
}
-export interface IIdentitySource extends IResource {
+export interface OpenIdConnectAccessTokenConfiguration {
/**
- * Identity Source identifier.
+ * The access token aud claim values that you want to accept in your policy store
+ *
+ * @default - no audiences
*
- * @attribute
*/
- readonly identitySourceId: string;
+ readonly audiences?: string[];
+
+ /**
+ * The claim that determines the principal in OIDC access tokens
+ *
+ * @default - no principal claim
+ */
+ readonly principalIdClaim?: string;
}
-abstract class IdentitySourceBase extends Resource implements IIdentitySource {
- abstract readonly identitySourceId: string;
+export interface OpenIdConnectIdentityTokenConfiguration {
+ /**
+ * The ID token audience, or client ID, claim values that you want to accept in your policy store from an OIDC identity provider
+ *
+ * @default - no client IDs
+ *
+ */
+ readonly clientIds?: string[];
+
+ /**
+ * The claim that determines the principal in OIDC access tokens
+ *
+ * @default - no principal claim
+ */
+ readonly principalIdClaim?: string;
}
-export interface IdentitySourceAttributes {
+export interface OpenIdConnectConfiguration {
/**
- * The identity Source identifier
+ * A descriptive string that you want to prefix to user entities from your OIDC identity provider
*
- * @attribute
+ * @default - no Entity ID Prefix
*/
- readonly identitySourceId: string;
+ readonly entityIdPrefix?: string;
+
+ /**
+ * The claim in OIDC identity provider tokens that indicates a user's group membership, and the entity type that you want to map it to
+ *
+ * @default - no Group Config
+ */
+ readonly groupConfiguration?: OpenIdConnectGroupConfiguration;
+
+ /**
+ * The issuer URL of an OIDC identity provider. This URL must have an OIDC discovery endpoint at the path .well-known/openid-configuration
+ *
+ */
+ readonly issuer: string;
+
+ /**
+ * The configuration for processing access tokens from your OIDC identity provider
+ * Exactly one between accessTokenOnly and identityTokenOnly must be defined
+ *
+ * @default - no Access Token Config
+ */
+ readonly accessTokenOnly?: OpenIdConnectAccessTokenConfiguration;
+
+ /**
+ * The configuration for processing identity (ID) tokens from your OIDC identity provider
+ * Exactly one between accessTokenOnly and identityTokenOnly must be defined
+ *
+ * @default - no ID Token Config
+ */
+ readonly identityTokenOnly?: OpenIdConnectIdentityTokenConfiguration;
+}
+
+export interface IdentitySourceConfiguration {
+ /**
+ * Cognito User Pool Configuration.
+ *
+ * @default - no Cognito User Pool Config
+ *
+ */
+ readonly cognitoUserPoolConfiguration?: CognitoUserPoolConfiguration;
+
+ /**
+ * OpenID Connect Idp configuration
+ *
+ * @default - no OpenID Provider config
+ *
+ */
+ readonly openIdConnectConfiguration?: OpenIdConnectConfiguration;
}
+
export interface IdentitySourceProps {
/**
* Identity Source configuration.
@@ -87,6 +166,24 @@ export interface IdentitySourceProps {
readonly principalEntityType?: string;
}
+export interface IIdentitySource extends IResource {
+ /**
+ * Identity Source identifier.
+ *
+ * @attribute
+ */
+ readonly identitySourceId: string;
+}
+
+abstract class IdentitySourceBase extends Resource implements IIdentitySource {
+ abstract readonly identitySourceId: string;
+}
+
+export interface IdentitySourceAttributes {
+ readonly identitySourceId: string;
+}
+
+
export class IdentitySource extends IdentitySourceBase {
/**
* Creates Identity Source from its attributes
@@ -132,52 +229,152 @@ export class IdentitySource extends IdentitySourceBase {
identitySourceId,
});
}
-
+ private readonly configurationMode: ConfigurationMode;
private readonly identitySource: CfnIdentitySource;
readonly clientIds: string[];
- readonly discoveryUrl: string;
readonly identitySourceId: string;
- readonly openIdIssuer: string;
- readonly userPoolArn: string;
+ readonly issuer: string;
+ readonly userPoolArn?: string;
readonly cognitoGroupEntityType?: string;
readonly policyStore: IPolicyStore;
+ readonly audiencesOIDC: string[];
+ readonly principalIdClaimOIDC?: string;
+ readonly groupConfigGroupClaimOIDC?: string;
+ readonly groupConfigGroupEntityTypeOIDC?: string;
constructor(scope: Construct, id: string, props: IdentitySourceProps) {
super(scope, id);
- this.clientIds =
- props.configuration.cognitoUserPoolConfiguration.clientIds ?? [];
- this.userPoolArn =
- props.configuration.cognitoUserPoolConfiguration.userPool.userPoolArn;
- const cognitoGroupConfiguration = props.configuration.cognitoUserPoolConfiguration.groupConfiguration?.groupEntityType
- ? {
- groupEntityType: props.configuration.cognitoUserPoolConfiguration.groupConfiguration.groupEntityType,
- }
- : undefined;
- this.identitySource = new CfnIdentitySource(this, id, {
- configuration: {
+ if (props.configuration.cognitoUserPoolConfiguration && props.configuration.openIdConnectConfiguration) {
+ throw new Error('Only one between cognitoUserPoolConfiguration or openIdConnectConfiguration must be defined');
+ }
+
+ let cfnConfiguration: CfnIdentitySource.IdentitySourceConfigurationProperty;
+ let issuer: string;
+ if (props.configuration.cognitoUserPoolConfiguration) {
+ this.clientIds = props.configuration.cognitoUserPoolConfiguration.clientIds ?? [];
+ this.audiencesOIDC = [];
+ const cognitoGroupConfiguration = props.configuration.cognitoUserPoolConfiguration.groupConfiguration?.groupEntityType
+ ? {
+ groupEntityType: props.configuration.cognitoUserPoolConfiguration.groupConfiguration.groupEntityType,
+ }
+ : undefined;
+ cfnConfiguration = {
cognitoUserPoolConfiguration: {
clientIds: Lazy.list({ produce: () => this.clientIds }),
- userPoolArn: this.userPoolArn,
+ userPoolArn: props.configuration.cognitoUserPoolConfiguration.userPool.userPoolArn,
groupConfiguration: cognitoGroupConfiguration,
},
- },
+ };
+ this.cognitoGroupEntityType = cognitoGroupConfiguration?.groupEntityType;
+ issuer = 'COGNITO';
+ this.configurationMode = ConfigurationMode.COGNITO;
+ } else if (props.configuration.openIdConnectConfiguration) {
+
+ if (props.configuration.openIdConnectConfiguration.accessTokenOnly &&
+ props.configuration.openIdConnectConfiguration.identityTokenOnly) {
+ throw new Error('Exactly one token selection method between accessTokenOnly and identityTokenOnly must be defined');
+ }
+
+ let tokenSelection: CfnIdentitySource.OpenIdConnectTokenSelectionProperty;
+ if (props.configuration.openIdConnectConfiguration.accessTokenOnly) {
+ if (!props.configuration.openIdConnectConfiguration.accessTokenOnly.audiences ||
+ props.configuration.openIdConnectConfiguration.accessTokenOnly.audiences.length == 0) {
+ throw new Error('At least one audience is expected in OIDC Access token selection mode');
+ }
+ this.clientIds = [];
+ this.audiencesOIDC = props.configuration.openIdConnectConfiguration.accessTokenOnly.audiences;
+ tokenSelection = {
+ accessTokenOnly: {
+ audiences: Lazy.list({ produce: () => this.audiencesOIDC }),
+ principalIdClaim: props.configuration.openIdConnectConfiguration.accessTokenOnly.principalIdClaim,
+ },
+ };
+ this.principalIdClaimOIDC = props.configuration.openIdConnectConfiguration.accessTokenOnly.principalIdClaim;
+ this.configurationMode = ConfigurationMode.OIDC_ACCESS_TOKEN;
+ } else if (props.configuration.openIdConnectConfiguration.identityTokenOnly) {
+ this.clientIds = props.configuration.openIdConnectConfiguration.identityTokenOnly.clientIds ?? [];
+ this.audiencesOIDC = [];
+ tokenSelection = {
+ identityTokenOnly: {
+ clientIds: Lazy.list({ produce: () => this.clientIds }),
+ principalIdClaim: props.configuration.openIdConnectConfiguration.identityTokenOnly.principalIdClaim,
+ },
+ };
+ this.principalIdClaimOIDC = props.configuration.openIdConnectConfiguration.identityTokenOnly.principalIdClaim;
+ this.configurationMode = ConfigurationMode.OIDC_ID_TOKEN;
+ } else {
+ throw new Error('One token selection method between accessTokenOnly and identityTokenOnly must be defined');
+ }
+ cfnConfiguration = {
+ openIdConnectConfiguration: {
+ issuer: props.configuration.openIdConnectConfiguration.issuer,
+ entityIdPrefix: props.configuration.openIdConnectConfiguration.entityIdPrefix,
+ groupConfiguration: props.configuration.openIdConnectConfiguration.groupConfiguration ? {
+ groupClaim: props.configuration.openIdConnectConfiguration.groupConfiguration.groupClaim,
+ groupEntityType: props.configuration.openIdConnectConfiguration.groupConfiguration.groupEntityType,
+ } : undefined,
+ tokenSelection: tokenSelection,
+ },
+ };
+ this.groupConfigGroupClaimOIDC = props.configuration.openIdConnectConfiguration.groupConfiguration?.groupClaim;
+ this.groupConfigGroupEntityTypeOIDC = props.configuration.openIdConnectConfiguration.groupConfiguration?.groupEntityType;
+ issuer = props.configuration.openIdConnectConfiguration.issuer;
+ } else {
+ throw new Error('One Identity provider configuration between cognitoUserPoolConfiguration and openIdConnectConfiguration must be defined');
+ }
+ this.identitySource = new CfnIdentitySource(this, id, {
+ configuration: cfnConfiguration,
policyStoreId: props.policyStore.policyStoreId,
principalEntityType: props.principalEntityType,
});
- this.discoveryUrl = this.identitySource.attrDetailsDiscoveryUrl;
+
+ this.userPoolArn = props.configuration.cognitoUserPoolConfiguration?.userPool.userPoolArn || undefined;
this.identitySourceId = this.identitySource.attrIdentitySourceId;
- this.openIdIssuer = this.identitySource.attrDetailsOpenIdIssuer;
+ this.issuer = issuer;
this.policyStore = props.policyStore;
- this.cognitoGroupEntityType = cognitoGroupConfiguration?.groupEntityType;
+
}
/**
* Add a User Pool Client
+ * The method can be called only when the Identity Source is configured with Cognito auth provider
*
* @param userPoolClient The User Pool Client Construct.
*/
public addUserPoolClient(userPoolClient: IUserPoolClient): void {
- this.clientIds.push(userPoolClient.userPoolClientId);
+ if (this.configurationMode != ConfigurationMode.COGNITO) {
+ throw new Error('Cannot add User Pool Client when IdentitySource auth provider is not Cognito');
+ }
+ this.addClientId(userPoolClient.userPoolClientId);
}
+
+ /**
+ * Add a clientId to the list
+ * The method can be called only when the Identity Source is configured with one of these configs:
+ * - Cognito auth provider
+ * - OIDC auth provider and ID Token Selection mode
+ *
+ * @param clientId The clientId to be added
+ */
+ public addClientId(clientId: string) {
+ if (this.configurationMode != ConfigurationMode.COGNITO && this.configurationMode != ConfigurationMode.OIDC_ID_TOKEN) {
+ throw new Error('Adding a Client ID is only supported for the auth providers Cognito or OIDC with configured with ID Token');
+ }
+ this.clientIds.push(clientId);
+ }
+
+ /**
+ * Add an audience to the list.
+ * The method can be called only when the Identity Source is configured with OIDC auth provider and Access Token Selection mode
+ *
+ * @param audience the audience to be added
+ */
+ public addAudience(audience: string) {
+ if (this.configurationMode != ConfigurationMode.OIDC_ACCESS_TOKEN) {
+ throw new Error('Cannot add audience when IdentitySource auth provider is not OIDC with Access Token');
+ }
+ this.audiencesOIDC.push(audience);
+ }
+
}
diff --git a/test/identity-source.test.ts b/test/identity-source.test.ts
index b9ece42..1328e1f 100644
--- a/test/identity-source.test.ts
+++ b/test/identity-source.test.ts
@@ -7,9 +7,9 @@ import { IdentitySource } from '../src/identity-source';
import { PolicyStore, ValidationSettingsMode } from '../src/policy-store';
-describe('Identity Source creation', () => {
+describe('Identity Source creation with Cognito config', () => {
- test('Creating Identity Source with required properties', () => {
+ test('Creating Identity Source with Cognito config', () => {
// GIVEN
const stack = new Stack(undefined, 'Stack');
@@ -48,7 +48,7 @@ describe('Identity Source creation', () => {
});
});
- test('Creating Identity Source with all properties', () => {
+ test('Creating Identity Source with Cognito config and all the properties', () => {
// GIVEN
const stack = new Stack(undefined, 'Stack');
@@ -120,7 +120,7 @@ describe('Identity Source reference existing Identity Source', () => {
});
describe('User Pool Client addition', () => {
- test('Adding a User Pool Client', () => {
+ test('Adding a User Pool Client to Identity Source configured with Cognito', () => {
// GIVEN
const stack = new Stack(undefined, 'Stack');
@@ -166,4 +166,676 @@ describe('User Pool Client addition', () => {
},
});
});
+
+ test('Adding a User Pool Client to Identity Source configured with OIDC, should throw', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const userPool = new UserPool(stack, 'UserPool');
+ const userPoolClient = new UserPoolClient(stack, 'UserPoolClient', { userPool: userPool });
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ const identitySource = new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ openIdConnectConfiguration: {
+ issuer: 'https://iamanidp.com',
+ accessTokenOnly: {
+ audiences: ['aud1'],
+ principalIdClaim: 'sub',
+ },
+ },
+ },
+ policyStore: policyStore,
+ principalEntityType: 'TestType',
+ });
+
+ // THEN
+ expect(() => {
+ identitySource.addUserPoolClient(userPoolClient);
+ }).toThrow('Cannot add User Pool Client when IdentitySource auth provider is not Cognito');
+
+ });
+});
+
+describe('Client addition to OIDC configured Identity Source', () => {
+ test('Adding a Client to Identity Source configured with OIDC and token selection = access token - should throw', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ const identitySource = new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ openIdConnectConfiguration: {
+ issuer: 'https://iamanidp.com',
+ accessTokenOnly: {
+ audiences: ['aud1'],
+ principalIdClaim: 'sub',
+ },
+ },
+ },
+ policyStore: policyStore,
+ principalEntityType: 'TestType',
+ });
+
+ // THEN
+ expect(() => {
+ identitySource.addClientId('testClientId');
+ }).toThrow('Adding a Client ID is only supported for the auth providers Cognito or OIDC with configured with ID Token');
+ });
+
+ test('Adding a Client to Identity Source configured with OIDC and token selection = identity token', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ const policyStoreLogicalId = getResourceLogicalId(policyStore, CfnPolicyStore);
+ const issuer = 'https://iamanidp.com';
+ const entityIdPrefix = 'prefix';
+ const groupClaim = 'group';
+ const groupEntityType = 'UserGroup';
+ const principalIdClaim = 'sub';
+ const identitySource = new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ openIdConnectConfiguration: {
+ issuer: issuer,
+ entityIdPrefix: entityIdPrefix,
+ groupConfiguration: {
+ groupClaim: groupClaim,
+ groupEntityType: groupEntityType,
+ },
+ identityTokenOnly: {
+ clientIds: [],
+ principalIdClaim: principalIdClaim,
+ },
+ },
+ },
+ policyStore: policyStore,
+ });
+
+ const clientToBeAdded = 'client1';
+ identitySource.addClientId(clientToBeAdded);
+
+ // THEN
+ Template.fromStack(stack).hasResourceProperties('AWS::VerifiedPermissions::IdentitySource', {
+ Configuration: {
+ OpenIdConnectConfiguration: {
+ Issuer: issuer,
+ EntityIdPrefix: entityIdPrefix,
+ GroupConfiguration: {
+ GroupClaim: groupClaim,
+ GroupEntityType: groupEntityType,
+ },
+ TokenSelection: {
+ IdentityTokenOnly: {
+ ClientIds: [clientToBeAdded],
+ PrincipalIdClaim: principalIdClaim,
+ },
+ },
+ },
+ },
+ PolicyStoreId: {
+ 'Fn::GetAtt': [policyStoreLogicalId, 'PolicyStoreId'],
+ },
+ });
+
+ });
+
+ test('Adding a Client to Identity Source configured with Cognito', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const userPool = new UserPool(stack, 'UserPool');
+ const userPoolClient = new UserPoolClient(stack, 'UserPoolClient', { userPool: userPool });
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ const policyStoreLogicalId = getResourceLogicalId(policyStore, CfnPolicyStore);
+ const identitySource = new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ cognitoUserPoolConfiguration: {
+ userPool: userPool,
+ },
+ },
+ policyStore: policyStore,
+ });
+
+ identitySource.addClientId(userPoolClient.userPoolClientId);
+
+ // THEN
+ Template.fromStack(stack).hasResourceProperties('AWS::VerifiedPermissions::IdentitySource', {
+ Configuration: {
+ CognitoUserPoolConfiguration: {
+ ClientIds: [
+ Match.objectEquals({
+ Ref: Match.stringLikeRegexp('UserPoolClient*'),
+ }),
+ ],
+ UserPoolArn: {
+ 'Fn::GetAtt': [
+ getResourceLogicalId(userPool, CfnUserPool),
+ 'Arn',
+ ],
+ },
+ },
+ },
+ PolicyStoreId: {
+ 'Fn::GetAtt': [policyStoreLogicalId, 'PolicyStoreId'],
+ },
+ });
+ });
+
+});
+
+describe('Audience addition to OIDC configured Identity Source', () => {
+ test('Adding an Audience to Identity Source configured with OIDC and token selection = id token - should throw', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ const identitySource = new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ openIdConnectConfiguration: {
+ issuer: 'https://iamanidp.com',
+ identityTokenOnly: {
+ clientIds: ['client1'],
+ principalIdClaim: 'sub',
+ },
+ },
+ },
+ policyStore: policyStore,
+ principalEntityType: 'TestType',
+ });
+
+ // THEN
+ expect(() => {
+ identitySource.addAudience('TestAudience');
+ }).toThrow('Cannot add audience when IdentitySource auth provider is not OIDC with Access Token');
+ });
+ test('Adding a Client to Identity Source configured with Cognito - should throw', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const userPool = new UserPool(stack, 'UserPool');
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ const identitySource = new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ cognitoUserPoolConfiguration: {
+ userPool: userPool,
+ },
+ },
+ policyStore: policyStore,
+ });
+
+ // THEN
+ expect(() => {
+ identitySource.addAudience('TestAudience');
+ }).toThrow('Cannot add audience when IdentitySource auth provider is not OIDC with Access Token');
+
+ });
+ test('Adding an Audience to Identity Source configured with OIDC and token selection = access token', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ const policyStoreLogicalId = getResourceLogicalId(policyStore, CfnPolicyStore);
+ const issuer = 'https://iamanidp.com';
+ const principalIdClaim = 'sub';
+ const entityIdPrefix = 'prefix';
+ const groupClaim = 'group';
+ const groupEntityType = 'GroupType';
+ const existingAudience = 'audience';
+ const identitySource = new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ openIdConnectConfiguration: {
+ issuer: issuer,
+ entityIdPrefix: entityIdPrefix,
+ groupConfiguration: {
+ groupClaim: groupClaim,
+ groupEntityType: groupEntityType,
+ },
+ accessTokenOnly: {
+ audiences: [existingAudience],
+ principalIdClaim: principalIdClaim,
+ },
+ },
+ },
+ policyStore: policyStore,
+ principalEntityType: 'TestType',
+ });
+
+ const audienceToBeAdded = 'TestAudience';
+ identitySource.addAudience(audienceToBeAdded);
+
+ // THEN
+ Template.fromStack(stack).hasResourceProperties('AWS::VerifiedPermissions::IdentitySource', {
+ Configuration: {
+ OpenIdConnectConfiguration: {
+ Issuer: issuer,
+ EntityIdPrefix: entityIdPrefix,
+ GroupConfiguration: {
+ GroupClaim: groupClaim,
+ GroupEntityType: groupEntityType,
+ },
+ TokenSelection: {
+ AccessTokenOnly: {
+ Audiences: [existingAudience, audienceToBeAdded],
+ PrincipalIdClaim: principalIdClaim,
+ },
+ },
+ },
+ },
+ PolicyStoreId: {
+ 'Fn::GetAtt': [policyStoreLogicalId, 'PolicyStoreId'],
+ },
+ });
+ });
});
+
+describe('Identity Source creation with OIDC config', () => {
+ test('Creating Identity Source with OIDC and token selection = access token', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ const policyStoreLogicalId = getResourceLogicalId(policyStore, CfnPolicyStore);
+ const issuer = 'https://iamanidp.com';
+ const principalIdClaim = 'sub';
+ const entityIdPrefix = 'prefix';
+ const groupClaim = 'group';
+ const groupEntityType = 'GroupType';
+ const audience = 'testAudience';
+ new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ openIdConnectConfiguration: {
+ issuer: issuer,
+ entityIdPrefix: entityIdPrefix,
+ groupConfiguration: {
+ groupClaim: groupClaim,
+ groupEntityType: groupEntityType,
+ },
+ accessTokenOnly: {
+ audiences: [audience],
+ principalIdClaim: principalIdClaim,
+ },
+ },
+ },
+ policyStore: policyStore,
+ principalEntityType: 'TestType',
+ });
+
+ // THEN
+ Template.fromStack(stack).hasResourceProperties('AWS::VerifiedPermissions::IdentitySource', {
+ Configuration: {
+ OpenIdConnectConfiguration: {
+ Issuer: issuer,
+ EntityIdPrefix: entityIdPrefix,
+ GroupConfiguration: {
+ GroupClaim: groupClaim,
+ GroupEntityType: groupEntityType,
+ },
+ TokenSelection: {
+ AccessTokenOnly: {
+ Audiences: [audience],
+ PrincipalIdClaim: principalIdClaim,
+ },
+ },
+ },
+ },
+ PolicyStoreId: {
+ 'Fn::GetAtt': [policyStoreLogicalId, 'PolicyStoreId'],
+ },
+ });
+ });
+
+ test('Creating Identity Source with OIDC and token selection = access token and audiences not set - should throw', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ const issuer = 'https://iamanidp.com';
+ const principalIdClaim = 'sub';
+ const entityIdPrefix = 'prefix';
+ const groupClaim = 'group';
+ const groupEntityType = 'GroupType';
+
+ // THEN
+ expect(() => {
+ new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ openIdConnectConfiguration: {
+ issuer: issuer,
+ entityIdPrefix: entityIdPrefix,
+ groupConfiguration: {
+ groupClaim: groupClaim,
+ groupEntityType: groupEntityType,
+ },
+ accessTokenOnly: {
+ principalIdClaim: principalIdClaim,
+ },
+ },
+ },
+ policyStore: policyStore,
+ principalEntityType: 'TestType',
+ });
+ }).toThrow('At least one audience is expected in OIDC Access token selection mode');
+ });
+
+
+ test('Creating Identity Source with OIDC and token selection = access token and audiences empty - should throw', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ const issuer = 'https://iamanidp.com';
+ const principalIdClaim = 'sub';
+ const entityIdPrefix = 'prefix';
+ const groupClaim = 'group';
+ const groupEntityType = 'GroupType';
+
+ // THEN
+ expect(() => {
+ new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ openIdConnectConfiguration: {
+ issuer: issuer,
+ entityIdPrefix: entityIdPrefix,
+ groupConfiguration: {
+ groupClaim: groupClaim,
+ groupEntityType: groupEntityType,
+ },
+ accessTokenOnly: {
+ principalIdClaim: principalIdClaim,
+ audiences: [],
+ },
+ },
+ },
+ policyStore: policyStore,
+ principalEntityType: 'TestType',
+ });
+ }).toThrow('At least one audience is expected in OIDC Access token selection mode');
+ });
+
+ test('Creating Identity Source with OIDC and token selection = identity token', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ const policyStoreLogicalId = getResourceLogicalId(policyStore, CfnPolicyStore);
+ const issuer = 'https://iamanidp.com';
+ const entityIdPrefix = 'prefix';
+ const groupClaim = 'group';
+ const groupEntityType = 'UserGroup';
+ const principalIdClaim = 'sub';
+ new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ openIdConnectConfiguration: {
+ issuer: issuer,
+ entityIdPrefix: entityIdPrefix,
+ groupConfiguration: {
+ groupClaim: groupClaim,
+ groupEntityType: groupEntityType,
+ },
+ identityTokenOnly: {
+ clientIds: [],
+ principalIdClaim: principalIdClaim,
+ },
+ },
+ },
+ policyStore: policyStore,
+ });
+
+ // THEN
+ Template.fromStack(stack).hasResourceProperties('AWS::VerifiedPermissions::IdentitySource', {
+ Configuration: {
+ OpenIdConnectConfiguration: {
+ Issuer: issuer,
+ EntityIdPrefix: entityIdPrefix,
+ GroupConfiguration: {
+ GroupClaim: groupClaim,
+ GroupEntityType: groupEntityType,
+ },
+ TokenSelection: {
+ IdentityTokenOnly: {
+ ClientIds: [],
+ PrincipalIdClaim: principalIdClaim,
+ },
+ },
+ },
+ },
+ PolicyStoreId: {
+ 'Fn::GetAtt': [policyStoreLogicalId, 'PolicyStoreId'],
+ },
+ });
+ });
+
+ test('Creating Identity Source with OIDC and token selection = identity token and client ids not set', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ const policyStoreLogicalId = getResourceLogicalId(policyStore, CfnPolicyStore);
+ const issuer = 'https://iamanidp.com';
+ const entityIdPrefix = 'prefix';
+ const groupClaim = 'group';
+ const groupEntityType = 'UserGroup';
+ const principalIdClaim = 'sub';
+ new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ openIdConnectConfiguration: {
+ issuer: issuer,
+ entityIdPrefix: entityIdPrefix,
+ groupConfiguration: {
+ groupClaim: groupClaim,
+ groupEntityType: groupEntityType,
+ },
+ identityTokenOnly: {
+ principalIdClaim: principalIdClaim,
+ },
+ },
+ },
+ policyStore: policyStore,
+ });
+
+ // THEN
+ Template.fromStack(stack).hasResourceProperties('AWS::VerifiedPermissions::IdentitySource', {
+ Configuration: {
+ OpenIdConnectConfiguration: {
+ Issuer: issuer,
+ EntityIdPrefix: entityIdPrefix,
+ GroupConfiguration: {
+ GroupClaim: groupClaim,
+ GroupEntityType: groupEntityType,
+ },
+ TokenSelection: {
+ IdentityTokenOnly: {
+ ClientIds: [],
+ PrincipalIdClaim: principalIdClaim,
+ },
+ },
+ },
+ },
+ PolicyStoreId: {
+ 'Fn::GetAtt': [policyStoreLogicalId, 'PolicyStoreId'],
+ },
+ });
+ });
+
+ test('Creating Identity Source with OIDC and token selection = access token and identity token - should throw', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ expect(() => {
+ new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ openIdConnectConfiguration: {
+ issuer: 'https://test.com',
+ entityIdPrefix: 'prefix',
+ groupConfiguration: {
+ groupClaim: 'group',
+ groupEntityType: 'GroupType',
+ },
+ accessTokenOnly: {
+ audiences: ['testAudience'],
+ principalIdClaim: 'sub',
+ },
+ identityTokenOnly: {
+ clientIds: [],
+ principalIdClaim: 'sub',
+ },
+ },
+ },
+ policyStore: policyStore,
+ principalEntityType: 'TestType',
+ });
+ }).toThrow('Exactly one token selection method between accessTokenOnly and identityTokenOnly must be defined');
+ });
+
+ test('Creating Identity Source with OIDC and without token selection - should throw', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ expect(() => {
+ new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ openIdConnectConfiguration: {
+ issuer: 'https://test.com',
+ entityIdPrefix: 'prefix',
+ groupConfiguration: {
+ groupClaim: 'group',
+ groupEntityType: 'GroupType',
+ },
+ },
+ },
+ policyStore: policyStore,
+ principalEntityType: 'TestType',
+ });
+ }).toThrow('One token selection method between accessTokenOnly and identityTokenOnly must be defined');
+ });
+});
+
+
+describe('Limit cases tests', () => {
+ test('Creating Identity Source without Cognito and OIDC configurations - should throw', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ expect(() => {
+ new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ },
+ policyStore: policyStore,
+ principalEntityType: 'TestType',
+ });
+ }).toThrow('One Identity provider configuration between cognitoUserPoolConfiguration and openIdConnectConfiguration must be defined');
+ });
+ test('Creating Identity Source with both Cognito and OIDC configurations - should throw', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const userPool = new UserPool(stack, 'UserPool');
+ const policyStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.OFF,
+ },
+ });
+ expect(() => {
+ new IdentitySource(stack, 'IdentitySource', {
+ configuration: {
+ cognitoUserPoolConfiguration: {
+ userPool: userPool,
+ },
+ openIdConnectConfiguration: {
+ issuer: 'https://test.com',
+ entityIdPrefix: 'prefix',
+ groupConfiguration: {
+ groupClaim: 'group',
+ groupEntityType: 'GroupType',
+ },
+ accessTokenOnly: {
+ audiences: ['testAudience'],
+ principalIdClaim: 'sub',
+ },
+ },
+ },
+ policyStore: policyStore,
+ principalEntityType: 'TestType',
+ });
+ }).toThrow('Only one between cognitoUserPoolConfiguration or openIdConnectConfiguration must be defined');
+ });
+});
\ No newline at end of file
diff --git a/test/policy-store.test.ts b/test/policy-store.test.ts
index 0b71ab7..29b87f8 100644
--- a/test/policy-store.test.ts
+++ b/test/policy-store.test.ts
@@ -661,4 +661,31 @@ describe('generating schemas from OpenApi specs', () => {
// it should have the eight explicitly defined actions plus the 6 derived from the 'any' definition
expect(Object.keys(schema.PodcastApp.actions).length).toEqual(8 + 6);
});
+ test('generate schema from openApi spec without userGroups', () => {
+ // GIVEN
+ const stack = new Stack(undefined, 'Stack');
+
+ // WHEN
+ const schema = PolicyStore.schemaFromOpenApiSpec(
+ path.join(__dirname, 'podcastappswagger.json'),
+
+ );
+ const pStore = new PolicyStore(stack, 'PolicyStore', {
+ validationSettings: {
+ mode: ValidationSettingsMode.STRICT,
+ },
+ schema: {
+ cedarJson: JSON.stringify(schema),
+ },
+ });
+
+ // THEN
+ expect(pStore.schema?.cedarJson).toBeDefined();
+ expect(Object.keys(schema.PodcastApp.entityTypes)).toStrictEqual([
+ 'User',
+ 'Application',
+ ]);
+ // it should have the eight explicitly defined actions plus the 6 derived from the 'any' definition
+ expect(Object.keys(schema.PodcastApp.actions).length).toEqual(8 + 6);
+ });
});
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 5a318df..23311d6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1204,10 +1204,10 @@ available-typed-arrays@^1.0.7:
dependencies:
possible-typed-array-names "^1.0.0"
-aws-cdk-lib@2.139.0:
- version "2.139.0"
- resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.139.0.tgz#bee393c979d74cf58c087850ce896df145b04776"
- integrity sha512-G9yoc+VFwF10kpgf4omtrAVmUNPeAP708oF5fc7XlRTzoTXMmAdUJW9cRGOMtAkFY83SxiJP0wm8n5Z9tjAdUA==
+aws-cdk-lib@2.148.0:
+ version "2.148.0"
+ resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.148.0.tgz#5d41ed56005fdfc928dfd31bcda268cf9acfcb41"
+ integrity sha512-Pa0pyIHlhnsqtMkPJS3tnptYhoOSNDOgoFurNB4Qfa0vnAkjYQ+JKQkR1tNNr8+UtO9jUfXRklQgjEqlFlrgBA==
dependencies:
"@aws-cdk/asset-awscli-v1" "^2.2.202"
"@aws-cdk/asset-kubectl-v20" "^2.1.2"
@@ -1220,7 +1220,7 @@ aws-cdk-lib@2.139.0:
mime-types "^2.1.35"
minimatch "^3.1.2"
punycode "^2.3.1"
- semver "^7.6.0"
+ semver "^7.6.2"
table "^6.8.2"
yaml "1.10.2"