Skip to content

Commit

Permalink
azuread_application Allow URNs to be specified for web redirect URIs (#…
Browse files Browse the repository at this point in the history
…577)

Resolves: #575
  • Loading branch information
manicminer authored Sep 16, 2021
1 parent 803de44 commit 9e98fdc
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 43 deletions.
6 changes: 3 additions & 3 deletions docs/resources/application.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ The following arguments are supported:

`public_client` block supports the following:

* `redirect_uris` - (Optional) A set of URLs where user tokens are sent for sign-in, or the redirect URIs where OAuth 2.0 authorization codes and access tokens are sent.
* `redirect_uris` - (Optional) A set of URLs where user tokens are sent for sign-in, or the redirect URIs where OAuth 2.0 authorization codes and access tokens are sent. Must be a valid `https` or `ms-appx-web` URL.

---

Expand All @@ -276,7 +276,7 @@ The following arguments are supported:

`single_page_application` block supports the following:

* `redirect_uris` - (Optional) A set of URLs where user tokens are sent for sign-in, or the redirect URIs where OAuth 2.0 authorization codes and access tokens are sent.
* `redirect_uris` - (Optional) A set of URLs where user tokens are sent for sign-in, or the redirect URIs where OAuth 2.0 authorization codes and access tokens are sent. Must be a valid `https` URL.

---

Expand All @@ -285,7 +285,7 @@ The following arguments are supported:
* `homepage_url` - (Optional) Home page or landing page of the application.
* `implicit_grant` - (Optional) An `implicit_grant` block as documented above.
* `logout_url` - (Optional) The URL that will be used by Microsoft's authorization service to sign out a user using front-channel, back-channel or SAML logout protocols.
* `redirect_uris` - (Optional) A set of URLs where user tokens are sent for sign-in, or the redirect URIs where OAuth 2.0 authorization codes and access tokens are sent.
* `redirect_uris` - (Optional) A set of URLs where user tokens are sent for sign-in, or the redirect URIs where OAuth 2.0 authorization codes and access tokens are sent. Must be a valid `http` URL or a URN.

---

Expand Down
14 changes: 7 additions & 7 deletions internal/services/applications/application_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ func applicationResource() *schema.Resource {
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateDiagFunc: validate.IsAppURI,
ValidateDiagFunc: validate.IsAppUri,
},
},

Expand Down Expand Up @@ -370,7 +370,7 @@ func applicationResource() *schema.Resource {
MaxItems: 256,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateDiagFunc: validate.IsRedirectURI,
ValidateDiagFunc: validate.IsRedirectUriFunc(false),
},
},
},
Expand Down Expand Up @@ -447,7 +447,7 @@ func applicationResource() *schema.Resource {
MaxItems: 256,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateDiagFunc: validate.IsRedirectURI,
ValidateDiagFunc: validate.IsRedirectUriFunc(false),
},
},
},
Expand Down Expand Up @@ -486,14 +486,14 @@ func applicationResource() *schema.Resource {
Description: "Home page or landing page of the application",
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: validate.IsHTTPOrHTTPSURL,
ValidateDiagFunc: validate.IsHttpOrHttpsUrl,
},

"logout_url": {
Description: "The URL that will be used by Microsoft's authorization service to sign out a user using front-channel, back-channel or SAML logout protocols",
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: validate.IsLogoutURL,
ValidateDiagFunc: validate.IsLogoutUrl,
},

"redirect_uris": {
Expand All @@ -503,7 +503,7 @@ func applicationResource() *schema.Resource {
MaxItems: 256,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateDiagFunc: validate.IsRedirectURI,
ValidateDiagFunc: validate.IsRedirectUriFunc(true),
},
},

Expand Down Expand Up @@ -664,7 +664,7 @@ func applicationResourceCustomizeDiff(ctx context.Context, diff *schema.Resource
}
// urn scheme not supported with personal account sign-ins
for _, v := range identifierUris {
if diags := validate.IsURIFunc([]string{"http", "https", "api", "ms-appx"}, false, false)(v, cty.Path{}); diags.HasError() {
if diags := validate.IsUriFunc([]string{"http", "https", "api", "ms-appx"}, false, false)(v, cty.Path{}); diags.HasError() {
return fmt.Errorf("`identifier_uris` is invalid. The URN scheme is not supported when `sign_in_audience` is %q or %q",
msgraph.SignInAudienceAzureADandPersonalMicrosoftAccount, msgraph.SignInAudiencePersonalMicrosoftAccount)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,7 @@ resource "azuread_application" "test" {
redirect_uris = [
"https://app.hashitown-%[1]d.com/",
"https://classic.hashitown-%[1]d.com/",
"urn:ietf:wg:oauth:2.0:oob",
]
implicit_grant {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ func ResourceApplicationInstanceResourceV0() *schema.Resource {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateDiagFunc: validate.IsHTTPOrHTTPSURL,
ValidateDiagFunc: validate.IsHttpOrHttpsUrl,
ConflictsWith: []string{"web.0.homepage_url"},
Deprecated: "[NOTE] This attribute will be replaced by a new attribute `homepage_url` in the `web` block in version 2.0 of the AzureAD provider",
},
Expand All @@ -205,14 +205,14 @@ func ResourceApplicationInstanceResourceV0() *schema.Resource {
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateDiagFunc: validate.IsAppURI,
ValidateDiagFunc: validate.IsAppUri,
},
},

"logout_url": {
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: validate.IsHTTPOrHTTPSURL,
ValidateDiagFunc: validate.IsHttpOrHttpsUrl,
Computed: true,
ConflictsWith: []string{"web.0.logout_url"},
Deprecated: "[NOTE] This attribute will be moved into the `web` block in version 2.0 of the AzureAD provider",
Expand Down Expand Up @@ -488,14 +488,14 @@ func ResourceApplicationInstanceResourceV0() *schema.Resource {
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"homepage"},
ValidateDiagFunc: validate.IsHTTPOrHTTPSURL,
ValidateDiagFunc: validate.IsHttpOrHttpsUrl,
},

"logout_url": {
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"logout_url"},
ValidateDiagFunc: validate.IsHTTPOrHTTPSURL,
ValidateDiagFunc: validate.IsHttpOrHttpsUrl,
},

"redirect_uris": {
Expand Down
2 changes: 1 addition & 1 deletion internal/services/invitations/invitation_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func invitationResource() *schema.Resource {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateDiagFunc: validate.IsHTTPOrHTTPSURL,
ValidateDiagFunc: validate.IsHttpOrHttpsUrl,
},

"user_email_address": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func servicePrincipalResource() *schema.Resource {
Description: "The URL where the service provider redirects the user to Azure AD to authenticate. Azure AD uses the URL to launch the application from Microsoft 365 or the Azure AD My Apps. When blank, Azure AD performs IdP-initiated sign-on for applications configured with SAML-based single sign-on",
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: validate.IsHTTPOrHTTPSURL,
ValidateDiagFunc: validate.IsHttpOrHttpsUrl,
},

"notes": {
Expand Down
48 changes: 25 additions & 23 deletions internal/validate/uri.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func IsHTTPSURL(i interface{}, path cty.Path) diag.Diagnostics {
return IsURIFunc([]string{"https"}, false, false)(i, path)
func IsAppUri(i interface{}, path cty.Path) diag.Diagnostics {
return IsUriFunc([]string{"http", "https", "api", "ms-appx"}, true, false)(i, path)
}

func IsHTTPOrHTTPSURL(i interface{}, path cty.Path) diag.Diagnostics {
return IsURIFunc([]string{"http", "https"}, false, false)(i, path)
func IsHttpOrHttpsUrl(i interface{}, path cty.Path) diag.Diagnostics {
return IsUriFunc([]string{"http", "https"}, false, false)(i, path)
}

func IsAppURI(i interface{}, path cty.Path) diag.Diagnostics {
return IsURIFunc([]string{"http", "https", "api", "ms-appx"}, true, false)(i, path)
func IsHttpsUrl(i interface{}, path cty.Path) diag.Diagnostics {
return IsUriFunc([]string{"https"}, false, false)(i, path)
}

func IsLogoutURL(i interface{}, path cty.Path) (ret diag.Diagnostics) {
ret = IsURIFunc([]string{"http", "https"}, false, false)(i, path)
func IsLogoutUrl(i interface{}, path cty.Path) (ret diag.Diagnostics) {
ret = IsUriFunc([]string{"http", "https"}, false, false)(i, path)
if len(ret) > 0 {
return
}
Expand All @@ -39,24 +39,26 @@ func IsLogoutURL(i interface{}, path cty.Path) (ret diag.Diagnostics) {
return
}

func IsRedirectURI(i interface{}, path cty.Path) (ret diag.Diagnostics) {
ret = IsURIFunc([]string{"http", "https", "ms-appx-web"}, false, true)(i, path)
if len(ret) > 0 {
return
}
func IsRedirectUriFunc(urnAllowed bool) schema.SchemaValidateDiagFunc {
return func(i interface{}, path cty.Path) (ret diag.Diagnostics) {
ret = IsUriFunc([]string{"http", "https", "ms-appx-web"}, urnAllowed, true)(i, path)
if len(ret) > 0 {
return
}

if len(i.(string)) > 256 {
ret = append(ret, diag.Diagnostic{
Severity: diag.Error,
Summary: "URI must be 256 characters or less",
AttributePath: path,
})
}
if len(i.(string)) > 256 {
ret = append(ret, diag.Diagnostic{
Severity: diag.Error,
Summary: "URI must be 256 characters or less",
AttributePath: path,
})
}

return
return
}
}

func IsURIFunc(validURLSchemes []string, URNAllowed bool, forceTrailingSlash bool) schema.SchemaValidateDiagFunc {
func IsUriFunc(validURLSchemes []string, urnAllowed bool, forceTrailingSlash bool) schema.SchemaValidateDiagFunc {
return func(i interface{}, path cty.Path) (ret diag.Diagnostics) {
v, ok := i.(string)
if !ok {
Expand All @@ -77,7 +79,7 @@ func IsURIFunc(validURLSchemes []string, URNAllowed bool, forceTrailingSlash boo
return
}

if URNAllowed {
if urnAllowed {
parts := strings.Split(v, ":")
if len(parts) >= 3 && parts[0] == "urn" {
return
Expand Down
6 changes: 3 additions & 3 deletions internal/validate/uri_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestIsHTTPSURL(t *testing.T) {

for _, tc := range cases {
t.Run(tc.Url, func(t *testing.T) {
diags := IsHTTPSURL(tc.Url, cty.Path{})
diags := IsHttpsUrl(tc.Url, cty.Path{})

if len(diags) != tc.Errors {
t.Fatalf("Expected URLIsHTTPS to have %d not %d errors for %q", tc.Errors, len(diags), tc.Url)
Expand Down Expand Up @@ -81,7 +81,7 @@ func TestIsHTTPOrHTTPSURL(t *testing.T) {

for _, tc := range cases {
t.Run(tc.Url, func(t *testing.T) {
diags := IsHTTPOrHTTPSURL(tc.Url, cty.Path{})
diags := IsHttpOrHttpsUrl(tc.Url, cty.Path{})

if len(diags) != tc.Errors {
t.Fatalf("Expected URLIsHTTPOrHTTPS to have %d not %d errors for %q", tc.Errors, len(diags), tc.Url)
Expand Down Expand Up @@ -139,7 +139,7 @@ func TestIsAppURI(t *testing.T) {

for _, tc := range cases {
t.Run(tc.Url, func(t *testing.T) {
diags := IsAppURI(tc.Url, cty.Path{})
diags := IsAppUri(tc.Url, cty.Path{})

if len(diags) != tc.Errors {
t.Fatalf("Expected URLIsAppURI to have %d not %d errors for %q", tc.Errors, len(diags), tc.Url)
Expand Down

0 comments on commit 9e98fdc

Please sign in to comment.