diff --git a/coderd/rbac/README.md b/coderd/rbac/README.md new file mode 100644 index 0000000000000..69ba5e9f925aa --- /dev/null +++ b/coderd/rbac/README.md @@ -0,0 +1,73 @@ +# Authz + +Package `authz` implements AuthoriZation for Coder. + +## Overview + +Authorization defines what **permission** a **subject** has to perform **actions** to **objects**: +- **Permission** is binary: *yes* (allowed) or *no* (denied). +- **Subject** in this case is anything that implements interface `authz.Subject`. +- **Action** here is an enumerated list of actions, but we stick to `Create`, `Read`, `Update`, and `Delete` here. +- **Object** here is anything that implements `authz.Object`. + +## Permission Structure + +A **permission** is a rule that grants or denies access for a **subject** to perform an **action** on a **object**. +A **permission** is always applied at a given **level**: + +- **site** level applies to all objects in a given Coder deployment. +- **org** level applies to all objects that have an organization owner (`org_owner`) +- **user** level applies to all objects that have an owner with the same ID as the subject. + +**Permissions** at a higher **level** always override permissions at a **lower** level. + +The effect of a **permission** can be: +- **positive** (allows) +- **negative** (denies) +- **abstain** (neither allows or denies, not applicable) + +**Negative** permissions **always** override **positive** permissions at the same level. +Both **negative** and **positive** permissions override **abstain** at the same level. + +This can be represented by the following truth table, where Y represents *positive*, N represents *negative*, and _ represents *abstain*: + +| Action | Positive | Negative | Result | +|--------|----------|----------|--------| +| read | Y | _ | Y | +| read | Y | N | N | +| read | _ | _ | _ | +| read | _ | N | Y | + + +## Permission Representation + +**Permissions** are represented in string format as `?...`, where: + +- `negated` can be either `+` or `-`. If it is omitted, sign is assumed to be `+`. +- `level` is either `site`, `org`, or `user`. +- `object` is any valid resource type. +- `id` is any valid UUID v4. +- `action` is `create`, `read`, `modify`, or `delete`. + +## Example Permissions + +- `+site.*.*.read`: allowed to perform the `read` action against all objects of type `devurl` in a given Coder deployment. +- `-user.workspace.*.create`: user is not allowed to create workspaces. + +## Roles + +A *role* is a set of permissions. When evaluating a role's permission to form an action, all the relevant permissions for the role are combined at each level. Permissions at a higher level override permissions at a lower level. + +The following table shows the per-level role evaluation. +Y indicates that the role provides positive permissions, N indicates the role provides negative permissions, and _ indicates the role does not provide positive or negative permissions. YN_ indicates that the value in the cell does not matter for the access result. + +| Role (example) | Site | Org | User | Result | +|-----------------|------|-----|------|--------| +| site-admin | Y | YN_ | YN_ | Y | +| no-permission | N | YN_ | YN_ | N | +| org-admin | _ | Y | YN_ | Y | +| non-org-member | _ | N | YN_ | N | +| user | _ | _ | Y | Y | +| | _ | _ | N | N | +| unauthenticated | _ | _ | _ | N | + diff --git a/coderd/rbac/action.go b/coderd/rbac/action.go new file mode 100644 index 0000000000000..224047808e15e --- /dev/null +++ b/coderd/rbac/action.go @@ -0,0 +1,11 @@ +package rbac + +// Action represents the allowed actions to be done on an object. +type Action string + +const ( + ActionCreate = "create" + ActionRead = "read" + ActionUpdate = "update" + ActionDelete = "delete" +) diff --git a/coderd/rbac/authz.go b/coderd/rbac/authz.go new file mode 100644 index 0000000000000..f1ae3335cc4d8 --- /dev/null +++ b/coderd/rbac/authz.go @@ -0,0 +1,70 @@ +package rbac + +import ( + "context" + _ "embed" + + "golang.org/x/xerrors" + + "github.com/open-policy-agent/opa/rego" +) + +// RegoAuthorizer will use a prepared rego query for performing authorize() +type RegoAuthorizer struct { + query rego.PreparedEvalQuery +} + +// Load the policy from policy.rego in this directory. +//go:embed policy.rego +var policy string + +func NewAuthorizer() (*RegoAuthorizer, error) { + ctx := context.Background() + query, err := rego.New( + // allowed is the `allow` field from the prepared query. This is the field to check if authorization is + // granted. + rego.Query("allowed = data.authz.allow"), + rego.Module("policy.rego", policy), + ).PrepareForEval(ctx) + + if err != nil { + return nil, xerrors.Errorf("prepare query: %w", err) + } + return &RegoAuthorizer{query: query}, nil +} + +type authSubject struct { + ID string `json:"id"` + Roles []Role `json:"roles"` +} + +func (a RegoAuthorizer) Authorize(ctx context.Context, subjectID string, roles []Role, action Action, object Object) error { + input := map[string]interface{}{ + "subject": authSubject{ + ID: subjectID, + Roles: roles, + }, + "object": object, + "action": action, + } + + results, err := a.query.Eval(ctx, rego.EvalInput(input)) + if err != nil { + return ForbiddenWithInternal(xerrors.Errorf("eval rego: %w, err"), input, results) + } + + if len(results) != 1 { + return ForbiddenWithInternal(xerrors.Errorf("expect only 1 result, got %d", len(results)), input, results) + } + + allowedResult, ok := (results[0].Bindings["allowed"]).(bool) + if !ok { + return ForbiddenWithInternal(xerrors.Errorf("expected allowed to be a bool but got %T", allowedResult), input, results) + } + + if !allowedResult { + return ForbiddenWithInternal(xerrors.Errorf("policy disallows request"), input, results) + } + + return nil +} diff --git a/coderd/rbac/authz_test.go b/coderd/rbac/authz_test.go new file mode 100644 index 0000000000000..c37b3d0f83a10 --- /dev/null +++ b/coderd/rbac/authz_test.go @@ -0,0 +1,633 @@ +package rbac_test + +import ( + "context" + "encoding/json" + "testing" + + "golang.org/x/xerrors" + + "github.com/stretchr/testify/require" + + "github.com/coder/coder/coderd/rbac" +) + +// subject is required because rego needs +type subject struct { + UserID string `json:"id"` + Roles []rbac.Role `json:"roles"` +} + +// TestAuthorizeDomain test the very basic roles that are commonly used. +func TestAuthorizeDomain(t *testing.T) { + t.Parallel() + defOrg := "default" + wrkID := "1234" + + user := subject{ + UserID: "me", + Roles: []rbac.Role{rbac.RoleMember, rbac.RoleOrgMember(defOrg)}, + } + + testAuthorize(t, "Member", user, []authTestCase{ + // Org + me + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg), actions: allActions(), allow: false}, + + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID), actions: allActions(), allow: true}, + + {resource: rbac.ResourceWorkspace.WithID(wrkID), actions: allActions(), allow: false}, + + {resource: rbac.ResourceWorkspace.All(), actions: allActions(), allow: false}, + + // Other org + me + id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other"), actions: allActions(), allow: false}, + + // Other org + other user + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: allActions(), allow: false}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false}, + + // Other org + other use + other id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me"), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID("not-id"), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other"), actions: allActions(), allow: false}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false}, + + {resource: rbac.ResourceWorkspace.WithID("not-id"), actions: allActions(), allow: false}, + }) + + user = subject{ + UserID: "me", + Roles: []rbac.Role{{ + Name: "deny-all", + // List out deny permissions explicitly + Site: []rbac.Permission{ + { + Negate: true, + ResourceType: rbac.WildcardSymbol, + ResourceID: rbac.WildcardSymbol, + Action: rbac.WildcardSymbol, + }, + }, + }}, + } + + testAuthorize(t, "DeletedMember", user, []authTestCase{ + // Org + me + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg), actions: allActions(), allow: false}, + + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID), actions: allActions(), allow: false}, + + {resource: rbac.ResourceWorkspace.WithID(wrkID), actions: allActions(), allow: false}, + + {resource: rbac.ResourceWorkspace.All(), actions: allActions(), allow: false}, + + // Other org + me + id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other"), actions: allActions(), allow: false}, + + // Other org + other user + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: allActions(), allow: false}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false}, + + // Other org + other use + other id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me"), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID("not-id"), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other"), actions: allActions(), allow: false}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false}, + + {resource: rbac.ResourceWorkspace.WithID("not-id"), actions: allActions(), allow: false}, + }) + + user = subject{ + UserID: "me", + Roles: []rbac.Role{ + rbac.RoleOrgAdmin(defOrg), + rbac.RoleMember, + }, + } + + testAuthorize(t, "OrgAdmin", user, []authTestCase{ + // Org + me + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg), actions: allActions(), allow: true}, + + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID), actions: allActions(), allow: true}, + + {resource: rbac.ResourceWorkspace.WithID(wrkID), actions: allActions(), allow: false}, + + {resource: rbac.ResourceWorkspace.All(), actions: allActions(), allow: false}, + + // Other org + me + id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other"), actions: allActions(), allow: false}, + + // Other org + other user + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: allActions(), allow: true}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false}, + + // Other org + other use + other id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me"), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID("not-id"), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other"), actions: allActions(), allow: false}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: false}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: false}, + + {resource: rbac.ResourceWorkspace.WithID("not-id"), actions: allActions(), allow: false}, + }) + + user = subject{ + UserID: "me", + Roles: []rbac.Role{ + rbac.RoleAdmin, + rbac.RoleMember, + }, + } + + testAuthorize(t, "SiteAdmin", user, []authTestCase{ + // Org + me + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg), actions: allActions(), allow: true}, + + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID), actions: allActions(), allow: true}, + + {resource: rbac.ResourceWorkspace.WithID(wrkID), actions: allActions(), allow: true}, + + {resource: rbac.ResourceWorkspace.All(), actions: allActions(), allow: true}, + + // Other org + me + id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID).WithID(wrkID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID(wrkID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg("other"), actions: allActions(), allow: true}, + + // Other org + other user + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), actions: allActions(), allow: true}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: true}, + + // Other org + other use + other id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me"), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID("not-id"), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg("other"), actions: allActions(), allow: true}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), actions: allActions(), allow: true}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me"), actions: allActions(), allow: true}, + + {resource: rbac.ResourceWorkspace.WithID("not-id"), actions: allActions(), allow: true}, + }) + + // In practice this is a token scope on a regular subject + user = subject{ + UserID: "me", + Roles: []rbac.Role{ + rbac.RoleWorkspaceAgent(wrkID), + }, + } + + testAuthorize(t, "WorkspaceAgentToken", user, + // Read Actions + cases(func(c authTestCase) authTestCase { + c.actions = []rbac.Action{rbac.ActionRead} + return c + }, []authTestCase{ + // Org + me + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg), allow: false}, + + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), allow: true}, + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID), allow: false}, + + {resource: rbac.ResourceWorkspace.WithID(wrkID), allow: true}, + + {resource: rbac.ResourceWorkspace.All(), allow: false}, + + // Other org + me + id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID).WithID(wrkID), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID(wrkID), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg("other"), allow: false}, + + // Other org + other user + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), allow: false}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), allow: true}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false}, + + // Other org + other use + other id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me").WithID("not-id"), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me"), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID("not-id"), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other"), allow: false}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), allow: false}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false}, + + {resource: rbac.ResourceWorkspace.WithID("not-id"), allow: false}, + }), + // Not read actions + cases(func(c authTestCase) authTestCase { + c.actions = []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete} + c.allow = false + return c + }, []authTestCase{ + // Org + me + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID)}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg)}, + + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID)}, + + {resource: rbac.ResourceWorkspace.WithID(wrkID)}, + + {resource: rbac.ResourceWorkspace.All()}, + + // Other org + me + id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID).WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID)}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg("other")}, + + // Other org + other user + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me")}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me")}, + + // Other org + other use + other id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me").WithID("not-id")}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me")}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID("not-id")}, + {resource: rbac.ResourceWorkspace.InOrg("other")}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id")}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me")}, + + {resource: rbac.ResourceWorkspace.WithID("not-id")}, + }), + ) + + // In practice this is a token scope on a regular subject + user = subject{ + UserID: "me", + Roles: []rbac.Role{ + { + Name: "ReadOnlyOrgAndUser", + Site: []rbac.Permission{}, + Org: map[string][]rbac.Permission{ + defOrg: {{ + Negate: false, + ResourceType: "*", + ResourceID: "*", + Action: rbac.ActionRead, + }}, + }, + User: []rbac.Permission{ + { + Negate: false, + ResourceType: "*", + ResourceID: "*", + Action: rbac.ActionRead, + }, + }, + }, + }, + } + + testAuthorize(t, "ReadOnly", user, + cases(func(c authTestCase) authTestCase { + c.actions = []rbac.Action{rbac.ActionRead} + return c + }, []authTestCase{ + // Read + // Org + me + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg), allow: true}, + + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), allow: true}, + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID), allow: true}, + + {resource: rbac.ResourceWorkspace.WithID(wrkID), allow: false}, + + {resource: rbac.ResourceWorkspace.All(), allow: false}, + + // Other org + me + id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID).WithID(wrkID), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID(wrkID), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other"), allow: false}, + + // Other org + other user + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), allow: true}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), allow: false}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false}, + + // Other org + other use + other id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me").WithID("not-id"), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me"), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID("not-id"), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other"), allow: false}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), allow: false}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false}, + + {resource: rbac.ResourceWorkspace.WithID("not-id"), allow: false}, + }), + + // Pass non-read actions + cases(func(c authTestCase) authTestCase { + c.actions = []rbac.Action{rbac.ActionCreate, rbac.ActionUpdate, rbac.ActionDelete} + c.allow = false + return c + }, []authTestCase{ + // Read + // Org + me + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID)}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg)}, + + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID)}, + + {resource: rbac.ResourceWorkspace.WithID(wrkID)}, + + {resource: rbac.ResourceWorkspace.All()}, + + // Other org + me + id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID).WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID)}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg("other")}, + + // Other org + other user + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me")}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me")}, + + // Other org + other use + other id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me").WithID("not-id")}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me")}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID("not-id")}, + {resource: rbac.ResourceWorkspace.InOrg("other")}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id")}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me")}, + + {resource: rbac.ResourceWorkspace.WithID("not-id")}, + })) +} + +// TestAuthorizeLevels ensures level overrides are acting appropriately +//nolint:paralleltest +func TestAuthorizeLevels(t *testing.T) { + defOrg := "default" + wrkID := "1234" + + user := subject{ + UserID: "me", + Roles: []rbac.Role{ + rbac.RoleAdmin, + rbac.RoleOrgDenyAll(defOrg), + { + Name: "user-deny-all", + // List out deny permissions explicitly + User: []rbac.Permission{ + { + Negate: true, + ResourceType: rbac.WildcardSymbol, + ResourceID: rbac.WildcardSymbol, + Action: rbac.WildcardSymbol, + }, + }, + }, + }, + } + + testAuthorize(t, "AdminAlwaysAllow", user, + cases(func(c authTestCase) authTestCase { + c.actions = allActions() + c.allow = true + return c + }, []authTestCase{ + // Org + me + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID)}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg)}, + + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID)}, + + {resource: rbac.ResourceWorkspace.WithID(wrkID)}, + + {resource: rbac.ResourceWorkspace.All()}, + + // Other org + me + id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID).WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID)}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg("other")}, + + // Other org + other user + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me")}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID)}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me")}, + + // Other org + other use + other id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me").WithID("not-id")}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me")}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID("not-id")}, + {resource: rbac.ResourceWorkspace.InOrg("other")}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id")}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me")}, + + {resource: rbac.ResourceWorkspace.WithID("not-id")}, + })) + + user = subject{ + UserID: "me", + Roles: []rbac.Role{ + { + Name: "site-noise", + Site: []rbac.Permission{ + { + Negate: true, + ResourceType: "random", + ResourceID: rbac.WildcardSymbol, + Action: rbac.WildcardSymbol, + }, + }, + }, + rbac.RoleOrgAdmin(defOrg), + { + Name: "user-deny-all", + // List out deny permissions explicitly + User: []rbac.Permission{ + { + Negate: true, + ResourceType: rbac.WildcardSymbol, + ResourceID: rbac.WildcardSymbol, + Action: rbac.WildcardSymbol, + }, + }, + }, + }, + } + + testAuthorize(t, "OrgAllowAll", user, + cases(func(c authTestCase) authTestCase { + c.actions = allActions() + return c + }, []authTestCase{ + // Org + me + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID).WithID(wrkID), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner(user.UserID), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithID(wrkID), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg), allow: true}, + + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID).WithID(wrkID), allow: false}, + {resource: rbac.ResourceWorkspace.WithOwner(user.UserID), allow: false}, + + {resource: rbac.ResourceWorkspace.WithID(wrkID), allow: false}, + + {resource: rbac.ResourceWorkspace.All(), allow: false}, + + // Other org + me + id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID).WithID(wrkID), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner(user.UserID), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID(wrkID), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other"), allow: false}, + + // Other org + other user + id + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me").WithID(wrkID), allow: true}, + {resource: rbac.ResourceWorkspace.InOrg(defOrg).WithOwner("not-me"), allow: true}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID(wrkID), allow: false}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false}, + + // Other org + other use + other id + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me").WithID("not-id"), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithOwner("not-me"), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other").WithID("not-id"), allow: false}, + {resource: rbac.ResourceWorkspace.InOrg("other"), allow: false}, + + {resource: rbac.ResourceWorkspace.WithOwner("not-me").WithID("not-id"), allow: false}, + {resource: rbac.ResourceWorkspace.WithOwner("not-me"), allow: false}, + + {resource: rbac.ResourceWorkspace.WithID("not-id"), allow: false}, + })) +} + +// cases applies a given function to all test cases. This makes generalities easier to create. +func cases(opt func(c authTestCase) authTestCase, cases []authTestCase) []authTestCase { + if opt == nil { + return cases + } + for i := range cases { + cases[i] = opt(cases[i]) + } + return cases +} + +type authTestCase struct { + resource rbac.Object + actions []rbac.Action + allow bool +} + +func testAuthorize(t *testing.T, name string, subject subject, sets ...[]authTestCase) { + authorizer, err := rbac.NewAuthorizer() + require.NoError(t, err) + for _, cases := range sets { + for _, c := range cases { + t.Run(name, func(t *testing.T) { + for _, a := range c.actions { + err := authorizer.Authorize(context.Background(), subject.UserID, subject.Roles, a, c.resource) + if c.allow { + if err != nil { + var uerr *rbac.UnauthorizedError + xerrors.As(err, &uerr) + d, _ := json.Marshal(uerr.Input()) + t.Logf("input: %s", string(d)) + t.Logf("internal error: %+v", uerr.Internal().Error()) + t.Logf("output: %+v", uerr.Output()) + } + require.NoError(t, err, "expected no error for testcase action %s", a) + continue + } + + if err == nil { + d, _ := json.Marshal(map[string]interface{}{ + "subject": subject, + "object": c.resource, + "action": a, + }) + t.Log(string(d)) + } + require.Error(t, err, "expected unauthorized") + } + }) + } + } +} + +// allActions is a helper function to return all the possible actions types. +func allActions() []rbac.Action { + return []rbac.Action{rbac.ActionCreate, rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete} +} diff --git a/coderd/rbac/error.go b/coderd/rbac/error.go new file mode 100644 index 0000000000000..593ca4d0fc23a --- /dev/null +++ b/coderd/rbac/error.go @@ -0,0 +1,52 @@ +package rbac + +import "github.com/open-policy-agent/opa/rego" + +const ( + // errUnauthorized is the error message that should be returned to + // clients when an action is forbidden. It is intentionally vague to prevent + // disclosing information that a client should not have access to. + errUnauthorized = "unauthorized" +) + +// UnauthorizedError is the error type for authorization errors +type UnauthorizedError struct { + // internal is the internal error that should never be shown to the client. + // It is only for debugging purposes. + internal error + input map[string]interface{} + output rego.ResultSet +} + +// ForbiddenWithInternal creates a new error that will return a simple +// "forbidden" to the client, logging internally the more detailed message +// provided. +func ForbiddenWithInternal(internal error, input map[string]interface{}, output rego.ResultSet) *UnauthorizedError { + if input == nil { + input = map[string]interface{}{} + } + return &UnauthorizedError{ + internal: internal, + input: input, + output: output, + } +} + +// Error implements the error interface. +func (UnauthorizedError) Error() string { + return errUnauthorized +} + +// Internal allows the internal error message to be logged. +func (e *UnauthorizedError) Internal() error { + return e.internal +} + +func (e *UnauthorizedError) Input() map[string]interface{} { + return e.input +} + +// Output contains the results of the Rego query for debugging. +func (e *UnauthorizedError) Output() rego.ResultSet { + return e.output +} diff --git a/coderd/rbac/example_test.go b/coderd/rbac/example_test.go new file mode 100644 index 0000000000000..e9c4222f9c4a9 --- /dev/null +++ b/coderd/rbac/example_test.go @@ -0,0 +1,54 @@ +package rbac_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/coder/coder/coderd/rbac" +) + +// TestExample gives some examples on how to use the authz library. +// This serves to test syntax more than functionality. +func TestExample(t *testing.T) { + t.Parallel() + ctx := context.Background() + authorizer, err := rbac.NewAuthorizer() + require.NoError(t, err) + + // user will become an authn object, and can even be a database.User if it + // fulfills the interface. Until then, use a placeholder. + user := subject{ + UserID: "alice", + Roles: []rbac.Role{ + rbac.RoleOrgAdmin("default"), + rbac.RoleMember, + }, + } + + //nolint:paralleltest + t.Run("ReadAllWorkspaces", func(t *testing.T) { + // To read all workspaces on the site + err := authorizer.Authorize(ctx, user.UserID, user.Roles, rbac.ActionRead, rbac.ResourceWorkspace.All()) + var _ = err + require.Error(t, err, "this user cannot read all workspaces") + }) + + //nolint:paralleltest + t.Run("ReadOrgWorkspaces", func(t *testing.T) { + // To read all workspaces on the org 'default' + err := authorizer.Authorize(ctx, user.UserID, user.Roles, rbac.ActionRead, rbac.ResourceWorkspace.InOrg("default")) + require.NoError(t, err, "this user can read all org workspaces in 'default'") + }) + + //nolint:paralleltest + t.Run("ReadMyWorkspace", func(t *testing.T) { + // Note 'database.Workspace' could fulfill the object interface and be passed in directly + err := authorizer.Authorize(ctx, user.UserID, user.Roles, rbac.ActionRead, rbac.ResourceWorkspace.InOrg("default").WithOwner(user.UserID)) + require.NoError(t, err, "this user can their workspace") + + err = authorizer.Authorize(ctx, user.UserID, user.Roles, rbac.ActionRead, rbac.ResourceWorkspace.InOrg("default").WithOwner(user.UserID).WithID("1234")) + require.NoError(t, err, "this user can read workspace '1234'") + }) +} diff --git a/coderd/rbac/object.go b/coderd/rbac/object.go new file mode 100644 index 0000000000000..c8b7c94f1d9dc --- /dev/null +++ b/coderd/rbac/object.go @@ -0,0 +1,76 @@ +package rbac + +const WildcardSymbol = "*" + +// Resources are just typed objects. Making resources this way allows directly +// passing them into an Authorize function and use the chaining api. +var ( + ResourceWorkspace = Object{ + Type: "workspace", + } + + ResourceTemplate = Object{ + Type: "template", + } + + // ResourceWildcard represents all resource types + ResourceWildcard = Object{ + Type: WildcardSymbol, + } +) + +// Object is used to create objects for authz checks when you have none in +// hand to run the check on. +// An example is if you want to list all workspaces, you can create a Object +// that represents the set of workspaces you are trying to get access too. +// Do not export this type, as it can be created from a resource type constant. +type Object struct { + ResourceID string `json:"id"` + Owner string `json:"owner"` + // OrgID specifies which org the object is a part of. + OrgID string `json:"org_owner"` + + // Type is "workspace", "project", "devurl", etc + Type string `json:"type"` + // TODO: SharedUsers? +} + +// All returns an object matching all resources of the same type. +func (z Object) All() Object { + return Object{ + ResourceID: "", + Owner: "", + OrgID: "", + Type: z.Type, + } +} + +// InOrg adds an org OwnerID to the resource +func (z Object) InOrg(orgID string) Object { + return Object{ + ResourceID: z.ResourceID, + Owner: z.Owner, + OrgID: orgID, + Type: z.Type, + } +} + +// WithOwner adds an OwnerID to the resource +func (z Object) WithOwner(ownerID string) Object { + return Object{ + ResourceID: z.ResourceID, + Owner: ownerID, + OrgID: z.OrgID, + Type: z.Type, + } +} + +// WithID adds a ResourceID to the resource +func (z Object) WithID(resourceID string) Object { + return Object{ + ResourceID: resourceID, + Owner: z.Owner, + OrgID: z.OrgID, + Type: z.Type, + } +} diff --git a/coderd/rbac/policy.rego b/coderd/rbac/policy.rego new file mode 100644 index 0000000000000..15d986ca08d32 --- /dev/null +++ b/coderd/rbac/policy.rego @@ -0,0 +1,140 @@ +package authz +import future.keywords.in +import future.keywords.every + +# A great playground: https://play.openpolicyagent.org/ +# TODO: Add debug instructions to do in the cli. Running really short on time, the +# playground is sufficient for now imo. In the future we can provide a tidy bash +# script for running this against predefined input. + +# bool_flip lets you assign a value to an inverted bool. +# You cannot do 'x := !false', but you can do 'x := bool_flip(false)' +bool_flip(b) = flipped { + b + flipped = false +} + +bool_flip(b) = flipped { + not b + flipped = true +} + +# perms_grant returns a set of boolean values {true, false}. +# True means a positive permission in the set, false is a negative permission. +# It will only return `bool_flip(perm.negate)` for permissions that affect a given +# resource_type, resource_id, and action. +# The empty set is returned if no relevant permissions are found. +perms_grant(permissions) = grants { + # If there are no permissions, this value is the empty set {}. + grants := { x | + # All permissions ... + perm := permissions[_] + # Such that the permission action, type, and resource_id matches + perm.action in [input.action, "*"] + perm.resource_type in [input.object.type, "*"] + perm.resource_id in [input.object.id, "*"] + x := bool_flip(perm.negate) + } +} + +# Site & User are both very simple. We default both to the empty set '{}'. If no permissions are present, then the +# result is the default value. +default site = {} +site = grant { + # Boolean set for all site wide permissions. + grant = { v | # Use set comprehension to remove duplicate values + # For each role, grab the site permission. + # Find the grants on this permission list. + v = perms_grant(input.subject.roles[_].site)[_] + } +} + +default user = {} +user = grant { + # Only apply user permissions if the user owns the resource + input.object.owner != "" + input.object.owner == input.subject.id + grant = { v | + # For each role, grab the user permissions. + # Find the grants on this permission list. + v = perms_grant(input.subject.roles[_].user)[_] + } +} + +# Organizations are more complex. If the user has no roles that specifically indicate the org_id of the object, +# then we want to block the action. This is because that means the user is not a member of the org. +# A non-member cannot access any org resources. + +# org_member returns the set of permissions associated with a user if the user is a member of the +# organization +org_member = grant { + input.object.org_owner != "" + grant = { v | + v = perms_grant(input.subject.roles[_].org[input.object.org_owner])[_] + } +} + +# If a user is not part of an organization, 'org_non_member' is set to true +org_non_member { + input.object.org_owner != "" + # Identify if the user is in the org + roles := input.subject.roles + every role in roles { + not role.org[input.object.org_owner] + } +} + +# org is two rules that equate to the following +# if org_non_member { return {false} } +# else { org_member } +# +# It is important both rules cannot be true, as the `org` rules cannot produce multiple outputs. +default org = {} +org = set { + # We have to do !org_non_member because rego rules must evaluate to 'true' + # to have a value set. + # So we do "not not-org-member" which means "subject is in org" + not org_non_member + set = org_member +} + +org = set { + org_non_member + set = {false} +} + +# The allow block is quite simple. Any set with `false` cascades down in levels. +# Authorization looks for any `allow` statement that is true. Multiple can be true! +# Note that the absense of `allow` means "unauthorized". +# An explicit `"allow": true` is required. + +# site allow +allow { + # No site wide deny + not false in site + # And all permissions are positive + site[_] +} + +# OR + +# org allow +allow { + # No site or org deny + not false in site + not false in org + # And all permissions are positive + org[_] +} + +# OR + +# user allow +allow { + # No site, org, or user deny + not false in site + not false in org + not false in user + # And all permissions are positive + user[_] +} \ No newline at end of file diff --git a/coderd/rbac/role.go b/coderd/rbac/role.go new file mode 100644 index 0000000000000..c25140aaf24cf --- /dev/null +++ b/coderd/rbac/role.go @@ -0,0 +1,138 @@ +package rbac + +import "fmt" + +type Permission struct { + // Negate makes this a negative permission + Negate bool `json:"negate"` + ResourceType string `json:"resource_type"` + ResourceID string `json:"resource_id"` + Action Action `json:"action"` +} + +// Role is a set of permissions at multiple levels: +// - Site level permissions apply EVERYWHERE +// - Org level permissions apply to EVERYTHING in a given ORG +// - User level permissions are the lowest +// In most cases, you will just want to use the pre-defined roles +// below. +type Role struct { + Name string `json:"name"` + Site []Permission `json:"site"` + // Org is a map of orgid to permissions. We represent orgid as a string. + // TODO: Maybe switch to uuid, but tokens might need to support a "wildcard" org + // which could be a special uuid (like all 0s?) + Org map[string][]Permission `json:"org"` + User []Permission `json:"user"` +} + +// Roles are stored as structs, so they can be serialized and stored. Until we store them elsewhere, +// const's will do just fine. +var ( + // RoleAdmin is a role that allows everything everywhere. + RoleAdmin = Role{ + Name: "admin", + Site: permissions(map[Object][]Action{ + ResourceWildcard: {WildcardSymbol}, + }), + } + + // RoleMember is a role that allows access to user-level resources. + RoleMember = Role{ + Name: "member", + User: permissions(map[Object][]Action{ + ResourceWildcard: {WildcardSymbol}, + }), + } + + // RoleAuditor is an example on how to give more precise permissions + RoleAuditor = Role{ + Name: "auditor", + Site: permissions(map[Object][]Action{ + // TODO: @emyrk when audit logs are added, add back a read perm + //ResourceAuditLogs: {ActionRead}, + // Should be able to read user details to associate with logs. + // Without this the user-id in logs is not very helpful + ResourceWorkspace: {ActionRead}, + }), + } +) + +func RoleOrgDenyAll(orgID string) Role { + return Role{ + Name: "org-deny-" + orgID, + Org: map[string][]Permission{ + orgID: { + { + Negate: true, + ResourceType: "*", + ResourceID: "*", + Action: "*", + }, + }, + }, + } +} + +// RoleOrgAdmin returns a role with all actions allows in a given +// organization scope. +func RoleOrgAdmin(orgID string) Role { + return Role{ + Name: "org-admin-" + orgID, + Org: map[string][]Permission{ + orgID: { + { + Negate: false, + ResourceType: "*", + ResourceID: "*", + Action: "*", + }, + }, + }, + } +} + +// RoleOrgMember returns a role with default permissions in a given +// organization scope. +func RoleOrgMember(orgID string) Role { + return Role{ + Name: "org-member-" + orgID, + Org: map[string][]Permission{ + orgID: {}, + }, + } +} + +// RoleWorkspaceAgent returns a role with permission to read a given +// workspace. +func RoleWorkspaceAgent(workspaceID string) Role { + return Role{ + Name: fmt.Sprintf("agent-%s", workspaceID), + // This is at the site level to prevent the token from losing access if the user + // is kicked from the org + Site: []Permission{ + { + Negate: false, + ResourceType: ResourceWorkspace.Type, + ResourceID: workspaceID, + Action: ActionRead, + }, + }, + } +} + +func permissions(perms map[Object][]Action) []Permission { + list := make([]Permission, 0, len(perms)) + for k, actions := range perms { + for _, act := range actions { + act := act + list = append(list, Permission{ + Negate: false, + ResourceType: k.Type, + ResourceID: WildcardSymbol, + Action: act, + }) + } + } + return list +} diff --git a/go.mod b/go.mod index 833debd678ef4..27199ad22d91b 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,6 @@ require ( github.com/charmbracelet/charm v0.11.0 github.com/charmbracelet/lipgloss v0.5.0 github.com/cli/safeexec v1.0.0 - github.com/cloudflare/cloudflared v0.0.0-20220308214351-5352b3cf0489 github.com/coder/retry v1.3.0 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/creack/pty v1.1.18 @@ -100,6 +99,18 @@ require ( storj.io/drpc v0.0.30 ) +require ( + github.com/cloudflare/cloudflared v0.0.0-00010101000000-000000000000 + github.com/open-policy-agent/opa v0.39.0 +) + +require ( + github.com/OneOfOne/xxhash v1.2.8 // indirect + github.com/ghodss/yaml v1.0.0 // indirect + github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect + github.com/yashtewari/glob-intersection v0.1.0 // indirect +) + require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/BurntSushi/toml v1.0.0 // indirect @@ -155,7 +166,7 @@ require ( github.com/gobwas/ws v1.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 // indirect - github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect + github.com/golang/glog v1.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.7 // indirect diff --git a/go.sum b/go.sum index 888473c4ac038..7558d7d6c93ac 100644 --- a/go.sum +++ b/go.sum @@ -141,6 +141,8 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDe github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= +github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= @@ -260,6 +262,8 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7 github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +github.com/bytecodealliance/wasmtime-go v0.35.0 h1:VZjaZ0XOY0qp9TQfh0CQj9zl/AbdeXePVTALy8V1sKs= +github.com/bytecodealliance/wasmtime-go v0.35.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= @@ -268,6 +272,7 @@ github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s= github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -423,6 +428,7 @@ github.com/coredns/coredns v1.9.0 h1:M1EF1uups4CYcQGb1z8A97mfoq4BYCw3+xCYcJkOSDc github.com/coredns/coredns v1.9.0/go.mod h1:czzy6Ofs15Mzn1PXpWoplBCZxoWdGoQUInL9uPSiYME= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= @@ -440,6 +446,7 @@ github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= @@ -466,14 +473,17 @@ github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xb github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8= +github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M= github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dhui/dktest v0.3.7/go.mod h1:nYMOkafiA07WchSwKnKFUSbGMb2hMm5DrCGiXYG6gwM= @@ -549,13 +559,17 @@ github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGE github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.5+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897 h1:E52jfcE64UG42SwLmrW0QByONfGynWuzBvm86BoB9z8= +github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= @@ -579,6 +593,7 @@ github.com/gdamore/tcell v1.4.0/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404Z github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -617,6 +632,7 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-ini/ini v1.66.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= @@ -629,6 +645,9 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= @@ -777,10 +796,13 @@ github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8l github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.7.0/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/flatbuffers v2.0.0+incompatible h1:dicJ2oXwypfwUGnB2/TYWYEKiuk9eYQlQO/AnOHl5mI= github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -872,6 +894,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.15.2/go.mod h1:vO11I9oWA+KsxmfFQPhLnnIb1VDE24M+pdxZFiuZcA8= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= @@ -1093,8 +1116,10 @@ github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U= @@ -1208,6 +1233,7 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= @@ -1224,6 +1250,7 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyex github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.46 h1:uzwpxRtSVxtcIZmz/4Uz6/Rn7G11DvsaslXoy5LxQio= github.com/miekg/dns v1.1.46/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= @@ -1307,6 +1334,7 @@ github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -1335,6 +1363,8 @@ github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je4 github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/open-policy-agent/opa v0.39.0 h1:nus6g0UC4+6adN5GV2W7K/gsL9QoELBlv5iBVyLVzWI= +github.com/open-policy-agent/opa v0.39.0/go.mod h1:M+l9UHc2T3PCZ/RMPxiHNKZZhukiOWfmaSapx71TmGM= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -1377,6 +1407,7 @@ github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrap github.com/pelletier/go-toml/v2 v2.0.0-beta.6 h1:JFNqj2afbbhCqTiyN16D7Tudc/aaDzE2FBDk+VlBQnE= github.com/pelletier/go-toml/v2 v2.0.0-beta.6/go.mod h1:ke6xncR3W76Ba8xnVxkrZG0js6Rd2BsQEAYrfgJ6eQA= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= @@ -1493,6 +1524,8 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/quasilyte/go-ruleguard/dsl v0.3.19 h1:5+KTKb2YREUYiqZFEIuifFyBxlcCUPWgNZkWy71XS0Q= github.com/quasilyte/go-ruleguard/dsl v0.3.19/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -1581,6 +1614,7 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.8.1 h1:izYHOT71f9iZ7iq37Uqjael60/vYC6vMtzedudZ0zEk= @@ -1590,6 +1624,7 @@ github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= @@ -1603,6 +1638,7 @@ github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -1661,6 +1697,7 @@ github.com/twitchtv/twirp v8.1.1+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vF github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/unrolled/secure v1.10.0 h1:TBNP42z2AB+2pW9PR6vdbqhlQuv1iTeSVzK1qHjOBzA= @@ -1713,6 +1750,8 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17 github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg= +github.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1743,14 +1782,26 @@ go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3C go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw= go.mongodb.org/mongo-driver v1.7.0/go.mod h1:Q4oFMbo1+MSNqICAdYMlC/zSTrwCogR4R8NzkI+yfU8= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.31.0/go.mod h1:PFmBsWbldL1kiWZk9+0LBZz2brhByaGsvp6pRICMlPE= go.opentelemetry.io/otel v0.11.0/go.mod h1:G8UCk+KooF2HLkgo8RHX9epABH/aRGYET7gQOqBVdB0= +go.opentelemetry.io/otel v1.6.0/go.mod h1:bfJD2DZVw0LBxghOTlgnlI0CV3hLDu9XF/QKOUXMTQQ= +go.opentelemetry.io/otel v1.6.1/go.mod h1:blzUabWHkX6LJewxvadmzafgh/wnvBSDBdOuwkAtrWQ= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.1/go.mod h1:NEu79Xo32iVb+0gVNV8PMd7GoWqnyDXRlj04yFjqz40= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.1/go.mod h1:YJ/JbY5ag/tSQFXzH3mtDmHqzF3aFn3DI/aB1n7pt4w= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.6.1/go.mod h1:UJJXJj0rltNIemDMwkOJyggsvyMG9QHfJeFH0HS5JjM= +go.opentelemetry.io/otel/metric v0.28.0/go.mod h1:TrzsfQAmQaB1PDcdhBauLMk7nyyg9hm+GoQq/ekE9Iw= +go.opentelemetry.io/otel/sdk v1.6.1/go.mod h1:IVYrddmFZ+eJqu2k38qD3WezFR2pymCzm8tdxyh3R4E= +go.opentelemetry.io/otel/trace v1.6.0/go.mod h1:qs7BrU5cZ8dXQHBGxHMOxwME/27YH2qEp4/+tZLLwJE= +go.opentelemetry.io/otel/trace v1.6.1/go.mod h1:RkFRM1m0puWIq10oxImnGEduNBzxiN7TXluRBtE+5j0= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.12.1/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -1768,6 +1819,7 @@ golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -1937,6 +1989,7 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211013171255-e13a2654a71e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1992,6 +2045,7 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2080,6 +2134,7 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2143,6 +2198,7 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -2413,6 +2469,7 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=