Skip to content

Commit

Permalink
fix: Count UTF-8 string length by # of chars vs. bytes in message and…
Browse files Browse the repository at this point in the history
… subject args for aws_cognito_user_pool
  • Loading branch information
acwwat committed Mar 30, 2024
1 parent 0d1d7b6 commit 0afa864
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 76 deletions.
124 changes: 96 additions & 28 deletions internal/service/cognitoidp/user_pool_test.go
Expand Up @@ -1500,29 +1500,35 @@ func TestAccCognitoIDPUserPool_withVerificationMessageTemplate(t *testing.T) {
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_cognito_user_pool.test"

emailMessage := "foo {####} bar"
emailMessageByLink := "{##foobar##}"
emailSubject := "foobar {####}"
emailSubjectByLink := "foobar"
smsMessage := "{####} baz"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheckIdentityProvider(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.CognitoIDPServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckUserPoolDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccUserPoolConfig_verificationMessageTemplate(rName),
Config: testAccUserPoolConfig_verificationMessageTemplate(rName, emailMessage, emailMessageByLink, emailSubject, emailSubjectByLink, smsMessage),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckUserPoolExists(ctx, resourceName, &pool),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.default_email_option", "CONFIRM_WITH_LINK"),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_message", "foo {####} bar"),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_message_by_link", "{##foobar##}"),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_subject", "foobar {####}"),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_subject_by_link", "foobar"),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.sms_message", "{####} baz"),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_message", emailMessage),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_message_by_link", emailMessageByLink),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_subject", emailSubject),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_subject_by_link", emailSubjectByLink),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.sms_message", smsMessage),

/* Setting Verification template attributes like EmailMessage, EmailSubject or SmsMessage
will implicitly set EmailVerificationMessage, EmailVerificationSubject and SmsVerificationMessage attributes.
*/
resource.TestCheckResourceAttr(resourceName, "email_verification_message", "foo {####} bar"),
resource.TestCheckResourceAttr(resourceName, "email_verification_subject", "foobar {####}"),
resource.TestCheckResourceAttr(resourceName, "sms_verification_message", "{####} baz"),
resource.TestCheckResourceAttr(resourceName, "email_verification_message", emailMessage),
resource.TestCheckResourceAttr(resourceName, "email_verification_subject", emailSubject),
resource.TestCheckResourceAttr(resourceName, "sms_verification_message", smsMessage),
),
},
{
Expand All @@ -1531,19 +1537,81 @@ func TestAccCognitoIDPUserPool_withVerificationMessageTemplate(t *testing.T) {
ImportStateVerify: true,
},
{
Config: testAccUserPoolConfig_verificationMessageTemplateDefaultEmailOption(rName),
Config: testAccUserPoolConfig_verificationMessageTemplateDefaultEmailOption(rName, emailMessage, emailSubject, smsMessage),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.default_email_option", "CONFIRM_WITH_CODE"),
resource.TestCheckResourceAttr(resourceName, "email_verification_message", "{####} Baz"),
resource.TestCheckResourceAttr(resourceName, "email_verification_subject", "BazBaz {####}"),
resource.TestCheckResourceAttr(resourceName, "sms_verification_message", "{####} BazBazBar?"),
resource.TestCheckResourceAttr(resourceName, "email_verification_message", emailMessage),
resource.TestCheckResourceAttr(resourceName, "email_verification_subject", emailSubject),
resource.TestCheckResourceAttr(resourceName, "sms_verification_message", smsMessage),

/* Setting EmailVerificationMessage, EmailVerificationSubject and SmsVerificationMessage attributes
will implicitly set verification template attributes like EmailMessage, EmailSubject or SmsMessage.
*/
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_message", "{####} Baz"),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_subject", "BazBaz {####}"),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.sms_message", "{####} BazBazBar?"),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_message", emailMessage),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_subject", emailSubject),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.sms_message", smsMessage),
),
},
},
})
}

func TestAccCognitoIDPUserPool_withVerificationMessageTemplateUTF8(t *testing.T) {
ctx := acctest.Context(t)
var pool cognitoidentityprovider.UserPoolType
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_cognito_user_pool.test"

emailMessage := "{####}" + strings.Repeat("あ", 994) // = 1000
emailMessageByLink := "{##foobar##}" + strings.Repeat("い", 988) // = 1000
emailSubject := strings.Repeat("う", 140)
emailSubjectByLink := strings.Repeat("え", 140)
smsMessage := "{####}" + strings.Repeat("お", 134) // = 140

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheckIdentityProvider(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.CognitoIDPServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckUserPoolDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccUserPoolConfig_verificationMessageTemplate(rName, emailMessage, emailMessageByLink, emailSubject, emailSubjectByLink, smsMessage),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckUserPoolExists(ctx, resourceName, &pool),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.default_email_option", "CONFIRM_WITH_LINK"),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_message", emailMessage),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_message_by_link", emailMessageByLink),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_subject", emailSubject),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_subject_by_link", emailSubjectByLink),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.sms_message", smsMessage),

/* Setting Verification template attributes like EmailMessage, EmailSubject or SmsMessage
will implicitly set EmailVerificationMessage, EmailVerificationSubject and SmsVerificationMessage attributes.
*/
resource.TestCheckResourceAttr(resourceName, "email_verification_message", emailMessage),
resource.TestCheckResourceAttr(resourceName, "email_verification_subject", emailSubject),
resource.TestCheckResourceAttr(resourceName, "sms_verification_message", smsMessage),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccUserPoolConfig_verificationMessageTemplateDefaultEmailOption(rName, emailMessage, emailSubject, smsMessage),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.default_email_option", "CONFIRM_WITH_CODE"),
resource.TestCheckResourceAttr(resourceName, "email_verification_message", emailMessage),
resource.TestCheckResourceAttr(resourceName, "email_verification_subject", emailSubject),
resource.TestCheckResourceAttr(resourceName, "sms_verification_message", smsMessage),

/* Setting EmailVerificationMessage, EmailVerificationSubject and SmsVerificationMessage attributes
will implicitly set verification template attributes like EmailMessage, EmailSubject or SmsMessage.
*/
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_message", emailMessage),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.email_subject", emailSubject),
resource.TestCheckResourceAttr(resourceName, "verification_message_template.0.sms_message", smsMessage),
),
},
},
Expand Down Expand Up @@ -2657,7 +2725,7 @@ resource "aws_cognito_user_pool" "test" {
`, name)
}

func testAccUserPoolConfig_verificationMessageTemplate(name string) string {
func testAccUserPoolConfig_verificationMessageTemplate(name, emailMessage, emailMessageByLink, emailSubject, emailSubjectByLink, smsMessage string) string {
return fmt.Sprintf(`
resource "aws_cognito_user_pool" "test" {
name = %[1]q
Expand All @@ -2668,30 +2736,30 @@ resource "aws_cognito_user_pool" "test" {
verification_message_template {
default_email_option = "CONFIRM_WITH_LINK"
email_message = "foo {####} bar"
email_message_by_link = "{##foobar##}"
email_subject = "foobar {####}"
email_subject_by_link = "foobar"
sms_message = "{####} baz"
email_message = %[2]q
email_message_by_link = %[3]q
email_subject = %[4]q
email_subject_by_link = %[5]q
sms_message = %[6]q
}
}
`, name)
`, name, emailMessage, emailMessageByLink, emailSubject, emailSubjectByLink, smsMessage)
}

func testAccUserPoolConfig_verificationMessageTemplateDefaultEmailOption(name string) string {
func testAccUserPoolConfig_verificationMessageTemplateDefaultEmailOption(name, emailVerificationMessage, emailVerificationSubject, smsVerificationMessage string) string {
return fmt.Sprintf(`
resource "aws_cognito_user_pool" "test" {
name = %[1]q
email_verification_message = "{####} Baz"
email_verification_subject = "BazBaz {####}"
sms_verification_message = "{####} BazBazBar?"
email_verification_message = %[2]q
email_verification_subject = %[3]q
sms_verification_message = %[4]q
verification_message_template {
default_email_option = "CONFIRM_WITH_CODE"
}
}
`, name)
`, name, emailVerificationMessage, emailVerificationSubject, smsVerificationMessage)
}

func testAccUserPoolConfig_update(name string, mfaconfig, smsAuthMsg string) string {
Expand Down
100 changes: 56 additions & 44 deletions internal/service/cognitoidp/validate.go
Expand Up @@ -5,6 +5,7 @@ package cognitoidp

import (
"fmt"
"unicode/utf8"

"github.com/YakDriver/regexache"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
Expand Down Expand Up @@ -44,12 +45,13 @@ func validUserGroupName(v interface{}, k string) (ws []string, es []error) {

func validUserPoolEmailVerificationMessage(v interface{}, k string) (ws []string, es []error) {
value := v.(string)
if len(value) < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 characters", k))
count := utf8.RuneCountInString(value)
if count < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 UTF-8 characters", k))
}

if len(value) > 20000 {
es = append(es, fmt.Errorf("%q cannot be longer than 20000 characters", k))
if count > 20000 {
es = append(es, fmt.Errorf("%q cannot be longer than 20000 UTF-8 characters", k))
}

if !regexache.MustCompile(`[\p{L}\p{M}\p{S}\p{N}\p{P}\s*]*\{####\}[\p{L}\p{M}\p{S}\p{N}\p{P}\s*]*`).MatchString(value) {
Expand All @@ -60,12 +62,13 @@ func validUserPoolEmailVerificationMessage(v interface{}, k string) (ws []string

func validUserPoolEmailVerificationSubject(v interface{}, k string) (ws []string, es []error) {
value := v.(string)
if len(value) < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 characters", k))
count := utf8.RuneCountInString(value)
if count < 1 {
es = append(es, fmt.Errorf("%q cannot be less than 1 UTF-8 characters", k))
}

if len(value) > 140 {
es = append(es, fmt.Errorf("%q cannot be longer than 140 characters", k))
if count > 140 {
es = append(es, fmt.Errorf("%q cannot be longer than 140 UTF-8 characters", k))
}

if !regexache.MustCompile(`[\p{L}\p{M}\p{S}\p{N}\p{P}\s]+`).MatchString(value) {
Expand All @@ -84,12 +87,13 @@ func validUserPoolID(v interface{}, k string) (ws []string, es []error) {

func validUserPoolInviteTemplateEmailMessage(v interface{}, k string) (ws []string, es []error) {
value := v.(string)
if len(value) < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 characters", k))
count := utf8.RuneCountInString(value)
if count < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 UTF-8 characters", k))
}

if len(value) > 20000 {
es = append(es, fmt.Errorf("%q cannot be longer than 20000 characters", k))
if count > 20000 {
es = append(es, fmt.Errorf("%q cannot be longer than 20000 UTF-8 characters", k))
}

if !regexache.MustCompile(`[\p{L}\p{M}\p{S}\p{N}\p{P}\s*]*\{####\}[\p{L}\p{M}\p{S}\p{N}\p{P}\s*]*`).MatchString(value) {
Expand All @@ -104,12 +108,13 @@ func validUserPoolInviteTemplateEmailMessage(v interface{}, k string) (ws []stri

func validUserPoolInviteTemplateSMSMessage(v interface{}, k string) (ws []string, es []error) {
value := v.(string)
if len(value) < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 characters", k))
count := utf8.RuneCountInString(value)
if count < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 UTF-8 characters", k))
}

if len(value) > 140 {
es = append(es, fmt.Errorf("%q cannot be longer than 140 characters", k))
if count > 140 {
es = append(es, fmt.Errorf("%q cannot be longer than 140 UTF-8 characters", k))
}

if !regexache.MustCompile(`.*\{####\}.*`).MatchString(value) {
Expand Down Expand Up @@ -140,12 +145,13 @@ func validUserPoolSchemaName(v interface{}, k string) (ws []string, es []error)

func validUserPoolSMSAuthenticationMessage(v interface{}, k string) (ws []string, es []error) {
value := v.(string)
if len(value) < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 characters", k))
count := utf8.RuneCountInString(value)
if count < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 UTF-8 characters", k))
}

if len(value) > 140 {
es = append(es, fmt.Errorf("%q cannot be longer than 140 characters", k))
if count > 140 {
es = append(es, fmt.Errorf("%q cannot be longer than 140 UTF-8 characters", k))
}

if !regexache.MustCompile(`.*\{####\}.*`).MatchString(value) {
Expand All @@ -156,12 +162,13 @@ func validUserPoolSMSAuthenticationMessage(v interface{}, k string) (ws []string

func validUserPoolSMSVerificationMessage(v interface{}, k string) (ws []string, es []error) {
value := v.(string)
if len(value) < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 characters", k))
count := utf8.RuneCountInString(value)
if count < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 UTF-8 characters", k))
}

if len(value) > 140 {
es = append(es, fmt.Errorf("%q cannot be longer than 140 characters", k))
if count > 140 {
es = append(es, fmt.Errorf("%q cannot be longer than 140 UTF-8 characters", k))
}

if !regexache.MustCompile(`.*\{####\}.*`).MatchString(value) {
Expand All @@ -172,12 +179,13 @@ func validUserPoolSMSVerificationMessage(v interface{}, k string) (ws []string,

func validUserPoolTemplateEmailMessage(v interface{}, k string) (ws []string, es []error) {
value := v.(string)
if len(value) < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 characters", k))
count := utf8.RuneCountInString(value)
if count < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 UTF-8 characters", k))
}

if len(value) > 20000 {
es = append(es, fmt.Errorf("%q cannot be longer than 20000 characters", k))
if count > 20000 {
es = append(es, fmt.Errorf("%q cannot be longer than 20000 UTF-8 characters", k))
}

if !regexache.MustCompile(`[\p{L}\p{M}\p{S}\p{N}\p{P}\s*]*\{####\}[\p{L}\p{M}\p{S}\p{N}\p{P}\s*]*`).MatchString(value) {
Expand All @@ -188,12 +196,13 @@ func validUserPoolTemplateEmailMessage(v interface{}, k string) (ws []string, es

func validUserPoolTemplateEmailMessageByLink(v interface{}, k string) (ws []string, es []error) {
value := v.(string)
if len(value) < 1 {
es = append(es, fmt.Errorf("%q cannot be less than 1 character", k))
count := utf8.RuneCountInString(value)
if count < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 UTF-8 characters", k))
}

if len(value) > 20000 {
es = append(es, fmt.Errorf("%q cannot be longer than 20000 characters", k))
if count > 20000 {
es = append(es, fmt.Errorf("%q cannot be longer than 20000 UTF-8 characters", k))
}

if !regexache.MustCompile(`[\p{L}\p{M}\p{S}\p{N}\p{P}\s*]*\{##[\p{L}\p{M}\p{S}\p{N}\p{P}\s*]*##\}[\p{L}\p{M}\p{S}\p{N}\p{P}\s*]*`).MatchString(value) {
Expand All @@ -204,12 +213,13 @@ func validUserPoolTemplateEmailMessageByLink(v interface{}, k string) (ws []stri

func validUserPoolTemplateEmailSubject(v interface{}, k string) (ws []string, es []error) {
value := v.(string)
if len(value) < 1 {
es = append(es, fmt.Errorf("%q cannot be less than 1 character", k))
count := utf8.RuneCountInString(value)
if count < 1 {
es = append(es, fmt.Errorf("%q cannot be less than 1 UTF-8 character", k))
}

if len(value) > 140 {
es = append(es, fmt.Errorf("%q cannot be longer than 140 characters", k))
if count > 140 {
es = append(es, fmt.Errorf("%q cannot be longer than 140 UTF-8 characters", k))
}

if !regexache.MustCompile(`[\p{L}\p{M}\p{S}\p{N}\p{P}\s]+`).MatchString(value) {
Expand All @@ -220,12 +230,13 @@ func validUserPoolTemplateEmailSubject(v interface{}, k string) (ws []string, es

func validUserPoolTemplateEmailSubjectByLink(v interface{}, k string) (ws []string, es []error) {
value := v.(string)
if len(value) < 1 {
es = append(es, fmt.Errorf("%q cannot be less than 1 character", k))
count := utf8.RuneCountInString(value)
if count < 1 {
es = append(es, fmt.Errorf("%q cannot be less than 1 UTF-8 character", k))
}

if len(value) > 140 {
es = append(es, fmt.Errorf("%q cannot be longer than 140 characters", k))
if count > 140 {
es = append(es, fmt.Errorf("%q cannot be longer than 140 UTF-8 characters", k))
}

if !regexache.MustCompile(`[\p{L}\p{M}\p{S}\p{N}\p{P}\s]+`).MatchString(value) {
Expand All @@ -236,12 +247,13 @@ func validUserPoolTemplateEmailSubjectByLink(v interface{}, k string) (ws []stri

func validUserPoolTemplateSMSMessage(v interface{}, k string) (ws []string, es []error) {
value := v.(string)
if len(value) < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 characters", k))
count := utf8.RuneCountInString(value)
if count < 6 {
es = append(es, fmt.Errorf("%q cannot be less than 6 UTF-8 characters", k))
}

if len(value) > 140 {
es = append(es, fmt.Errorf("%q cannot be longer than 140 characters", k))
if count > 140 {
es = append(es, fmt.Errorf("%q cannot be longer than 140 UTF-8 characters", k))
}

if !regexache.MustCompile(`.*\{####\}.*`).MatchString(value) {
Expand Down

0 comments on commit 0afa864

Please sign in to comment.