Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

access_users: Add ZeroTrust Users & Seats functions. #1427

Merged
merged 5 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .changelog/1427.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
```release-note:enhancement
access_seats: Add UpdateAccessUserSeat() to list IP Access Rules
```

```release-note:enhancement
access_user: Add GetAccessUserActiveSessions() to get all active sessions for a Access/Zero-Trust user.
```

```release-note:enhancement
access_user: Add GetAccessUserSingleActiveSession() to get an active session for a Access/Zero-Trust user.
```

```release-note:enhancement
access_user: Add GetAccessUserFailedLogins() to get all failed login attempts for a Access/Zero-Trust user.
```

```release-note:enhancement
access_user: Add GetAccessUserLastSeenIdentity() to get last seen identity for a Access/Zero-Trust user.
```

```release-note:enhancement
access_user: Add ListAccessUsers() to get a list of users for a Access/Zero-Trust account.
```
68 changes: 68 additions & 0 deletions access_seats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package cloudflare

import (
"context"
"errors"
"fmt"
"net/http"
"time"

"github.com/goccy/go-json"
)

var errMissingAccessSeatUID = errors.New("missing required access seat UID")

// AccessUpdateAccessUserSeatResult represents a Access User Seat.
type AccessUpdateAccessUserSeatResult struct {
AccessSeat bool `json:"access_seat"`
CreatedAt *time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at"`
GatewaySeat bool `json:"gateway_seat"`
SeatUID string `json:"seat_uid,omitempty"`
}

// UpdateAccessUserSeatParams
jacobbednarz marked this conversation as resolved.
Show resolved Hide resolved
type UpdateAccessUserSeatParams struct {
SeatUID string `json:"seat_uid,omitempty"`
AccessSeat bool `json:"access_seat"`
GatewaySeat bool `json:"gateway_seat"`
}

// AccessUserSeatResponse represents the response from the access user seat endpoints.
type UpdateAccessUserSeatResponse struct {
Response
Result []AccessUpdateAccessUserSeatResult `json:"result"`
ResultInfo ResultInfo `json:"result_info"`
}

// UpdateAccessUserSeat updates a Access User Seat.
//
// API documentation: https://developers.cloudflare.com/api/operations/zero-trust-seats-update-a-user-seat
func (api *API) UpdateAccessUserSeat(ctx context.Context, rc *ResourceContainer, params UpdateAccessUserSeatParams) ([]AccessUpdateAccessUserSeatResult, error) {
if rc.Level != AccountRouteLevel {
return []AccessUpdateAccessUserSeatResult{}, fmt.Errorf(errInvalidResourceContainerAccess, rc.Level)
}

if params.SeatUID == "" {
return []AccessUpdateAccessUserSeatResult{}, errMissingAccessSeatUID
}

uri := fmt.Sprintf(
"/%s/%s/access/seats",
rc.Level,
rc.Identifier,
)

res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, params)
if err != nil {
return []AccessUpdateAccessUserSeatResult{}, fmt.Errorf("%s: %w", errMakeRequestError, err)
}

var updateAccessUserSeatResponse UpdateAccessUserSeatResponse
err = json.Unmarshal(res, &updateAccessUserSeatResponse)
if err != nil {
return []AccessUpdateAccessUserSeatResult{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}

return updateAccessUserSeatResponse.Result, nil
}
82 changes: 82 additions & 0 deletions access_seats_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package cloudflare

import (
"context"
"fmt"
"net/http"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

var testAccessGroupSeatUID = "access-group-seat-uid"

func TestUpdateAccessUserSeat_ZoneIsNotSupported(t *testing.T) {
setup()
defer teardown()

_, err := client.UpdateAccessUserSeat(context.Background(), testZoneRC, UpdateAccessUserSeatParams{})
assert.EqualError(t, err, fmt.Sprintf(errInvalidResourceContainerAccess, ZoneRouteLevel))
}

func TestUpdateAccessUserSeat_MissingUID(t *testing.T) {
setup()
defer teardown()

_, err := client.UpdateAccessUserSeat(context.Background(), testAccountRC, UpdateAccessUserSeatParams{})
assert.EqualError(t, err, "missing required access seat UID")
}

func TestUpdateAccessUserSeat(t *testing.T) {
setup()
defer teardown()

handler := func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodPatch, r.Method, "Expected method 'PATCH', got %s", r.Method)
w.Header().Set("content-type", "application/json")
fmt.Fprintf(w, `{
"errors": [],
"messages": [],
"result": [
{
"access_seat": false,
"created_at": "2014-01-01T05:20:00.12345Z",
"gateway_seat": false,
"seat_uid": null,
"updated_at": "2014-01-01T05:20:00.12345Z"
}
],
"success": true,
"result_info": {
"count": 1,
"page": 1,
"per_page": 20,
"total_count": 2000
}
}
`)
}

mux.HandleFunc("/accounts/"+testAccountID+"/access/seats", handler)
createdAt, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00.12345Z")
updatedAt, _ := time.Parse(time.RFC3339, "2014-01-01T05:20:00.12345Z")
want := []AccessUpdateAccessUserSeatResult{
{
AccessSeat: false,
CreatedAt: &createdAt,
GatewaySeat: false,
SeatUID: "",
UpdatedAt: &updatedAt,
},
}

actual, err := client.UpdateAccessUserSeat(context.Background(), testAccountRC, UpdateAccessUserSeatParams{
SeatUID: testAccessGroupSeatUID,
AccessSeat: false,
GatewaySeat: false,
})
if assert.NoError(t, err) {
assert.Equal(t, want, actual)
}
}
Loading