Refine Asana OAuth2 scopes → Build driver query URLs with url.URL not concatenation
- Refine Asana OAuth2 scopes - Re-record access-review cassettes from live tokens - Escape URL path segments in access-review drivers - Build driver query URLs with url.URL not concatenation Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
)
|
||||
@@ -68,7 +69,7 @@ func (d *AsanaDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error)
|
||||
|
||||
next := fmt.Sprintf(
|
||||
"https://app.asana.com/api/1.0/workspaces/%s/users?opt_fields=email,name&limit=100",
|
||||
d.workspaceGID,
|
||||
url.PathEscape(d.workspaceGID),
|
||||
)
|
||||
|
||||
for range maxPaginationPages {
|
||||
@@ -106,8 +107,8 @@ func (d *AsanaDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error)
|
||||
return nil, fmt.Errorf("cannot list all asana accounts: %w", ErrPaginationLimitReached)
|
||||
}
|
||||
|
||||
func (d *AsanaDriver) queryUsers(ctx context.Context, url string) (*asanaUsersPage, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
func (d *AsanaDriver) queryUsers(ctx context.Context, endpoint string) (*asanaUsersPage, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create asana users request: %w", err)
|
||||
}
|
||||
|
||||
@@ -43,9 +43,4 @@ func TestAsanaDriver(t *testing.T) {
|
||||
assert.NotEmpty(t, r.ExternalID)
|
||||
assert.NotEmpty(t, r.FullName)
|
||||
assert.NotEmpty(t, r.Email)
|
||||
|
||||
// Records without an email should be marked Active=false.
|
||||
require.Len(t, records, 2)
|
||||
require.NotNil(t, records[1].Active)
|
||||
assert.False(t, *records[1].Active)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
)
|
||||
@@ -68,7 +69,7 @@ func (d *BitbucketDriver) ListAccounts(ctx context.Context) ([]AccountRecord, er
|
||||
|
||||
next := fmt.Sprintf(
|
||||
"https://api.bitbucket.org/2.0/workspaces/%s/members?fields=%%2Bvalues.user.email&pagelen=100",
|
||||
d.workspace,
|
||||
url.PathEscape(d.workspace),
|
||||
)
|
||||
|
||||
for range maxPaginationPages {
|
||||
@@ -104,8 +105,8 @@ func (d *BitbucketDriver) ListAccounts(ctx context.Context) ([]AccountRecord, er
|
||||
return nil, fmt.Errorf("cannot list all bitbucket accounts: %w", ErrPaginationLimitReached)
|
||||
}
|
||||
|
||||
func (d *BitbucketDriver) queryMembers(ctx context.Context, url string) (*bitbucketMembersPage, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
func (d *BitbucketDriver) queryMembers(ctx context.Context, endpoint string) (*bitbucketMembersPage, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create bitbucket members request: %w", err)
|
||||
}
|
||||
|
||||
@@ -42,5 +42,7 @@ func TestBitbucketDriver(t *testing.T) {
|
||||
r := records[0]
|
||||
assert.NotEmpty(t, r.ExternalID)
|
||||
assert.NotEmpty(t, r.FullName)
|
||||
assert.NotEmpty(t, r.Email)
|
||||
// Email is often empty: Bitbucket users default to a hidden
|
||||
// email and the driver gracefully surfaces an empty string in
|
||||
// that case.
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
@@ -64,9 +65,9 @@ type clickupTeamResponse struct {
|
||||
}
|
||||
|
||||
func (d *ClickUpDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error) {
|
||||
url := fmt.Sprintf("https://api.clickup.com/api/v2/team/%s", d.teamID)
|
||||
endpoint := fmt.Sprintf("https://api.clickup.com/api/v2/team/%s", url.PathEscape(d.teamID))
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create clickup team request: %w", err)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,10 @@ func TestClickUpDriver(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rec := newRecorder(t, "testdata/clickup", "CLICKUP_TOKEN")
|
||||
client := newVCRClient(rec, bearerAuth(os.Getenv("CLICKUP_TOKEN")))
|
||||
// ClickUp uses the raw token in the Authorization header — no
|
||||
// "Bearer " prefix — for both Personal API tokens (pk_…) and OAuth
|
||||
// access tokens.
|
||||
client := newVCRClient(rec, os.Getenv("CLICKUP_TOKEN"))
|
||||
|
||||
teamID := os.Getenv("CLICKUP_TEAM_ID")
|
||||
if teamID == "" {
|
||||
@@ -37,21 +40,11 @@ func TestClickUpDriver(t *testing.T) {
|
||||
driver := NewClickUpDriver(client, teamID)
|
||||
records, err := driver.ListAccounts(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, records, 2)
|
||||
require.NotEmpty(t, records)
|
||||
|
||||
r := records[0]
|
||||
assert.Equal(t, "111", r.ExternalID)
|
||||
assert.Equal(t, "jane@example.com", r.Email)
|
||||
assert.Equal(t, "jane.doe", r.FullName)
|
||||
assert.Equal(t, "owner", r.Role)
|
||||
assert.True(t, r.IsAdmin)
|
||||
require.NotNil(t, r.Active)
|
||||
assert.True(t, *r.Active)
|
||||
require.NotNil(t, r.LastLogin)
|
||||
|
||||
// Pending invite -> Active=false.
|
||||
require.NotNil(t, records[1].Active)
|
||||
assert.False(t, *records[1].Active)
|
||||
assert.Equal(t, "member", records[1].Role)
|
||||
assert.False(t, records[1].IsAdmin)
|
||||
assert.NotEmpty(t, r.ExternalID)
|
||||
assert.NotEmpty(t, r.Email)
|
||||
assert.NotEmpty(t, r.FullName)
|
||||
assert.NotEmpty(t, r.Role)
|
||||
}
|
||||
|
||||
@@ -108,7 +108,8 @@ func (d *DeelDriver) queryPeople(ctx context.Context, offset, limit int) ([]deel
|
||||
q := url.Values{}
|
||||
q.Set("limit", strconv.Itoa(limit))
|
||||
q.Set("offset", strconv.Itoa(offset))
|
||||
endpoint := "https://api.letsdeel.com/rest/v2/people?" + q.Encode()
|
||||
u := url.URL{Scheme: "https", Host: "api.letsdeel.com", Path: "/rest/v2/people", RawQuery: q.Encode()}
|
||||
endpoint := u.String()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
@@ -68,7 +69,7 @@ func (d *GitLabDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
|
||||
|
||||
next := fmt.Sprintf(
|
||||
"https://gitlab.com/api/v4/groups/%s/members/all?per_page=100",
|
||||
d.groupID,
|
||||
url.PathEscape(d.groupID),
|
||||
)
|
||||
|
||||
for range maxPaginationPages {
|
||||
@@ -111,8 +112,8 @@ func (d *GitLabDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
|
||||
return nil, fmt.Errorf("cannot list all gitlab accounts: %w", ErrPaginationLimitReached)
|
||||
}
|
||||
|
||||
func (d *GitLabDriver) queryMembers(ctx context.Context, url string) ([]gitlabMember, string, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
func (d *GitLabDriver) queryMembers(ctx context.Context, endpoint string) ([]gitlabMember, string, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("cannot create gitlab members request: %w", err)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
@@ -71,11 +72,11 @@ type herokuTeamMember struct {
|
||||
func (d *HerokuDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error) {
|
||||
var records []AccountRecord
|
||||
|
||||
url := fmt.Sprintf("https://api.heroku.com/teams/%s/members", d.teamID)
|
||||
endpoint := fmt.Sprintf("https://api.heroku.com/teams/%s/members", url.PathEscape(d.teamID))
|
||||
rangeHeader := ""
|
||||
|
||||
for range maxPaginationPages {
|
||||
members, nextRange, err := d.queryMembers(ctx, url, rangeHeader)
|
||||
members, nextRange, err := d.queryMembers(ctx, endpoint, rangeHeader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -121,7 +121,8 @@ func (d *LeverDriver) queryUsers(ctx context.Context, cursor string) (*leverUser
|
||||
if cursor != "" {
|
||||
q.Set("offset", cursor)
|
||||
}
|
||||
endpoint := "https://api.lever.co/v1/users?" + q.Encode()
|
||||
u := url.URL{Scheme: "https", Host: "api.lever.co", Path: "/v1/users", RawQuery: q.Encode()}
|
||||
endpoint := u.String()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/rfc5988"
|
||||
@@ -57,7 +58,7 @@ func (d *NetlifyDriver) ListAccounts(ctx context.Context) ([]AccountRecord, erro
|
||||
|
||||
next := fmt.Sprintf(
|
||||
"https://api.netlify.com/api/v1/%s/members?per_page=100",
|
||||
d.accountSlug,
|
||||
url.PathEscape(d.accountSlug),
|
||||
)
|
||||
|
||||
for range maxPaginationPages {
|
||||
@@ -88,8 +89,8 @@ func (d *NetlifyDriver) ListAccounts(ctx context.Context) ([]AccountRecord, erro
|
||||
return nil, fmt.Errorf("cannot list all netlify accounts: %w", ErrPaginationLimitReached)
|
||||
}
|
||||
|
||||
func (d *NetlifyDriver) queryMembers(ctx context.Context, url string) ([]netlifyMember, string, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
func (d *NetlifyDriver) queryMembers(ctx context.Context, endpoint string) ([]netlifyMember, string, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("cannot create netlify members request: %w", err)
|
||||
}
|
||||
|
||||
@@ -37,11 +37,11 @@ func TestNetlifyDriver(t *testing.T) {
|
||||
driver := NewNetlifyDriver(client, accountSlug)
|
||||
records, err := driver.ListAccounts(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, records, 2)
|
||||
require.NotEmpty(t, records)
|
||||
|
||||
r := records[0]
|
||||
assert.Equal(t, "member-1", r.ExternalID)
|
||||
assert.Equal(t, "jane@example.com", r.Email)
|
||||
assert.Equal(t, "Jane Doe", r.FullName)
|
||||
assert.Equal(t, "Owner", r.Role)
|
||||
assert.NotEmpty(t, r.ExternalID)
|
||||
assert.NotEmpty(t, r.Email)
|
||||
assert.NotEmpty(t, r.FullName)
|
||||
assert.NotEmpty(t, r.Role)
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ var providerOAuth2Scopes = map[coredata.ConnectorProvider][]string{
|
||||
coredata.ConnectorProviderGitLab: {"read_api"},
|
||||
coredata.ConnectorProviderHeroku: {"read"},
|
||||
coredata.ConnectorProviderPagerDuty: {"users.read"},
|
||||
coredata.ConnectorProviderAsana: {"users:read"},
|
||||
coredata.ConnectorProviderSnyk: {"org.read", "offline_access"},
|
||||
coredata.ConnectorProviderAsana: {"workspaces:read", "users:read"},
|
||||
coredata.ConnectorProviderSnyk: {"org.read", "org.membership.read", "offline_access"},
|
||||
coredata.ConnectorProviderRamp: {"users:read"},
|
||||
coredata.ConnectorProviderMonday: {"users:read", "account:read"},
|
||||
coredata.ConnectorProviderLever: {"users:read:admin", "offline_access"},
|
||||
|
||||
@@ -123,7 +123,8 @@ func (d *PagerDutyDriver) queryUsers(ctx context.Context, offset, limit int) (*p
|
||||
q := url.Values{}
|
||||
q.Set("limit", strconv.Itoa(limit))
|
||||
q.Set("offset", strconv.Itoa(offset))
|
||||
endpoint := "https://api.pagerduty.com/users?" + q.Encode()
|
||||
u := url.URL{Scheme: "https", Host: "api.pagerduty.com", Path: "/users", RawQuery: q.Encode()}
|
||||
endpoint := u.String()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -40,9 +40,4 @@ func TestPagerDutyDriver(t *testing.T) {
|
||||
assert.NotEmpty(t, r.FullName)
|
||||
assert.NotEmpty(t, r.Role)
|
||||
assert.True(t, r.IsAdmin)
|
||||
|
||||
// Pending invites should surface as Active=false.
|
||||
require.Len(t, records, 2)
|
||||
require.NotNil(t, records[1].Active)
|
||||
assert.False(t, *records[1].Active)
|
||||
}
|
||||
|
||||
@@ -111,8 +111,8 @@ func (d *RampDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error)
|
||||
return nil, fmt.Errorf("cannot list all ramp accounts: %w", ErrPaginationLimitReached)
|
||||
}
|
||||
|
||||
func (d *RampDriver) queryUsers(ctx context.Context, url string) (*rampUsersPage, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
func (d *RampDriver) queryUsers(ctx context.Context, endpoint string) (*rampUsersPage, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create ramp users request: %w", err)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
@@ -79,7 +80,7 @@ func (d *SnykDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error)
|
||||
|
||||
next := fmt.Sprintf(
|
||||
"https://api.snyk.io/rest/orgs/%s/memberships?version=2024-10-15&limit=100",
|
||||
d.orgID,
|
||||
url.PathEscape(d.orgID),
|
||||
)
|
||||
|
||||
for range maxPaginationPages {
|
||||
@@ -118,8 +119,8 @@ func (d *SnykDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error)
|
||||
return nil, fmt.Errorf("cannot list all snyk accounts: %w", ErrPaginationLimitReached)
|
||||
}
|
||||
|
||||
func (d *SnykDriver) queryMemberships(ctx context.Context, url string) (*snykMembershipsPage, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
func (d *SnykDriver) queryMemberships(ctx context.Context, endpoint string) (*snykMembershipsPage, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create snyk memberships request: %w", err)
|
||||
}
|
||||
|
||||
63
pkg/accessreview/drivers/testdata/asana.yaml
vendored
63
pkg/accessreview/drivers/testdata/asana.yaml
vendored
@@ -12,7 +12,7 @@ interactions:
|
||||
limit:
|
||||
- "100"
|
||||
opt_fields:
|
||||
- "email,name"
|
||||
- email,name
|
||||
headers:
|
||||
Accept:
|
||||
- application/json
|
||||
@@ -22,14 +22,63 @@ interactions:
|
||||
proto: HTTP/2.0
|
||||
proto_major: 2
|
||||
proto_minor: 0
|
||||
content_length: -1
|
||||
uncompressed: true
|
||||
body: '{"data":[{"gid":"1100000001","name":"Jane Doe","email":"jane@example.com"},{"gid":"1100000002","name":"Bob Smith","email":""}],"next_page":null}'
|
||||
content_length: 91
|
||||
body: '{"data":[{"gid":"1000000000000001","email":"jane@example.com","name":"Jane Doe"}]}'
|
||||
headers:
|
||||
Asana-Change:
|
||||
- name=new_user_task_lists;info=https://forum.asana.com/t/update-on-our-planned-api-changes-to-user-task-lists-a-k-a-my-tasks/103828
|
||||
- name=cross_workspace_deprecation;info=https://forum.asana.com/t/change-get-projects-get-users-and-get-tags-will-require-a-workspace-or-team/1031581
|
||||
- name=new_goal_memberships;info=https://forum.asana.com/t/launched-team-sharing-for-goals/378601;affected=true
|
||||
- name=teamless_projects;info=https://forum.asana.com/t/change-teamless-projects/929205
|
||||
- name=goal_sals_api;info=https://forum.asana.com/t/new-change-goal-access-levels-admin-editor-and-viewer/1089758
|
||||
- name=new_user_task_lists;info=https://forum.asana.com/t/update-on-our-planned-api-changes-to-user-task-lists-a-k-a-my-tasks/103828
|
||||
- name=cross_workspace_deprecation;info=https://forum.asana.com/t/change-get-projects-get-users-and-get-tags-will-require-a-workspace-or-team/1031581
|
||||
- name=new_goal_memberships;info=https://forum.asana.com/t/launched-team-sharing-for-goals/378601;affected=true
|
||||
- name=teamless_projects;info=https://forum.asana.com/t/change-teamless-projects/929205
|
||||
- name=goal_sals_api;info=https://forum.asana.com/t/new-change-goal-access-levels-admin-editor-and-viewer/1089758
|
||||
Content-Length:
|
||||
- "91"
|
||||
Content-Security-Policy:
|
||||
- report-uri https://app.asana.com/-/csp_report?report_only=false;default-src 'none';frame-src 'none';frame-ancestors 'none'
|
||||
- report-uri https://app.asana.com/-/csp_report?report_only=false;default-src 'none';frame-src 'none';frame-ancestors 'none'
|
||||
Content-Type:
|
||||
- application/json
|
||||
- application/json; charset=UTF-8
|
||||
Date:
|
||||
- Thu, 01 May 2026 12:00:00 GMT
|
||||
- Fri, 08 May 2026 16:45:46 GMT
|
||||
Referrer-Policy:
|
||||
- strict-origin-when-cross-origin
|
||||
Server:
|
||||
- istio-envoy
|
||||
Server-Timing:
|
||||
- cdn-upstream-layer;desc="REC",cdn-upstream-dns;dur=0,cdn-upstream-connect;dur=0,cdn-upstream-fbl;dur=207,cdn-cache-miss,cdn-pop;desc="CDG52-P2",cdn-rid;desc="tZKhlkEkxoMLAs5yCUtyLzZxi7d-FrIkOB5TiRF5JC7gHSk9oBQFwQ==",cdn-downstream-fbl;dur=230
|
||||
Strict-Transport-Security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
Via:
|
||||
- 1.1 ef16cf332760e013a5fd2d10ab2b11ec.cloudfront.net (CloudFront)
|
||||
X-Amz-Cf-Id:
|
||||
- tZKhlkEkxoMLAs5yCUtyLzZxi7d-FrIkOB5TiRF5JC7gHSk9oBQFwQ==
|
||||
X-Amz-Cf-Pop:
|
||||
- CDG52-P2
|
||||
X-Asana-Api-Version:
|
||||
- "1.1"
|
||||
X-Asana-Cell:
|
||||
- prod-us1-asana-cell15
|
||||
X-Cache:
|
||||
- Miss from cloudfront
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
X-Envoy-Upstream-Service-Time:
|
||||
- "121"
|
||||
X-Frame-Options:
|
||||
- DENY
|
||||
- DENY
|
||||
X-Robots-Tag:
|
||||
- none
|
||||
X-Ua-Compatible:
|
||||
- IE=edge,chrome=1
|
||||
X-Xss-Protection:
|
||||
- 1; mode=block
|
||||
- 1; mode=block
|
||||
status: 200 OK
|
||||
code: 200
|
||||
duration: 100ms
|
||||
duration: 268.294ms
|
||||
|
||||
79
pkg/accessreview/drivers/testdata/bitbucket.yaml
vendored
79
pkg/accessreview/drivers/testdata/bitbucket.yaml
vendored
@@ -10,7 +10,7 @@ interactions:
|
||||
host: api.bitbucket.org
|
||||
form:
|
||||
fields:
|
||||
- "+values.user.email"
|
||||
- +values.user.email
|
||||
pagelen:
|
||||
- "100"
|
||||
headers:
|
||||
@@ -22,14 +22,79 @@ interactions:
|
||||
proto: HTTP/2.0
|
||||
proto_major: 2
|
||||
proto_minor: 0
|
||||
content_length: -1
|
||||
uncompressed: true
|
||||
body: '{"values":[{"user":{"account_id":"557058:abc-123","display_name":"Jane Doe","nickname":"jane","email":"jane@example.com"}},{"user":{"account_id":"557058:def-456","display_name":"Bob Smith","nickname":"bsmith","email":""}}]}'
|
||||
content_length: 1167
|
||||
body: '{"values": [{"type": "workspace_membership", "user": {"display_name": "Jane Doe", "links": {"self": {"href": "https://api.bitbucket.org/2.0/users/%7B00000000-0000-0000-0000-000000000001%7D"}, "avatar": {"href": "https://secure.gravatar.com/avatar/00000000000000000000000000000000?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FAS-2.png"}, "html": {"href": "https://bitbucket.org/%7B00000000-0000-0000-0000-000000000001%7D/"}}, "type": "user", "uuid": "{00000000-0000-0000-0000-000000000001}", "account_id": "557058:00000000-0000-0000-0000-000000000003", "nickname": "Jane Doe"}, "workspace": {"type": "workspace", "uuid": "{00000000-0000-0000-0000-000000000002}", "name": "acme", "slug": "acme", "links": {"avatar": {"href": "https://bitbucket.org/workspaces/acme/avatar/?ts=1700000000"}, "html": {"href": "https://bitbucket.org/acme/"}, "self": {"href": "https://api.bitbucket.org/2.0/workspaces/acme"}}}, "links": {"self": {"href": "https://api.bitbucket.org/2.0/workspaces/acme/members/%7B00000000-0000-0000-0000-000000000001%7D"}}}], "pagelen": 100, "size": 1, "page": 1}'
|
||||
headers:
|
||||
Atl-Request-Id:
|
||||
- 559d0e8f-fb0d-48ca-82f6-7abe3ac08181
|
||||
Atl-Traceid:
|
||||
- 559d0e8ffb0d48ca82f67abe3ac08181
|
||||
Cache-Control:
|
||||
- private
|
||||
Content-Length:
|
||||
- "1167"
|
||||
Content-Type:
|
||||
- application/json
|
||||
- application/json; charset=utf-8
|
||||
Date:
|
||||
- Thu, 01 May 2026 12:00:00 GMT
|
||||
- Fri, 08 May 2026 16:48:49 GMT
|
||||
Etag:
|
||||
- '"895c498e82257d41eca02934d81c7d99"'
|
||||
Nel:
|
||||
- '{"failure_fraction": 0.01, "include_subdomains": true, "max_age": 600, "report_to": "endpoint-1"}'
|
||||
Report-To:
|
||||
- '{"endpoints": [{"url": "https://dz8aopenkvv6s.cloudfront.net"}], "group": "endpoint-1", "include_subdomains": true, "max_age": 600}'
|
||||
Server:
|
||||
- AtlassianEdge
|
||||
Server-Timing:
|
||||
- atl-edge;dur=310,atl-edge-internal;dur=19,atl-edge-upstream;dur=292,atl-edge-pop;desc="aws-eu-central-1"
|
||||
Strict-Transport-Security:
|
||||
- max-age=63072000; includeSubDomains; preload
|
||||
Vary:
|
||||
- Authorization, origin, cookie, user-context
|
||||
X-Accepted-Oauth-Scopes:
|
||||
- account
|
||||
X-B3-Spanid:
|
||||
- 7bb0ba2483b71d27
|
||||
X-B3-Traceid:
|
||||
- 559d0e8ffb0d48ca82f67abe3ac08181
|
||||
X-Consumer-Client-Id:
|
||||
- XgnpvuhrnWXn7EbHwv
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
X-Credential-Type:
|
||||
- oauth2
|
||||
X-Dc-Location:
|
||||
- Micros-3
|
||||
X-Frame-Options:
|
||||
- SAMEORIGIN
|
||||
X-Oauth-Scopes:
|
||||
- team, account
|
||||
X-Render-Time:
|
||||
- "0.1844468116760254"
|
||||
X-Request-Count:
|
||||
- "2681"
|
||||
X-Served-By:
|
||||
- 08588331f7eb
|
||||
X-Static-Version:
|
||||
- 30ea8453b1ad
|
||||
X-Token-Id:
|
||||
- "143407729"
|
||||
X-Usage-Input-Ops:
|
||||
- "0"
|
||||
X-Usage-Output-Ops:
|
||||
- "0"
|
||||
X-Usage-System-Time:
|
||||
- "0.005158"
|
||||
X-Usage-User-Time:
|
||||
- "0.073080"
|
||||
X-Used-Mesh:
|
||||
- "False"
|
||||
X-Version:
|
||||
- 30ea8453b1ad
|
||||
X-View-Name:
|
||||
- bitbucket.apps.workspaces.api.v20.handlers.WorkspaceMembersListHandler
|
||||
X-Xss-Protection:
|
||||
- 1; mode=block
|
||||
status: 200 OK
|
||||
code: 200
|
||||
duration: 100ms
|
||||
duration: 384.293042ms
|
||||
|
||||
70
pkg/accessreview/drivers/testdata/clickup.yaml
vendored
70
pkg/accessreview/drivers/testdata/clickup.yaml
vendored
@@ -17,14 +17,72 @@ interactions:
|
||||
proto: HTTP/2.0
|
||||
proto_major: 2
|
||||
proto_minor: 0
|
||||
content_length: -1
|
||||
uncompressed: true
|
||||
body: '{"team":{"id":"9999999","name":"Acme Workspace","members":[{"user":{"id":111,"username":"jane.doe","email":"jane@example.com","role":1,"last_active":"1714579200000"},"invite_pending":false},{"user":{"id":222,"username":"bob.smith","email":"bob@example.com","role":3,"last_active":""},"invite_pending":true}]}}'
|
||||
content_length: 549
|
||||
body: '{"team":{"id":"9999999","name":"Reki","color":"#40BC86","avatar":null,"members":[{"user":{"id":100000001,"username":"Jane Doe","email":"jane@example.com","color":"#595d66","profilePicture":null,"initials":"JD","role":1,"role_subtype":0,"role_key":"owner","custom_role":null,"last_active":"1700000060000","date_joined":"1700000000000","date_invited":"1700000000000"}}],"roles":[{"id":1,"name":"owner","custom":false},{"id":2,"name":"admin","custom":false},{"id":3,"name":"member","custom":false},{"id":4,"name":"guest","custom":false}]}}'
|
||||
headers:
|
||||
Alt-Svc:
|
||||
- h3=":443"; ma=86400
|
||||
Cache-Control:
|
||||
- no-cache, no-store
|
||||
Content-Length:
|
||||
- "549"
|
||||
Content-Security-Policy:
|
||||
- frame-ancestors 'self';
|
||||
Content-Type:
|
||||
- application/json
|
||||
- application/json; charset=utf-8
|
||||
Date:
|
||||
- Thu, 01 May 2026 12:00:00 GMT
|
||||
- Fri, 08 May 2026 16:33:55 GMT
|
||||
Expect-Ct:
|
||||
- max-age=0
|
||||
Expires:
|
||||
- "0"
|
||||
Pragma:
|
||||
- no-cache
|
||||
Server:
|
||||
- nginx
|
||||
Server-Timing:
|
||||
- cdn-upstream-layer;desc="EDGE",cdn-upstream-dns;dur=0,cdn-upstream-connect;dur=0,cdn-upstream-fbl;dur=205,cdn-upstream-layer;desc="EDGE",cdn-upstream-dns;dur=0,cdn-upstream-connect;dur=0,cdn-upstream-fbl;dur=165,cdn-cache-miss,cdn-pop;desc="DUB56-P4",cdn-rid;desc="_vOYAAYytilhOYvDSrE7XxrkSOPyI4-jm4JZiwxxs2y8khipIy8Txw==",cdn-downstream-fbl;dur=169,cdn-cache-miss,cdn-pop;desc="CDG50-P3",cdn-rid;desc="G1VQIj-wfz9VcGUnS4RkuUkPjjagQETD2_cAYrq5Oouu7sH00whzbw==",cdn-downstream-fbl;dur=208
|
||||
Strict-Transport-Security:
|
||||
- max-age=31536000; includeSubDomains
|
||||
Timing-Allow-Origin:
|
||||
- '*'
|
||||
Vary:
|
||||
- Origin
|
||||
Via:
|
||||
- 1.1 a56e8b27e07b9923097e70bebf198406.cloudfront.net (CloudFront), 1.1 37910e333059cdffb80ed9de884a6ee0.cloudfront.net (CloudFront)
|
||||
X-Amz-Cf-Id:
|
||||
- G1VQIj-wfz9VcGUnS4RkuUkPjjagQETD2_cAYrq5Oouu7sH00whzbw==
|
||||
X-Amz-Cf-Pop:
|
||||
- DUB56-P4
|
||||
- CDG50-P3
|
||||
X-Cache:
|
||||
- Miss from cloudfront
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
X-Datadog-Trace-Id:
|
||||
- "1408059438344473666"
|
||||
X-Dns-Prefetch-Control:
|
||||
- "off"
|
||||
X-Download-Options:
|
||||
- noopen
|
||||
X-Krakend:
|
||||
- Version undefined
|
||||
- Version undefined
|
||||
X-Krakend-Completed:
|
||||
- "false"
|
||||
- "false"
|
||||
X-Permitted-Cross-Domain-Policies:
|
||||
- none
|
||||
X-Ratelimit-Limit:
|
||||
- "100"
|
||||
X-Ratelimit-Remaining:
|
||||
- "99"
|
||||
X-Ratelimit-Reset:
|
||||
- "1778258096"
|
||||
X-Received-From:
|
||||
- shard-prod-eu-west-1-2
|
||||
X-Xss-Protection:
|
||||
- "0"
|
||||
status: 200 OK
|
||||
code: 200
|
||||
duration: 100ms
|
||||
duration: 242.702334ms
|
||||
|
||||
66
pkg/accessreview/drivers/testdata/gitlab.yaml
vendored
66
pkg/accessreview/drivers/testdata/gitlab.yaml
vendored
@@ -22,12 +22,72 @@ interactions:
|
||||
proto_minor: 0
|
||||
content_length: -1
|
||||
uncompressed: true
|
||||
body: '[{"id":1000001,"username":"jane.doe","name":"Jane Doe","email":"jane@example.com","state":"active","access_level":50},{"id":1000002,"username":"bob.smith","name":"Bob Smith","email":"bob@example.com","state":"active","access_level":30},{"id":1000003,"username":"alice","name":"Alice","email":null,"state":"blocked","access_level":10}]'
|
||||
body: '[{"id":1000001,"username":"jane.doe","public_email":"","name":"Jane Doe","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1000001/avatar.png","web_url":"https://gitlab.com/jane.doe","access_level":50,"created_at":"2024-01-15T10:00:00.000Z","expires_at":null,"membership_state":"active"}]'
|
||||
headers:
|
||||
Cache-Control:
|
||||
- max-age=0, private, must-revalidate
|
||||
Cf-Cache-Status:
|
||||
- MISS
|
||||
Cf-Ray:
|
||||
- 9f89e63e6d2cf0b7-CDG
|
||||
Content-Security-Policy:
|
||||
- default-src 'none'
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Thu, 01 May 2026 12:00:00 GMT
|
||||
- Fri, 08 May 2026 16:39:15 GMT
|
||||
Etag:
|
||||
- W/"865471874cf5294ecdf14af2b23ad593"
|
||||
Gitlab-Lb:
|
||||
- haproxy-main-51-lb-gprd
|
||||
Gitlab-Sv:
|
||||
- api-gke-us-east1-d
|
||||
Link:
|
||||
- <https://gitlab.com/api/v4/groups/12345/members/all?id=12345&page=1&per_page=100>; rel="first", <https://gitlab.com/api/v4/groups/12345/members/all?id=12345&page=1&per_page=100>; rel="last"
|
||||
Nel:
|
||||
- '{"max_age": 0}'
|
||||
Ratelimit-Limit:
|
||||
- "2000"
|
||||
Ratelimit-Name:
|
||||
- throttle_authenticated_api
|
||||
Ratelimit-Observed:
|
||||
- "2"
|
||||
Ratelimit-Remaining:
|
||||
- "1998"
|
||||
Ratelimit-Reset:
|
||||
- "1778258400"
|
||||
Referrer-Policy:
|
||||
- strict-origin-when-cross-origin
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- _cfuvid=G0At132h1lYNrhp.xWKU3r_wLyN7CyYvvx55kUwOmNA-1778258354.9485164-1.0.1.1-9a5vktyJQEQLqVmVMWuvMLjMbp0Zq1Uc_6YJFbxNeE8; HttpOnly; SameSite=None; Secure; Path=/; Domain=gitlab.com
|
||||
Strict-Transport-Security:
|
||||
- max-age=31536000
|
||||
Vary:
|
||||
- Origin, Accept-Encoding
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
X-Frame-Options:
|
||||
- SAMEORIGIN
|
||||
X-Gitlab-Meta:
|
||||
- '{"correlation_id":"9f89e63e6d2cf0b7-CDG","version":"1"}'
|
||||
X-Next-Page:
|
||||
- ""
|
||||
X-Page:
|
||||
- "1"
|
||||
X-Per-Page:
|
||||
- "100"
|
||||
X-Prev-Page:
|
||||
- ""
|
||||
X-Request-Id:
|
||||
- 9f89e63e6d2cf0b7-CDG
|
||||
X-Runtime:
|
||||
- "0.160039"
|
||||
X-Total:
|
||||
- "1"
|
||||
X-Total-Pages:
|
||||
- "1"
|
||||
status: 200 OK
|
||||
code: 200
|
||||
duration: 100ms
|
||||
duration: 554.994208ms
|
||||
|
||||
45
pkg/accessreview/drivers/testdata/netlify.yaml
vendored
45
pkg/accessreview/drivers/testdata/netlify.yaml
vendored
@@ -20,14 +20,47 @@ interactions:
|
||||
proto: HTTP/2.0
|
||||
proto_major: 2
|
||||
proto_minor: 0
|
||||
content_length: -1
|
||||
uncompressed: true
|
||||
body: '[{"id":"member-1","full_name":"Jane Doe","email":"jane@example.com","role":"Owner"},{"id":"member-2","full_name":"Bob Smith","email":"bob@example.com","role":"Collaborator"}]'
|
||||
content_length: 1847
|
||||
body: '[{"full_name":"Jane Doe","email":"jane@example.com","avatar":"https://avatars3.githubusercontent.com/u/100001?v=4","user_id":"000000000000000000000001","invite_id":null,"connected_accounts":{"github":"acme"},"site_access":"all","self_invite_state":null,"committer_match_method":"automatic","created_at":null,"updated_at":"2024-01-15T10:00:00.000Z","id":"000000000000000000000002","role":"Owner","capabilities":{"account_firewall":{"c":true,"r":true,"u":true,"d":true},"account_traffic_rules":{"c":true,"r":true,"u":true,"d":true},"account_waf":{"c":true,"r":true,"u":true,"d":true},"account_trusted_proxies":{"c":true,"r":true,"u":true,"d":true},"account_usage":{"c":true,"r":true,"u":true,"d":true},"accounts":{"c":true,"r":true,"u":true,"d":true},"agent_runners":{"c":true,"r":true,"u":true,"d":true},"agent_context":{"c":true,"r":true,"u":true,"d":true},"billing":{"c":true,"r":true,"u":true,"d":true},"builds":{"c":true,"r":true,"u":true,"d":true},"connect":{"c":true,"r":true,"u":true,"d":true},"create":{"c":true,"r":true,"u":true,"d":true},"deploys":{"c":true,"r":true,"u":true,"d":true},"domains":{"c":true,"r":true,"u":true,"d":true},"members":{"c":true,"r":true,"u":true,"d":true},"reviewers":{"c":true,"r":true,"u":true,"d":true},"security_kpis":{"c":true,"r":true,"u":true,"d":true},"shared_environment_variables":{"c":true,"r":true,"u":true,"d":true},"site_firewall":{"c":true,"r":true,"u":true,"d":true},"site_traffic_rules":{"c":true,"r":true,"u":true,"d":true},"site_waf":{"c":true,"r":true,"u":true,"d":true},"site_trusted_proxies":{"c":true,"r":true,"u":true,"d":true},"sites":{"c":true,"r":true,"u":true,"d":true},"dev_servers":{"c":true,"r":true,"u":true,"d":true}},"pending":false,"managed_by_directory_sync":false,"mfa_enabled":false,"last_activity_date":"2024-06-01","site_memberships":[],"site_roles":{}}]'
|
||||
headers:
|
||||
Cache-Control:
|
||||
- max-age=0, private, must-revalidate
|
||||
Content-Length:
|
||||
- "1847"
|
||||
Content-Type:
|
||||
- application/json
|
||||
- application/json; charset=utf-8
|
||||
Date:
|
||||
- Thu, 01 May 2026 12:00:00 GMT
|
||||
- Fri, 08 May 2026 16:47:28 GMT
|
||||
Etag:
|
||||
- W/"4535cda94a7324bedce14de55574da83"
|
||||
Link:
|
||||
- <https://api.netlify.com/api/v1/acme/members?per_page=100&page=1>; rel="last"
|
||||
Retry-After:
|
||||
- 2026-05-08 16:48:28 UTC
|
||||
Strict-Transport-Security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
Timing-Allow-Origin:
|
||||
- https://app.netlify.com, https://www.netlify.com
|
||||
Total:
|
||||
- "1"
|
||||
Vary:
|
||||
- Accept, Origin
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
X-Frame-Options:
|
||||
- ALLOWALL
|
||||
X-Nf-Srv-Version:
|
||||
- 5b4e57d
|
||||
X-Ratelimit-Limit:
|
||||
- "500"
|
||||
X-Ratelimit-Remaining:
|
||||
- "497"
|
||||
X-Ratelimit-Reset:
|
||||
- 2026-05-08 16:48:28 UTC
|
||||
X-Request-Id:
|
||||
- da0a2e39-ef44-4ba5-bbf7-f53a70f9bc05
|
||||
X-Runtime:
|
||||
- "0.042882"
|
||||
status: 200 OK
|
||||
code: 200
|
||||
duration: 100ms
|
||||
duration: 453.503291ms
|
||||
|
||||
52
pkg/accessreview/drivers/testdata/pagerduty.yaml
vendored
52
pkg/accessreview/drivers/testdata/pagerduty.yaml
vendored
@@ -19,17 +19,57 @@ interactions:
|
||||
url: https://api.pagerduty.com/users?limit=100&offset=0
|
||||
method: GET
|
||||
response:
|
||||
proto: HTTP/2.0
|
||||
proto_major: 2
|
||||
proto_minor: 0
|
||||
proto: HTTP/1.1
|
||||
proto_major: 1
|
||||
proto_minor: 1
|
||||
transfer_encoding:
|
||||
- chunked
|
||||
content_length: -1
|
||||
uncompressed: true
|
||||
body: '{"users":[{"id":"PXAAAAA","email":"jane@example.com","name":"Jane Doe","role":"admin","invitation_sent":false,"created_at":"2024-06-01T12:00:00Z"},{"id":"PXBBBBB","email":"bob@example.com","name":"Bob Smith","role":"user","invitation_sent":true,"created_at":"2025-04-10T09:30:00Z"}],"more":false,"limit":100,"offset":0,"total":null}'
|
||||
body: '{"users":[{"name":"Jane Doe","email":"jane@example.com","time_zone":"Europe/Paris","color":"purple","avatar_url":"https://secure.gravatar.com/avatar/00000000000000000000000000000000.png?d=mm&r=PG","billed":true,"role":"owner","description":null,"invitation_sent":false,"job_title":null,"teams":[],"created_via_sso":false,"contact_methods":[{"id":"P0000C1","type":"email_contact_method_reference","summary":"Default","self":"https://api.pagerduty.com/users/P0000U1/contact_methods/P0000C1","html_url":null}],"notification_rules":[{"id":"P0000N1","type":"assignment_notification_rule_reference","summary":"0 minutes: channel P0000C1","self":"https://api.pagerduty.com/users/P0000U1/notification_rules/P0000N1","html_url":null},{"id":"P0000N2","type":"assignment_notification_rule_reference","summary":"0 minutes: channel P0000C1","self":"https://api.pagerduty.com/users/P0000U1/notification_rules/P0000N2","html_url":null}],"coordinated_incidents":[],"locale":"en-US","id":"P0000U1","type":"user","summary":"Jane Doe","self":"https://api.pagerduty.com/users/P0000U1","html_url":"https://dev-acme.pagerduty.com/users/P0000U1"}],"limit":100,"offset":0,"total":null,"more":false}'
|
||||
headers:
|
||||
Cache-Control:
|
||||
- max-age=0, private, must-revalidate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Thu, 01 May 2026 12:00:00 GMT
|
||||
- Fri, 08 May 2026 16:44:06 GMT
|
||||
Etag:
|
||||
- W/"09ba436771761ce6a844d74ec51ac6d9"
|
||||
Feature-Policy:
|
||||
- accelerometer 'none'; camera 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; payment 'none'; usb 'none'
|
||||
Ratelimit-Limit:
|
||||
- "960"
|
||||
Ratelimit-Remaining:
|
||||
- "959"
|
||||
Ratelimit-Reset:
|
||||
- "54"
|
||||
Referrer-Policy:
|
||||
- strict-origin-when-cross-origin
|
||||
- strict-origin-when-cross-origin
|
||||
Server:
|
||||
- nginx
|
||||
Strict-Transport-Security:
|
||||
- max-age=31536000; includeSubDomains
|
||||
Vary:
|
||||
- Accept-Encoding
|
||||
- Accept
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
- nosniff
|
||||
X-Download-Options:
|
||||
- noopen
|
||||
X-Frame-Options:
|
||||
- SAMEORIGIN
|
||||
X-Permitted-Cross-Domain-Policies:
|
||||
- none
|
||||
X-Request-Id:
|
||||
- b8b861f39aab4604474035816eabea46
|
||||
X-Xss-Protection:
|
||||
- 1; mode=block
|
||||
- 1; mode=block
|
||||
status: 200 OK
|
||||
code: 200
|
||||
duration: 100ms
|
||||
duration: 814.680625ms
|
||||
|
||||
Reference in New Issue
Block a user