diff --git a/docs/data-sources/app.md b/docs/data-sources/app.md
index 235d57c..d6233f8 100644
--- a/docs/data-sources/app.md
+++ b/docs/data-sources/app.md
@@ -35,6 +35,7 @@ output "app" {
- `assigned_members` (List of String) Users and groups which the application is applied to
- `description` (String)
- `direct_sso_login` (String) IDP to use when logging into Proofpoint NaaS directly, while performing SSO login to SP
+- `domain_federation` (List of Object) SSO configuration for Office-365 SP (see [below for nested schema](#nestedatt--domain_federation))
- `enabled` (Boolean)
- `id` (String) The ID of this resource.
- `ip_whitelist` (List of String) Users and groups which the application is applied to
@@ -44,6 +45,14 @@ output "app" {
- `saml` (List of Object) SAML-based app properties (see [below for nested schema](#nestedatt--saml))
- `visible` (Boolean) Application visibility, defining whether to display application to user or not
+
+### Nested Schema for `domain_federation`
+
+Read-Only:
+
+- `domain` (String)
+
+
### Nested Schema for `mapped_attributes`
diff --git a/docs/resources/app.md b/docs/resources/app.md
index 994d0b8..5984163 100644
--- a/docs/resources/app.md
+++ b/docs/resources/app.md
@@ -89,6 +89,30 @@ resource "pfptmeta_app" "app_oidc" {
initiate_login_url = "https://intial-login.myApp.example.com"
}
}
+
+resource "pfptmeta_app" "app_office365" {
+ name = "office-365 app name"
+ description = "office-365 app description"
+ enabled = true
+ assigned_members = ["usr-abcd1234"]
+ protocol = "SAML"
+
+ saml {
+ audience_uri = "https://audience.myApp.com"
+ sso_acs_url = "https://login.myApp.example.com"
+ destination = "https://login.myApp.example.com"
+ recipient = "https://login.myApp.example.com"
+ default_relay_state = "https://relay.myApp.com"
+ subject_name_id_attribute = "email"
+ subject_name_id_format = "emailAddress"
+ signature_algorithm = "RSA-SHA256"
+ digest_algorithm = "SHA256"
+ }
+
+ domain_federation {
+ domain = "my-office365-domain.com"
+ }
+}
```
@@ -104,6 +128,7 @@ resource "pfptmeta_app" "app_oidc" {
- `assigned_members` (Set of String) Users and groups which the application is applied to
- `description` (String)
- `direct_sso_login` (String) IDP to use when logging into Proofpoint NaaS directly, while performing SSO login to SP
+- `domain_federation` (Block List, Max: 1) SSO configuration for Office-365 SP (see [below for nested schema](#nestedblock--domain_federation))
- `enabled` (Boolean)
- `ip_whitelist` (Set of String) List of IPs allowed to be authenticated by the application
- `mapped_attributes` (Block List, Max: 15) User attributes to map and return to SP upon successful SAML assertion/OIDC authorization (see [below for nested schema](#nestedblock--mapped_attributes))
@@ -115,6 +140,14 @@ resource "pfptmeta_app" "app_oidc" {
- `id` (String) The ID of this resource.
+
+### Nested Schema for `domain_federation`
+
+Required:
+
+- `domain` (String) Office-365 domain to be federated
+
+
### Nested Schema for `mapped_attributes`
diff --git a/examples/resources/pfptmeta_app/resource.tf b/examples/resources/pfptmeta_app/resource.tf
index ae88b44..b930c64 100644
--- a/examples/resources/pfptmeta_app/resource.tf
+++ b/examples/resources/pfptmeta_app/resource.tf
@@ -73,4 +73,28 @@ resource "pfptmeta_app" "app_oidc" {
scopes = ["openid", "profile", "email"]
initiate_login_url = "https://intial-login.myApp.example.com"
}
+}
+
+resource "pfptmeta_app" "app_office365" {
+ name = "office-365 app name"
+ description = "office-365 app description"
+ enabled = true
+ assigned_members = ["usr-abcd1234"]
+ protocol = "SAML"
+
+ saml {
+ audience_uri = "https://audience.myApp.com"
+ sso_acs_url = "https://login.myApp.example.com"
+ destination = "https://login.myApp.example.com"
+ recipient = "https://login.myApp.example.com"
+ default_relay_state = "https://relay.myApp.com"
+ subject_name_id_attribute = "email"
+ subject_name_id_format = "emailAddress"
+ signature_algorithm = "RSA-SHA256"
+ digest_algorithm = "SHA256"
+ }
+
+ domain_federation {
+ domain = "my-office365-domain.com"
+ }
}
\ No newline at end of file
diff --git a/internal/client/app.go b/internal/client/app.go
index 731e0bf..8728e23 100644
--- a/internal/client/app.go
+++ b/internal/client/app.go
@@ -43,6 +43,10 @@ type AppMappedAttributes struct {
FilterValue *string `json:"filter_value"`
}
+type AppDomainFederation struct {
+ Domain string `json:"domain"`
+}
+
type App struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
@@ -56,6 +60,7 @@ type App struct {
Saml *AppSaml `json:"saml,omitempty"`
Oidc *AppOidc `json:"oidc,omitempty"`
MappedAttributes []AppMappedAttributes `json:"mapped_attributes,omitempty"`
+ DomainFederation *AppDomainFederation `json:"domain_federation,omitempty"`
}
func NewApp(d *schema.ResourceData) *App {
@@ -188,6 +193,24 @@ func NewAppMappedAttr(d *schema.ResourceData) *[]AppMappedAttributes {
return &res
}
+func NewAppDomainFederation(protocol string, d *schema.ResourceData) (*AppDomainFederation, error) {
+ res := &AppDomainFederation{}
+ df, exists := d.GetOk("domain_federation")
+ if !exists {
+ return nil, nil
+ }
+ if protocol != "SAML" {
+ return nil, fmt.Errorf("Domain federation with sso protocol %s is not supported", protocol)
+ }
+ domain_fed := df.([]interface{})
+ if len(domain_fed) != 1 {
+ return nil, nil
+ }
+ domain_federation_conf := domain_fed[0].(map[string]interface{})
+ res.Domain = domain_federation_conf["domain"].(string)
+ return res, nil
+}
+
func parseApp(resp []byte) (*App, error) {
app := &App{}
err := json.Unmarshal(resp, app)
@@ -224,6 +247,15 @@ func parseAppMappedAttributes(resp []byte) ([]AppMappedAttributes, error) {
return *app_mapped_attrs, nil
}
+func parseAppDomainFederation(resp []byte) (*AppDomainFederation, error) {
+ app_domain_federation := &AppDomainFederation{}
+ err := json.Unmarshal(resp, app_domain_federation)
+ if err != nil {
+ return nil, fmt.Errorf("could not parse app domain federation response: %v", err)
+ }
+ return app_domain_federation, nil
+}
+
func UpdateAppProto(ctx context.Context, c *Client, app *App, saml []byte, oidc []byte,
delete_on_failure bool) (*App, error) {
if saml != nil {
@@ -264,6 +296,20 @@ func UpdateAppProto(ctx context.Context, c *Client, app *App, saml []byte, oidc
return app, nil
}
+func UpdateAppDomainFederation(ctx context.Context, c *Client, app_id string,
+ domainFed []byte) (*AppDomainFederation, error) {
+ DomainFedUrl := fmt.Sprintf("%s/%s/%s/domain_federation", c.BaseURL, appEndpoint, app_id)
+ resp, err := c.Patch(ctx, DomainFedUrl, domainFed)
+ if err != nil {
+ return nil, err
+ }
+ domain_federation_resp, err := parseAppDomainFederation(resp)
+ if err != nil {
+ return nil, err
+ }
+ return domain_federation_resp, nil
+}
+
func UpdateAppMappedAttrs(ctx context.Context, c *Client, app_id string,
mappedAttrs []byte) ([]AppMappedAttributes, error) {
MappedAttrsUrl := fmt.Sprintf("%s/%s/%s/attribute_mapping", c.BaseURL, appEndpoint, app_id)
@@ -297,7 +343,7 @@ func MarshalAppProtocol(protocol string, saml *AppSaml, oidc *AppOidc) ([]byte,
}
func CreateApp(ctx context.Context, c *Client, app *App, saml *AppSaml, oidc *AppOidc,
- mappedAttrs *[]AppMappedAttributes) (*App, error) {
+ mappedAttrs *[]AppMappedAttributes, domainFed *AppDomainFederation) (*App, error) {
body, err := json.Marshal(app)
if err != nil {
return nil, fmt.Errorf("could not convert app to json: %v", err)
@@ -330,11 +376,26 @@ func CreateApp(ctx context.Context, c *Client, app *App, saml *AppSaml, oidc *Ap
app_resp.MappedAttributes = mappedAttrs
}
}
+ if domainFed != nil {
+ domain_federation_body, err := json.Marshal(domainFed)
+ if err != nil {
+ DeleteApp(ctx, c, app_resp.ID)
+ return nil, fmt.Errorf("could not convert app domain federation to json: %v", err)
+ }
+ if domain_federation_body != nil {
+ domainFed, err := UpdateAppDomainFederation(ctx, c, app_resp.ID, domain_federation_body)
+ if err != nil {
+ DeleteApp(ctx, c, app_resp.ID)
+ return nil, err
+ }
+ app_resp.DomainFederation = domainFed
+ }
+ }
return UpdateAppProto(ctx, c, app_resp, saml_body, oidc_body, true)
}
func UpdateApp(ctx context.Context, c *Client, appID string, app *App, saml *AppSaml, oidc *AppOidc,
- mappedAttrs *[]AppMappedAttributes) (*App, error) {
+ mappedAttrs *[]AppMappedAttributes, domainFed *AppDomainFederation) (*App, error) {
var empty_proto string
proto := app.Protocol
app.Protocol = empty_proto
@@ -369,6 +430,19 @@ func UpdateApp(ctx context.Context, c *Client, appID string, app *App, saml *App
app_resp.MappedAttributes = mappedAttrs
}
}
+ if domainFed != nil {
+ domain_federation_body, err := json.Marshal(domainFed)
+ if err != nil {
+ return nil, fmt.Errorf("could not convert app domain federation to json: %v", err)
+ }
+ if domain_federation_body != nil {
+ domainFed, err := UpdateAppDomainFederation(ctx, c, app_resp.ID, domain_federation_body)
+ if err != nil {
+ return nil, err
+ }
+ app_resp.DomainFederation = domainFed
+ }
+ }
app.Protocol = proto
return UpdateAppProto(ctx, c, app_resp, saml_body, oidc_body, false)
}
@@ -394,6 +468,16 @@ func GetApp(ctx context.Context, c *Client, appID string, protocol string) (*App
return nil, err
}
app_resp.Saml = saml_resp
+ DomainFedUrl := fmt.Sprintf("%s/%s/%s/domain_federation", c.BaseURL, appEndpoint, app_resp.ID)
+ resp, err = c.Get(ctx, DomainFedUrl, nil)
+ if err != nil {
+ return nil, err
+ }
+ domain_federation_resp, err := parseAppDomainFederation(resp)
+ if err != nil {
+ return nil, err
+ }
+ app_resp.DomainFederation = domain_federation_resp
} else if protocol == "OIDC" {
oidcUrl := fmt.Sprintf("%s/%s/%s/oidc", c.BaseURL, appEndpoint, app_resp.ID)
resp, err = c.Get(ctx, oidcUrl, nil)
diff --git a/internal/provider/acc_tests/app_test.go b/internal/provider/acc_tests/app_test.go
index a3ae694..bf7b852 100644
--- a/internal/provider/acc_tests/app_test.go
+++ b/internal/provider/acc_tests/app_test.go
@@ -33,6 +33,10 @@ resource "pfptmeta_app" "app_saml" {
signature_algorithm = "RSA-SHA256"
digest_algorithm = "SHA256"
}
+
+ domain_federation {
+ domain = "my-domain.com"
+ }
}
data "pfptmeta_app" "app_saml" {
@@ -170,6 +174,7 @@ func TestAccDataSourceAppSaml(t *testing.T) {
resource.TestCheckResourceAttr("pfptmeta_app.app_saml", "saml.0.subject_name_id_format", "emailAddress"),
resource.TestCheckResourceAttr("pfptmeta_app.app_saml", "saml.0.signature_algorithm", "RSA-SHA256"),
resource.TestCheckResourceAttr("pfptmeta_app.app_saml", "saml.0.digest_algorithm", "SHA256"),
+ resource.TestCheckResourceAttr("pfptmeta_app.app_saml", "domain_federation.0.domain", "my-domain.com"),
),
},
{
@@ -190,6 +195,7 @@ func TestAccDataSourceAppSaml(t *testing.T) {
resource.TestCheckResourceAttr("data.pfptmeta_app.app_saml", "saml.0.subject_name_id_format", "emailAddress"),
resource.TestCheckResourceAttr("data.pfptmeta_app.app_saml", "saml.0.signature_algorithm", "RSA-SHA256"),
resource.TestCheckResourceAttr("data.pfptmeta_app.app_saml", "saml.0.digest_algorithm", "SHA256"),
+ resource.TestCheckResourceAttr("data.pfptmeta_app.app_saml", "domain_federation.0.domain", "my-domain.com"),
),
},
},
diff --git a/internal/provider/app/common.go b/internal/provider/app/common.go
index 4467911..d754732 100644
--- a/internal/provider/app/common.go
+++ b/internal/provider/app/common.go
@@ -9,7 +9,7 @@ import (
"net/http"
)
-var excludedKeys = []string{"id", "saml", "oidc", "mapped_attributes"}
+var excludedKeys = []string{"id", "saml", "oidc", "mapped_attributes", "domain_federation"}
const (
description = "Application for configuring SSO by SPs based on SAML or OIDC protocols"
@@ -33,7 +33,8 @@ const (
samlSsoUrleDesc = "SAML url to be configured at SP side"
samlAuthnCtxClassDesc = "SAML authentication context class to be configured at the SP side"
samlDefRelayStateDesc = "SAML default relay state URL to use after successful assertion"
- wsFedDesc = "SSO configuration for Office365 SP"
+ domainFedDesc = "SSO configuration for Office-365 SP"
+ domainFedDomainDesc = "Office-365 domain to be federated"
oidcDesc = "OIDC-based app properties"
oidcSigninRedUrlsDesc = "Redirect URLs which are allowed after successful authorization"
oidcGrantTypesDesc = "OIDC-supported access/ID token grant types"
@@ -75,13 +76,19 @@ func appCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) di
app_body := client.NewApp(d)
var saml_body *client.AppSaml
var oidc_body *client.AppOidc
+ var domain_fed_body *client.AppDomainFederation
if app_body.Protocol == "SAML" {
saml_body = client.NewAppSaml(d)
} else if app_body.Protocol == "OIDC" {
oidc_body = client.NewAppOidc(d)
}
+ domain_fed_body, err := client.NewAppDomainFederation(app_body.Protocol, d)
+ if err != nil {
+ return diag.FromErr(err)
+ }
mapped_attrs_body := client.NewAppMappedAttr(d)
- a, err := client.CreateApp(ctx, c, app_body, saml_body, oidc_body, mapped_attrs_body)
+ a, err := client.CreateApp(ctx, c, app_body, saml_body, oidc_body,
+ mapped_attrs_body, domain_fed_body)
if err != nil {
return diag.FromErr(err)
}
@@ -95,13 +102,19 @@ func appUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) di
app_body := client.NewApp(d)
var saml_body *client.AppSaml
var oidc_body *client.AppOidc
+ var domain_fed_body *client.AppDomainFederation
if app_body.Protocol == "SAML" {
saml_body = client.NewAppSaml(d)
} else if app_body.Protocol == "OIDC" {
oidc_body = client.NewAppOidc(d)
}
+ domain_fed_body, err := client.NewAppDomainFederation(app_body.Protocol, d)
+ if err != nil {
+ return diag.FromErr(err)
+ }
mapped_attrs_body := client.NewAppMappedAttr(d)
- a, err := client.UpdateApp(ctx, c, id, app_body, saml_body, oidc_body, mapped_attrs_body)
+ a, err := client.UpdateApp(ctx, c, id, app_body, saml_body, oidc_body,
+ mapped_attrs_body, domain_fed_body)
if err != nil {
return diag.FromErr(err)
}
@@ -167,5 +180,14 @@ func appToResource(d *schema.ResourceData, a *client.App) diag.Diagnostics {
if err != nil {
return diag.FromErr(err)
}
+ if a.DomainFederation != nil {
+ domainFedToResource := []map[string]interface{}{
+ {"domain": a.DomainFederation.Domain},
+ }
+ err = d.Set("domain_federation", domainFedToResource)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ }
return diags
}
diff --git a/internal/provider/app/data_source.go b/internal/provider/app/data_source.go
index c85643f..e9a0a30 100644
--- a/internal/provider/app/data_source.go
+++ b/internal/provider/app/data_source.go
@@ -130,6 +130,20 @@ func DataSource() *schema.Resource {
},
},
},
+ "domain_federation": {
+ Description: domainFedDesc,
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "domain": {
+ Description: domainFedDomainDesc,
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ },
+ },
"oidc": {
Description: oidcDesc,
Type: schema.TypeList,
diff --git a/internal/provider/app/resource.go b/internal/provider/app/resource.go
index c1a04ab..1bd73a8 100644
--- a/internal/provider/app/resource.go
+++ b/internal/provider/app/resource.go
@@ -152,6 +152,23 @@ func Resource() *schema.Resource {
},
ConflictsWith: []string{"oidc"},
},
+ "domain_federation": {
+ Description: domainFedDesc,
+ Type: schema.TypeList,
+ Optional: true,
+ MaxItems: 1,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "domain": {
+ Description: domainFedDomainDesc,
+ Type: schema.TypeString,
+ Required: true,
+ ValidateDiagFunc: common.ValidatePattern(common.DomainPattern),
+ },
+ },
+ },
+ ConflictsWith: []string{"oidc"},
+ },
"oidc": {
Description: oidcDesc,
Type: schema.TypeList,
@@ -203,7 +220,7 @@ func Resource() *schema.Resource {
},
},
},
- ConflictsWith: []string{"saml"},
+ ConflictsWith: []string{"saml", "domain_federation"},
},
"mapped_attributes": {
Description: MappedAttributesDesc,