|
| 1 | +import { Names, Token } from '@aws-cdk/core'; |
| 2 | +import { Construct } from 'constructs'; |
| 3 | +import { CfnUserPoolIdentityProvider } from '../cognito.generated'; |
| 4 | +import { UserPoolIdentityProviderProps } from './base'; |
| 5 | +import { UserPoolIdentityProviderBase } from './private/user-pool-idp-base'; |
| 6 | + |
| 7 | +/** |
| 8 | + * Properties to initialize UserPoolIdentityProviderSaml. |
| 9 | + */ |
| 10 | +export interface UserPoolIdentityProviderSamlProps extends UserPoolIdentityProviderProps { |
| 11 | + /** |
| 12 | + * The name of the provider. Must be between 3 and 32 characters. |
| 13 | + * |
| 14 | + * @default - the unique ID of the construct |
| 15 | + */ |
| 16 | + readonly name?: string; |
| 17 | + |
| 18 | + /** |
| 19 | + * Identifiers |
| 20 | + * |
| 21 | + * Identifiers can be used to redirect users to the correct IdP in multitenant apps. |
| 22 | + * |
| 23 | + * @default - no identifiers used |
| 24 | + */ |
| 25 | + readonly identifiers?: string[] |
| 26 | + |
| 27 | + /** |
| 28 | + * The SAML metadata. |
| 29 | + */ |
| 30 | + readonly metadata: UserPoolIdentityProviderSamlMetadata; |
| 31 | + |
| 32 | + /** |
| 33 | + * Whether to enable the "Sign-out flow" feature. |
| 34 | + * |
| 35 | + * @default - false |
| 36 | + */ |
| 37 | + readonly idpSignout?: boolean; |
| 38 | +} |
| 39 | + |
| 40 | +/** |
| 41 | + * Metadata types that can be used for a SAML user pool identity provider. |
| 42 | + */ |
| 43 | +export enum UserPoolIdentityProviderSamlMetadataType { |
| 44 | + /** Metadata provided via a URL. */ |
| 45 | + URL = 'url', |
| 46 | + |
| 47 | + /** Metadata provided via the contents of a file. */ |
| 48 | + FILE = 'file', |
| 49 | +} |
| 50 | + |
| 51 | +/** |
| 52 | + * Metadata for a SAML user pool identity provider. |
| 53 | + */ |
| 54 | +export class UserPoolIdentityProviderSamlMetadata { |
| 55 | + |
| 56 | + /** |
| 57 | + * Specify SAML metadata via a URL. |
| 58 | + */ |
| 59 | + public static url(url: string): UserPoolIdentityProviderSamlMetadata { |
| 60 | + return new UserPoolIdentityProviderSamlMetadata(url, UserPoolIdentityProviderSamlMetadataType.URL); |
| 61 | + } |
| 62 | + |
| 63 | + /** |
| 64 | + * Specify SAML metadata via the contents of a file. |
| 65 | + */ |
| 66 | + public static file(fileContent: string): UserPoolIdentityProviderSamlMetadata { |
| 67 | + return new UserPoolIdentityProviderSamlMetadata(fileContent, UserPoolIdentityProviderSamlMetadataType.FILE); |
| 68 | + } |
| 69 | + |
| 70 | + /** |
| 71 | + * Construct the metadata for a SAML identity provider. |
| 72 | + * |
| 73 | + * @param metadataContent A URL hosting SAML metadata, or the content of a file containing SAML metadata. |
| 74 | + * @param metadataType The type of metadata, either a URL or file content. |
| 75 | + */ |
| 76 | + private constructor(public readonly metadataContent: string, public readonly metadataType: UserPoolIdentityProviderSamlMetadataType) { |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +/** |
| 81 | + * Represents a identity provider that integrates with SAML. |
| 82 | + * @resource AWS::Cognito::UserPoolIdentityProvider |
| 83 | + */ |
| 84 | +export class UserPoolIdentityProviderSaml extends UserPoolIdentityProviderBase { |
| 85 | + public readonly providerName: string; |
| 86 | + |
| 87 | + constructor(scope: Construct, id: string, props: UserPoolIdentityProviderSamlProps) { |
| 88 | + super(scope, id, props); |
| 89 | + |
| 90 | + this.validateName(props.name); |
| 91 | + |
| 92 | + const { metadataType, metadataContent } = props.metadata; |
| 93 | + |
| 94 | + const resource = new CfnUserPoolIdentityProvider(this, 'Resource', { |
| 95 | + userPoolId: props.userPool.userPoolId, |
| 96 | + providerName: this.getProviderName(props.name), |
| 97 | + providerType: 'SAML', |
| 98 | + providerDetails: { |
| 99 | + IDPSignout: props.idpSignout ?? false, |
| 100 | + MetadataURL: metadataType === UserPoolIdentityProviderSamlMetadataType.URL ? metadataContent : undefined, |
| 101 | + MetadataFile: metadataType === UserPoolIdentityProviderSamlMetadataType.FILE ? metadataContent : undefined, |
| 102 | + }, |
| 103 | + idpIdentifiers: props.identifiers, |
| 104 | + attributeMapping: super.configureAttributeMapping(), |
| 105 | + }); |
| 106 | + |
| 107 | + this.providerName = super.getResourceNameAttribute(resource.ref); |
| 108 | + } |
| 109 | + |
| 110 | + private getProviderName(name?: string): string { |
| 111 | + if (name) { |
| 112 | + this.validateName(name); |
| 113 | + return name; |
| 114 | + } |
| 115 | + |
| 116 | + const uniqueName = Names.uniqueResourceName(this, { |
| 117 | + maxLength: 32, |
| 118 | + }); |
| 119 | + |
| 120 | + if (uniqueName.length < 3) { |
| 121 | + return `${uniqueName}saml`; |
| 122 | + } |
| 123 | + |
| 124 | + return uniqueName; |
| 125 | + } |
| 126 | + |
| 127 | + private validateName(name?: string) { |
| 128 | + if (name && !Token.isUnresolved(name) && (name.length < 3 || name.length > 32)) { |
| 129 | + throw new Error(`Expected provider name to be between 3 and 32 characters, received ${name} (${name.length} characters)`); |
| 130 | + } |
| 131 | + } |
| 132 | +} |
0 commit comments