mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
This PR migrates 224 typed JSON response sites across 46 files to `codersdk.ReadBodyAsJSON`, so invalid 2xx bodies return structured errors while preserving URL credential redaction. It intentionally excludes agent-direct HTTP, Azure IMDS, `UseNumber`, and chat paths; stacked on coder/coder#27804, with chat and lint follow-ups in coder/coder#27858 and coder/coder#27859. Refs coder/coder#27044. Reviewed and updated by Coder Agents on behalf of @dylanhuff-at-coder.
148 lines
5.2 KiB
Go
148 lines
5.2 KiB
Go
package codersdk
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/google/uuid"
|
|
"golang.org/x/xerrors"
|
|
)
|
|
|
|
const (
|
|
WorkspaceAgentPortShareLevelOwner WorkspaceAgentPortShareLevel = "owner"
|
|
WorkspaceAgentPortShareLevelAuthenticated WorkspaceAgentPortShareLevel = "authenticated"
|
|
WorkspaceAgentPortShareLevelOrganization WorkspaceAgentPortShareLevel = "organization"
|
|
WorkspaceAgentPortShareLevelPublic WorkspaceAgentPortShareLevel = "public"
|
|
|
|
WorkspaceAgentPortShareProtocolHTTP WorkspaceAgentPortShareProtocol = "http"
|
|
WorkspaceAgentPortShareProtocolHTTPS WorkspaceAgentPortShareProtocol = "https"
|
|
)
|
|
|
|
type (
|
|
WorkspaceAgentPortShareLevel string
|
|
WorkspaceAgentPortShareProtocol string
|
|
UpsertWorkspaceAgentPortShareRequest struct {
|
|
AgentName string `json:"agent_name"`
|
|
Port int32 `json:"port"`
|
|
ShareLevel WorkspaceAgentPortShareLevel `json:"share_level" enums:"owner,authenticated,organization,public"`
|
|
Protocol WorkspaceAgentPortShareProtocol `json:"protocol" enums:"http,https"`
|
|
}
|
|
WorkspaceAgentPortShares struct {
|
|
Shares []WorkspaceAgentPortShare `json:"shares"`
|
|
}
|
|
WorkspaceAgentPortShare struct {
|
|
WorkspaceID uuid.UUID `json:"workspace_id" format:"uuid"`
|
|
AgentName string `json:"agent_name"`
|
|
Port int32 `json:"port"`
|
|
ShareLevel WorkspaceAgentPortShareLevel `json:"share_level" enums:"owner,authenticated,organization,public"`
|
|
Protocol WorkspaceAgentPortShareProtocol `json:"protocol" enums:"http,https"`
|
|
}
|
|
DeleteWorkspaceAgentPortShareRequest struct {
|
|
AgentName string `json:"agent_name"`
|
|
Port int32 `json:"port"`
|
|
}
|
|
)
|
|
|
|
func (l WorkspaceAgentPortShareLevel) ValidMaxLevel() bool {
|
|
return l == WorkspaceAgentPortShareLevelOwner ||
|
|
l == WorkspaceAgentPortShareLevelAuthenticated ||
|
|
l == WorkspaceAgentPortShareLevelOrganization ||
|
|
l == WorkspaceAgentPortShareLevelPublic
|
|
}
|
|
|
|
func (l WorkspaceAgentPortShareLevel) ValidPortShareLevel() bool {
|
|
return l == WorkspaceAgentPortShareLevelAuthenticated ||
|
|
l == WorkspaceAgentPortShareLevelOrganization ||
|
|
l == WorkspaceAgentPortShareLevelPublic
|
|
}
|
|
|
|
// IsCompatibleWithMaxLevel determines whether the sharing level is valid under
|
|
// the specified maxLevel. The values are fully ordered, from "highest" to
|
|
// "lowest" as
|
|
// 1. Public
|
|
// 2. Authenticated
|
|
// 3. Organization
|
|
// 4. Owner
|
|
// Returns an error if either level is invalid.
|
|
func (l WorkspaceAgentPortShareLevel) IsCompatibleWithMaxLevel(maxLevel WorkspaceAgentPortShareLevel) error {
|
|
// Owner is always allowed.
|
|
if l == WorkspaceAgentPortShareLevelOwner {
|
|
return nil
|
|
}
|
|
// If public is allowed, anything is allowed.
|
|
if maxLevel == WorkspaceAgentPortShareLevelPublic {
|
|
return nil
|
|
}
|
|
// Public is not allowed.
|
|
if l == WorkspaceAgentPortShareLevelPublic {
|
|
return xerrors.Errorf("%q sharing level is not allowed under max level %q", l, maxLevel)
|
|
}
|
|
// If authenticated is allowed, public has already been filtered out so
|
|
// anything is allowed.
|
|
if maxLevel == WorkspaceAgentPortShareLevelAuthenticated {
|
|
return nil
|
|
}
|
|
// Authenticated is not allowed.
|
|
if l == WorkspaceAgentPortShareLevelAuthenticated {
|
|
return xerrors.Errorf("%q sharing level is not allowed under max level %q", l, maxLevel)
|
|
}
|
|
// If organization is allowed, public and authenticated have already been
|
|
// filtered out so anything is allowed.
|
|
if maxLevel == WorkspaceAgentPortShareLevelOrganization {
|
|
return nil
|
|
}
|
|
// Organization is not allowed.
|
|
if l == WorkspaceAgentPortShareLevelOrganization {
|
|
return xerrors.Errorf("%q sharing level is not allowed under max level %q", l, maxLevel)
|
|
}
|
|
|
|
// An invalid value was provided.
|
|
return xerrors.New("port sharing level is invalid.")
|
|
}
|
|
|
|
func (p WorkspaceAgentPortShareProtocol) ValidPortProtocol() bool {
|
|
return p == WorkspaceAgentPortShareProtocolHTTP ||
|
|
p == WorkspaceAgentPortShareProtocolHTTPS
|
|
}
|
|
|
|
func (c *Client) GetWorkspaceAgentPortShares(ctx context.Context, workspaceID uuid.UUID) (WorkspaceAgentPortShares, error) {
|
|
var shares WorkspaceAgentPortShares
|
|
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/workspaces/%s/port-share", workspaceID), nil)
|
|
if err != nil {
|
|
return shares, err
|
|
}
|
|
defer res.Body.Close()
|
|
if res.StatusCode != http.StatusOK {
|
|
return shares, ReadBodyAsError(res)
|
|
}
|
|
|
|
return shares, ReadBodyAsJSON(res, &shares)
|
|
}
|
|
|
|
func (c *Client) UpsertWorkspaceAgentPortShare(ctx context.Context, workspaceID uuid.UUID, req UpsertWorkspaceAgentPortShareRequest) (WorkspaceAgentPortShare, error) {
|
|
var share WorkspaceAgentPortShare
|
|
res, err := c.Request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/workspaces/%s/port-share", workspaceID), req)
|
|
if err != nil {
|
|
return share, err
|
|
}
|
|
defer res.Body.Close()
|
|
if res.StatusCode != http.StatusOK {
|
|
return share, ReadBodyAsError(res)
|
|
}
|
|
|
|
return share, ReadBodyAsJSON(res, &share)
|
|
}
|
|
|
|
func (c *Client) DeleteWorkspaceAgentPortShare(ctx context.Context, workspaceID uuid.UUID, req DeleteWorkspaceAgentPortShareRequest) error {
|
|
res, err := c.Request(ctx, http.MethodDelete, fmt.Sprintf("/api/v2/workspaces/%s/port-share", workspaceID), req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer res.Body.Close()
|
|
if res.StatusCode != http.StatusOK {
|
|
return ReadBodyAsError(res)
|
|
}
|
|
return nil
|
|
}
|