Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into add-vnet-param
Browse files Browse the repository at this point in the history
  • Loading branch information
vavsab committed May 31, 2022
2 parents 3119632 + b005e27 commit 0657062
Show file tree
Hide file tree
Showing 8 changed files with 815 additions and 16 deletions.
38 changes: 35 additions & 3 deletions access_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,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.
Expand All @@ -38,6 +43,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"`
Expand Down Expand Up @@ -80,6 +86,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
Expand Down
145 changes: 145 additions & 0 deletions access_application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
141 changes: 130 additions & 11 deletions logpush.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,51 @@ import (

// LogpushJob describes a Logpush job.
type LogpushJob struct {
ID int `json:"id,omitempty"`
Dataset string `json:"dataset"`
Enabled bool `json:"enabled"`
Name string `json:"name"`
LogpullOptions string `json:"logpull_options"`
DestinationConf string `json:"destination_conf"`
OwnershipChallenge string `json:"ownership_challenge,omitempty"`
LastComplete *time.Time `json:"last_complete,omitempty"`
LastError *time.Time `json:"last_error,omitempty"`
ErrorMessage string `json:"error_message,omitempty"`
Frequency string `json:"frequency,omitempty"`
ID int `json:"id,omitempty"`
Dataset string `json:"dataset"`
Enabled bool `json:"enabled"`
Name string `json:"name"`
LogpullOptions string `json:"logpull_options"`
DestinationConf string `json:"destination_conf"`
OwnershipChallenge string `json:"ownership_challenge,omitempty"`
LastComplete *time.Time `json:"last_complete,omitempty"`
LastError *time.Time `json:"last_error,omitempty"`
ErrorMessage string `json:"error_message,omitempty"`
Frequency string `json:"frequency,omitempty"`
Filter LogpushJobFilters `json:"filter,omitempty"`
}

type LogpushJobFilters struct {
Where LogpushJobFilter `json:"where"`
}

type Operator string

const (
Equal Operator = "eq"
NotEqual Operator = "!eq"
LessThan Operator = "lt"
LessThanOrEqual Operator = "lte"
GreaterThan Operator = "gt"
GreaterThanOrEqual Operator = "gte"
StartsWith Operator = "startsWith"
EndsWith Operator = "endsWith"
NotStartsWith Operator = "!startsWith"
NotEndsWith Operator = "!endsWith"
Contains Operator = "contains"
NotContains Operator = "!contains"
ValueIsIn Operator = "in"
ValueIsNotIn Operator = "!in"
)

type LogpushJobFilter struct {
// either this
And []LogpushJobFilter `json:"and,omitempty"`
Or []LogpushJobFilter `json:"or,omitempty"`
// or this
Key string `json:"key,omitempty"`
Operator Operator `json:"operator,omitempty"`
Value interface{} `json:"value,omitempty"`
}

// LogpushJobsResponse is the API response, containing an array of Logpush Jobs.
Expand Down Expand Up @@ -93,6 +127,91 @@ type LogpushDestinationExistsRequest struct {
DestinationConf string `json:"destination_conf"`
}

// Custom Marshaller for LogpushJob filter key.
func (f LogpushJob) MarshalJSON() ([]byte, error) {
type Alias LogpushJob

filter, err := json.Marshal(f.Filter)

if err != nil {
return nil, err
}

return json.Marshal(&struct {
Filter string `json:"filter,omitempty"`
Alias
}{
Filter: string(filter),
Alias: (Alias)(f),
})
}

// Custom Unmarshaller for LogpushJob filter key.
func (f *LogpushJob) UnmarshalJSON(data []byte) error {
type Alias LogpushJob
aux := &struct {
Filter string `json:"filter,omitempty"`
*Alias
}{
Alias: (*Alias)(f),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}

if aux != nil && aux.Filter != "" {
var filter LogpushJobFilters
if err := json.Unmarshal([]byte(aux.Filter), &filter); err != nil {
return err
}
if err := filter.Where.Validate(); err != nil {
return err
}
f.Filter = filter
}
return nil
}

func (filter *LogpushJobFilter) Validate() error {
if filter.And != nil {
if filter.Or != nil || filter.Key != "" || filter.Operator != "" || filter.Value != nil {
return errors.New("And can't be set with Or, Key, Operator or Value")
}
for i, element := range filter.And {
err := element.Validate()
if err != nil {
return errors.WithMessagef(err, "element %v in And is invalid", i)
}
}
return nil
}
if filter.Or != nil {
if filter.And != nil || filter.Key != "" || filter.Operator != "" || filter.Value != nil {
return errors.New("Or can't be set with And, Key, Operator or Value")
}
for i, element := range filter.Or {
err := element.Validate()
if err != nil {
return errors.WithMessagef(err, "element %v in Or is invalid", i)
}
}
return nil
}
if filter.Key == "" {
return errors.New("Key is missing")
}

if filter.Operator == "" {
return errors.New("Operator is missing")
}

if filter.Value == nil {
return errors.New("Value is missing")
}

return nil
}

// CreateAccountLogpushJob creates a new account-level Logpush Job.
//
// API reference: https://api.cloudflare.com/#logpush-jobs-create-logpush-job
Expand Down
26 changes: 26 additions & 0 deletions logpush_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cloudflare_test

import (
"context"
"encoding/json"
"fmt"
"log"

Expand Down Expand Up @@ -172,3 +173,28 @@ func ExampleAPI_CheckZoneLogpushDestinationExists() {

fmt.Printf("%+v\n", exists)
}

func ExampleLogpushJob_MarshalJSON() {
job := cloudflare.LogpushJob{
Name: "example.com static assets",
LogpullOptions: "fields=RayID,ClientIP,EdgeStartTimestamp&timestamps=rfc3339&CVE-2021-44228=true",
Dataset: "http_requests",
DestinationConf: "s3://<BUCKET_PATH>?region=us-west-2/",
Filter: cloudflare.LogpushJobFilters{
Where: cloudflare.LogpushJobFilter{
And: []cloudflare.LogpushJobFilter{
{Key: "ClientRequestPath", Operator: cloudflare.Contains, Value: "/static\\"},
{Key: "ClientRequestHost", Operator: cloudflare.Equal, Value: "example.com"},
},
},
},
}

jobstring, err := json.Marshal(job)
if err != nil {
log.Fatal(err)
}

fmt.Printf("%s", jobstring)
// Output: {"filter":"{\"where\":{\"and\":[{\"key\":\"ClientRequestPath\",\"operator\":\"contains\",\"value\":\"/static\\\\\"},{\"key\":\"ClientRequestHost\",\"operator\":\"eq\",\"value\":\"example.com\"}]}}","dataset":"http_requests","enabled":false,"name":"example.com static assets","logpull_options":"fields=RayID,ClientIP,EdgeStartTimestamp\u0026timestamps=rfc3339\u0026CVE-2021-44228=true","destination_conf":"s3://\u003cBUCKET_PATH\u003e?region=us-west-2/"}
}

0 comments on commit 0657062

Please sign in to comment.