forked from cloudflare/cloudflare-go
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for Access Audit Logs (cloudflare#496)
Introduces support for querying the Access Audit Log endpoint. API reference: https://api.cloudflare.com/#access-requests-access-requests-audit
- Loading branch information
1 parent
f76f4c8
commit 246f2f7
Showing
3 changed files
with
200 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package cloudflare | ||
|
||
import ( | ||
"encoding/json" | ||
"net/url" | ||
"strconv" | ||
"time" | ||
|
||
"github.com/pkg/errors" | ||
) | ||
|
||
// AccessAuditLogRecord is the structure of a single Access Audit Log entry. | ||
type AccessAuditLogRecord struct { | ||
UserEmail string `json:"user_email"` | ||
IPAddress string `json:"ip_address"` | ||
AppUID string `json:"app_uid"` | ||
AppDomain string `json:"app_domain"` | ||
Action string `json:"action"` | ||
Connection string `json:"connection"` | ||
Allowed bool `json:"allowed"` | ||
CreatedAt *time.Time `json:"created_at"` | ||
RayID string `json:"ray_id"` | ||
} | ||
|
||
// AccessAuditLogListResponse represents the response from the list | ||
// access applications endpoint. | ||
type AccessAuditLogListResponse struct { | ||
Result []AccessAuditLogRecord `json:"result"` | ||
Response | ||
ResultInfo `json:"result_info"` | ||
} | ||
|
||
// AccessAuditLogFilterOptions provides the structure of available audit log | ||
// filters. | ||
type AccessAuditLogFilterOptions struct { | ||
Direction string | ||
Since *time.Time | ||
Until *time.Time | ||
Limit int | ||
} | ||
|
||
// AccessAuditLogs retrieves all audit logs for the Access service. | ||
// | ||
// API reference: https://api.cloudflare.com/#access-requests-access-requests-audit | ||
func (api *API) AccessAuditLogs(accountID string, opts AccessAuditLogFilterOptions) ([]AccessAuditLogRecord, error) { | ||
uri := "/accounts/" + accountID + "/access/logs/access-requests?" + opts.Encode() | ||
|
||
res, err := api.makeRequest("GET", uri, nil) | ||
if err != nil { | ||
return []AccessAuditLogRecord{}, errors.Wrap(err, errMakeRequestError) | ||
} | ||
|
||
var accessAuditLogListResponse AccessAuditLogListResponse | ||
err = json.Unmarshal(res, &accessAuditLogListResponse) | ||
if err != nil { | ||
return []AccessAuditLogRecord{}, errors.Wrap(err, errUnmarshalError) | ||
} | ||
|
||
return accessAuditLogListResponse.Result, nil | ||
} | ||
|
||
// Encode is a custom method for encoding the filter options into a usable HTTP | ||
// query parameter string. | ||
func (a AccessAuditLogFilterOptions) Encode() string { | ||
v := url.Values{} | ||
|
||
if a.Direction != "" { | ||
v.Set("direction", a.Direction) | ||
} | ||
|
||
if a.Limit > 0 { | ||
v.Set("limit", strconv.Itoa(a.Limit)) | ||
} | ||
|
||
if a.Since != nil { | ||
v.Set("since", (*a.Since).Format(time.RFC3339)) | ||
} | ||
|
||
if a.Until != nil { | ||
v.Set("until", (*a.Until).Format(time.RFC3339)) | ||
} | ||
|
||
return v.Encode() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package cloudflare_test | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"log" | ||
|
||
cloudflare "github.com/cloudflare/cloudflare-go" | ||
) | ||
|
||
func ExampleAPI_AccessAuditLogs() { | ||
api, err := cloudflare.New("deadbeef", "[email protected]") | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
filterOpts := cloudflare.AccessAuditLogFilterOptions{} | ||
results, _ := api.AccessAuditLogs("someaccountid", filterOpts) | ||
|
||
for _, record := range results { | ||
b, _ := json.Marshal(record) | ||
fmt.Println(string(b)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package cloudflare | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestAccessAuditLogs(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
handler := func(w http.ResponseWriter, r *http.Request) { | ||
assert.Equal(t, r.Method, "GET", "Expected method 'GET', got %s", r.Method) | ||
w.Header().Set("content-type", "application/json") | ||
fmt.Fprintf(w, `{ | ||
"success": true, | ||
"errors": [], | ||
"messages": [], | ||
"result": [ | ||
{ | ||
"user_email": "[email protected]", | ||
"ip_address": "198.41.129.166", | ||
"app_uid": "df7e2w5f-02b7-4d9d-af26-8d1988fca630", | ||
"app_domain": "test.example.com/admin", | ||
"action": "login", | ||
"connection": "saml", | ||
"allowed": false, | ||
"created_at": "2014-01-01T05:20:00.12345Z", | ||
"ray_id": "187d944c61940c77" | ||
} | ||
] | ||
} | ||
`) | ||
} | ||
|
||
mux.HandleFunc("/accounts/01a7362d577a6c3019a474fd6f485823/access/logs/access-requests", handler) | ||
createdAt, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00.12345Z") | ||
|
||
want := []AccessAuditLogRecord{{ | ||
UserEmail: "[email protected]", | ||
IPAddress: "198.41.129.166", | ||
AppUID: "df7e2w5f-02b7-4d9d-af26-8d1988fca630", | ||
AppDomain: "test.example.com/admin", | ||
Action: "login", | ||
Connection: "saml", | ||
Allowed: false, | ||
CreatedAt: &createdAt, | ||
RayID: "187d944c61940c77", | ||
}} | ||
|
||
actual, err := client.AccessAuditLogs("01a7362d577a6c3019a474fd6f485823", AccessAuditLogFilterOptions{}) | ||
|
||
if assert.NoError(t, err) { | ||
assert.Equal(t, want, actual) | ||
} | ||
} | ||
|
||
func TestAccessAuditLogsEncodeAllParametersDefined(t *testing.T) { | ||
since, _ := time.Parse(time.RFC3339, "2020-07-01T00:00:00Z") | ||
until, _ := time.Parse(time.RFC3339, "2020-07-02T00:00:00Z") | ||
|
||
opts := AccessAuditLogFilterOptions{ | ||
Direction: "desc", | ||
Since: &since, | ||
Until: &until, | ||
Limit: 10, | ||
} | ||
|
||
assert.Equal(t, "direction=desc&limit=10&since=2020-07-01T00%3A00%3A00Z&until=2020-07-02T00%3A00%3A00Z", opts.Encode()) | ||
} | ||
|
||
func TestAccessAuditLogsEncodeOnlyDatesDefined(t *testing.T) { | ||
since, _ := time.Parse(time.RFC3339, "2020-07-01T00:00:00Z") | ||
until, _ := time.Parse(time.RFC3339, "2020-07-02T00:00:00Z") | ||
|
||
opts := AccessAuditLogFilterOptions{ | ||
Since: &since, | ||
Until: &until, | ||
} | ||
|
||
assert.Equal(t, "since=2020-07-01T00%3A00%3A00Z&until=2020-07-02T00%3A00%3A00Z", opts.Encode()) | ||
} | ||
|
||
func TestAccessAuditLogsEncodeEmptyValues(t *testing.T) { | ||
opts := AccessAuditLogFilterOptions{} | ||
|
||
assert.Equal(t, "", opts.Encode()) | ||
} |