diff --git a/access_application.go b/access_application.go index bd872e351..270483cf2 100644 --- a/access_application.go +++ b/access_application.go @@ -16,9 +16,14 @@ type AccessApplicationType string // These constants represent all valid application types. const ( - SelfHosted AccessApplicationType = "self_hosted" - SSH AccessApplicationType = "ssh" - VNC AccessApplicationType = "vnc" + SelfHosted AccessApplicationType = "self_hosted" + SSH AccessApplicationType = "ssh" + VNC AccessApplicationType = "vnc" + Biso AccessApplicationType = "biso" + AppLauncher AccessApplicationType = "app_launcher" + Warp AccessApplicationType = "warp" + Bookmark AccessApplicationType = "bookmark" + Saas AccessApplicationType = "saas" ) // AccessApplication represents an Access application. @@ -39,6 +44,7 @@ type AccessApplication struct { CorsHeaders *AccessApplicationCorsHeaders `json:"cors_headers,omitempty"` CreatedAt *time.Time `json:"created_at,omitempty"` UpdatedAt *time.Time `json:"updated_at,omitempty"` + SaasApplication *SaasApplication `json:"saas_app,omitempty"` AutoRedirectToIdentity bool `json:"auto_redirect_to_identity,omitempty"` SkipInterstitial bool `json:"skip_interstitial,omitempty"` AppLauncherVisible bool `json:"app_launcher_visible,omitempty"` @@ -81,6 +87,32 @@ type AccessApplicationDetailResponse struct { Result AccessApplication `json:"result"` } +type SourceConfig struct { + Name string `json:"name,omitempty"` + NameByIDP map[string]string `json:"name_by_idp,omitempty"` +} + +type SAMLAttributeConfig struct { + Name string `json:"name,omitempty"` + NameFormat string `json:"name_format,omitempty"` + FriendlyName string `json:"friendly_name,omitempty"` + Required bool `json:"required,omitempty"` + Source SourceConfig `json:"source"` +} + +type SaasApplication struct { + AppID string `json:"app_id,omitempty"` + ConsumerServiceUrl string `json:"consumer_service_url,omitempty"` + SPEntityID string `json:"sp_entity_id,omitempty"` + PublicKey string `json:"public_key,omitempty"` + IDPEntityID string `json:"idp_entity_id,omitempty"` + NameIDFormat string `json:"name_id_format,omitempty"` + SSOEndpoint string `json:"sso_endpoint,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + CustomAttributes []SAMLAttributeConfig `json:"custom_attributes,omitempty"` +} + // AccessApplications returns all applications within an account. // // API reference: https://api.cloudflare.com/#access-applications-list-access-applications diff --git a/access_application_test.go b/access_application_test.go index 00c07dd06..8c365a0ba 100644 --- a/access_application_test.go +++ b/access_application_test.go @@ -543,3 +543,148 @@ func TestCreatePrivateAccessApplication(t *testing.T) { assert.Equal(t, fullAccessApplication, actual) } } + +func TestCreateSaasAccessApplications(t *testing.T) { + setup() + defer teardown() + + handler := func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) + w.Header().Set("content-type", "application/json") + fmt.Fprintf(w, `{ + "success": true, + "errors": [], + "messages": [], + "result": { + "id": "480f4f69-1a28-4fdd-9240-1ed29f0ac1db", + "created_at": "2014-01-01T05:20:00.12345Z", + "updated_at": "2014-01-01T05:20:00.12345Z", + "aud": "737646a56ab1df6ec9bddc7e5ca84eaf3b0768850f3ffb5d74f1534911fe3893", + "name": "Admin Saas App", + "domain": "example.cloudflareaccess.com/cdn-cgi/access/sso/saml/737646a56ab1df6ec9bddc7e5ca84eaf3b0768850f3ffb5d74f1534911fe3893", + "type": "saas", + "session_duration": "24h", + "allowed_idps": [], + "auto_redirect_to_identity": false, + "enable_binding_cookie": false, + "custom_deny_url": "https://www.example.com", + "custom_deny_message": "denied!", + "logo_url": "https://www.example.com/example.png", + "skip_interstitial": true, + "app_launcher_visible": true, + "service_auth_401_redirect": true, + "saas_app": { + "consumer_service_url": "https://saas.example.com", + "sp_entity_id": "dash.example.com", + "name_id_format": "id", + "custom_attributes": [ + { + "name": "test1", + "name_format": "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified", + "source": { + "name": "test1" + } + }, + { + "name": "test2", + "name_format": "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", + "source": { + "name": "test2" + } + }, + { + "name": "test3", + "name_format": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", + "source": { + "name": "test3" + } + } + ] + } + } + } + `) + } + + createdAt, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00.12345Z") + updatedAt, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00.12345Z") + fullAccessApplication := AccessApplication{ + ID: "480f4f69-1a28-4fdd-9240-1ed29f0ac1db", + Name: "Admin Saas App", + Domain: "example.cloudflareaccess.com/cdn-cgi/access/sso/saml/737646a56ab1df6ec9bddc7e5ca84eaf3b0768850f3ffb5d74f1534911fe3893", + Type: "saas", + SessionDuration: "24h", + AUD: "737646a56ab1df6ec9bddc7e5ca84eaf3b0768850f3ffb5d74f1534911fe3893", + AllowedIdps: []string{}, + AutoRedirectToIdentity: false, + EnableBindingCookie: false, + AppLauncherVisible: true, + ServiceAuth401Redirect: true, + CustomDenyMessage: "denied!", + CustomDenyURL: "https://www.example.com", + LogoURL: "https://www.example.com/example.png", + SkipInterstitial: true, + SaasApplication: &SaasApplication{ + ConsumerServiceUrl: "https://saas.example.com", + SPEntityID: "dash.example.com", + NameIDFormat: "id", + CustomAttributes: []SAMLAttributeConfig{ + { + Name: "test1", + NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified", + Source: SourceConfig{ + Name: "test1", + }, + }, + { + Name: "test2", + NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", + Source: SourceConfig{ + Name: "test2", + }, + }, + { + Name: "test3", + NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:uri", + Source: SourceConfig{ + Name: "test3", + }, + }, + }, + }, + CreatedAt: &createdAt, + UpdatedAt: &updatedAt, + } + + mux.HandleFunc("/accounts/"+testAccountID+"/access/apps", handler) + + actual, err := client.CreateAccessApplication(context.Background(), testAccountID, AccessApplication{ + Name: "Admin Saas Site", + SaasApplication: &SaasApplication{ + ConsumerServiceUrl: "https://examplesaas.com", + SPEntityID: "TEST12345678", + NameIDFormat: "id", + }, + SessionDuration: "24h", + }) + + if assert.NoError(t, err) { + assert.Equal(t, fullAccessApplication, actual) + } + + mux.HandleFunc("/zones/"+testZoneID+"/access/apps", handler) + + actual, err = client.CreateZoneLevelAccessApplication(context.Background(), testZoneID, AccessApplication{ + Name: "Admin Saas Site", + SaasApplication: &SaasApplication{ + ConsumerServiceUrl: "https://saas.example.com", + SPEntityID: "TEST12345678", + NameIDFormat: "id", + }, + SessionDuration: "24h", + }) + + if assert.NoError(t, err) { + assert.Equal(t, fullAccessApplication, actual) + } +}