Skip to content

Commit

Permalink
NSOF-8039 app: add domain_federation sub resource
Browse files Browse the repository at this point in the history
  • Loading branch information
Chen Peled committed Oct 16, 2023
1 parent d828118 commit 2708be6
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 7 deletions.
9 changes: 9 additions & 0 deletions docs/data-sources/app.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

<a id="nestedatt--domain_federation"></a>
### Nested Schema for `domain_federation`

Read-Only:

- `domain` (String)


<a id="nestedatt--mapped_attributes"></a>
### Nested Schema for `mapped_attributes`

Expand Down
33 changes: 33 additions & 0 deletions docs/resources/app.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
```

<!-- schema generated by tfplugindocs -->
Expand All @@ -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))
Expand All @@ -115,6 +140,14 @@ resource "pfptmeta_app" "app_oidc" {

- `id` (String) The ID of this resource.

<a id="nestedblock--domain_federation"></a>
### Nested Schema for `domain_federation`

Required:

- `domain` (String) Office-365 domain to be federated


<a id="nestedblock--mapped_attributes"></a>
### Nested Schema for `mapped_attributes`

Expand Down
24 changes: 24 additions & 0 deletions examples/resources/pfptmeta_app/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
88 changes: 86 additions & 2 deletions internal/client/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions internal/provider/acc_tests/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down Expand Up @@ -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"),
),
},
{
Expand All @@ -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"),
),
},
},
Expand Down
30 changes: 26 additions & 4 deletions internal/provider/app/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand Down Expand Up @@ -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
}
14 changes: 14 additions & 0 deletions internal/provider/app/data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading

0 comments on commit 2708be6

Please sign in to comment.