fix: Protect codersdk.Client SessionToken so it can be updated (#4965)

This feature is used by the coder agent to exchange a new token. By
protecting the SessionToken via mutex we ensure there are no data races
when accessing it.
This commit is contained in:
Mathias Fredriksson
2022-11-09 15:31:24 +02:00
committed by GitHub
parent 8cadb33396
commit 26ab0d37c1
25 changed files with 82 additions and 64 deletions

View File

@ -11,6 +11,7 @@ import (
"net/http"
"net/url"
"strings"
"sync"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
@ -56,9 +57,11 @@ func New(serverURL *url.URL) *Client {
// Client is an HTTP caller for methods to the Coder API.
// @typescript-ignore Client
type Client struct {
HTTPClient *http.Client
SessionToken string
URL *url.URL
mu sync.RWMutex // Protects following.
sessionToken string
HTTPClient *http.Client
URL *url.URL
// Logger can be provided to log requests. Request method, URL and response
// status code will be logged by default.
@ -77,12 +80,27 @@ type Client struct {
PropagateTracing bool
}
func (c *Client) SessionToken() string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.sessionToken
}
func (c *Client) SetSessionToken(token string) {
c.mu.Lock()
defer c.mu.Unlock()
c.sessionToken = token
}
func (c *Client) Clone() *Client {
c.mu.Lock()
defer c.mu.Unlock()
hc := *c.HTTPClient
u := *c.URL
return &Client{
HTTPClient: &hc,
SessionToken: c.SessionToken,
sessionToken: c.sessionToken,
URL: &u,
Logger: c.Logger,
LogBodies: c.LogBodies,
@ -147,7 +165,7 @@ func (c *Client) Request(ctx context.Context, method, path string, body interfac
if err != nil {
return nil, xerrors.Errorf("create request: %w", err)
}
req.Header.Set(SessionCustomHeader, c.SessionToken)
req.Header.Set(SessionCustomHeader, c.SessionToken())
if c.BypassRatelimits {
req.Header.Set(BypassRatelimitHeader, "true")
}

View File

@ -58,7 +58,7 @@ func Test_Client(t *testing.T) {
u, err := url.Parse(s.URL)
require.NoError(t, err)
client := New(u)
client.SessionToken = token
client.SetSessionToken(token)
client.BypassRatelimits = true
logBuf := bytes.NewBuffer(nil)

View File

@ -121,7 +121,7 @@ func (c *Client) provisionerJobLogsAfter(ctx context.Context, path string, after
}
jar.SetCookies(followURL, []*http.Cookie{{
Name: SessionTokenKey,
Value: c.SessionToken,
Value: c.SessionToken(),
}})
httpClient := &http.Client{
Jar: jar,

View File

@ -319,7 +319,7 @@ func (c *Client) ListenWorkspaceAgent(ctx context.Context) (net.Conn, error) {
}
jar.SetCookies(coordinateURL, []*http.Cookie{{
Name: SessionTokenKey,
Value: c.SessionToken,
Value: c.SessionToken(),
}})
httpClient := &http.Client{
Jar: jar,
@ -385,7 +385,7 @@ func (c *Client) DialWorkspaceAgent(ctx context.Context, agentID uuid.UUID, opti
}
jar.SetCookies(coordinateURL, []*http.Cookie{{
Name: SessionTokenKey,
Value: c.SessionToken,
Value: c.SessionToken(),
}})
httpClient := &http.Client{
Jar: jar,
@ -508,7 +508,7 @@ func (c *Client) WorkspaceAgentReconnectingPTY(ctx context.Context, agentID, rec
}
jar.SetCookies(serverURL, []*http.Cookie{{
Name: SessionTokenKey,
Value: c.SessionToken,
Value: c.SessionToken(),
}})
httpClient := &http.Client{
Jar: jar,
@ -569,7 +569,7 @@ func (c *Client) AgentReportStats(
jar.SetCookies(serverURL, []*http.Cookie{{
Name: SessionTokenKey,
Value: c.SessionToken,
Value: c.SessionToken(),
}})
httpClient := &http.Client{