chore: support signed token query param for web terminal (#7197)

* chore: add endpoint to get token for web terminal

* chore: support signed token query param for web terminal
This commit is contained in:
Dean Sheather
2023-04-20 16:59:45 -07:00
committed by GitHub
parent ac3c530283
commit 68667323f3
25 changed files with 886 additions and 164 deletions

View File

@ -10,10 +10,12 @@ import (
"github.com/google/uuid"
"golang.org/x/xerrors"
agpl "github.com/coder/coder/coderd"
"github.com/coder/coder/coderd/audit"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/httpapi"
"github.com/coder/coder/coderd/httpmw"
"github.com/coder/coder/coderd/rbac"
"github.com/coder/coder/coderd/workspaceapps"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/cryptorand"
@ -314,3 +316,99 @@ func (api *API) workspaceProxyRegister(rw http.ResponseWriter, r *http.Request)
AppSecurityKey: api.AppSecurityKey.String(),
})
}
// reconnectingPTYSignedToken issues a signed app token for use when connecting
// to the reconnecting PTY websocket on an external workspace proxy. This is set
// by the client as a query parameter when connecting.
//
// @Summary Issue signed app token for reconnecting PTY
// @ID issue-signed-app-token-for-reconnecting-pty
// @Security CoderSessionToken
// @Tags Applications Enterprise
// @Accept json
// @Produce json
// @Param request body codersdk.IssueReconnectingPTYSignedTokenRequest true "Issue reconnecting PTY signed token request"
// @Success 200 {object} codersdk.IssueReconnectingPTYSignedTokenResponse
// @Router /applications/reconnecting-pty-signed-token [post]
// @x-apidocgen {"skip": true}
func (api *API) reconnectingPTYSignedToken(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
apiKey := httpmw.APIKey(r)
if !api.Authorize(r, rbac.ActionCreate, apiKey) {
httpapi.ResourceNotFound(rw)
return
}
var req codersdk.IssueReconnectingPTYSignedTokenRequest
if !httpapi.Read(ctx, rw, r, &req) {
return
}
u, err := url.Parse(req.URL)
if err == nil && u.Scheme != "ws" && u.Scheme != "wss" {
err = xerrors.Errorf("invalid URL scheme %q, expected 'ws' or 'wss'", u.Scheme)
}
if err != nil {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Invalid URL.",
Detail: err.Error(),
})
return
}
// Assert the URL is a valid reconnecting-pty URL.
expectedPath := fmt.Sprintf("/api/v2/workspaceagents/%s/pty", req.AgentID.String())
if u.Path != expectedPath {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Invalid URL path.",
Detail: "The provided URL is not a valid reconnecting PTY endpoint URL.",
})
return
}
scheme, err := api.AGPL.ValidWorkspaceAppHostname(ctx, u.Host, agpl.ValidWorkspaceAppHostnameOpts{
// Only allow the proxy access URL as a hostname since we don't need a
// ticket for the primary dashboard URL terminal.
AllowPrimaryAccessURL: false,
AllowPrimaryWildcard: false,
AllowProxyAccessURL: true,
AllowProxyWildcard: false,
})
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to verify hostname in URL.",
Detail: err.Error(),
})
return
}
if scheme == "" {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Invalid hostname in URL.",
Detail: "The hostname must be the primary wildcard app hostname, a workspace proxy access URL or a workspace proxy wildcard app hostname.",
})
return
}
_, tokenStr, ok := api.AGPL.WorkspaceAppsProvider.Issue(ctx, rw, r, workspaceapps.IssueTokenRequest{
AppRequest: workspaceapps.Request{
AccessMethod: workspaceapps.AccessMethodTerminal,
BasePath: u.Path,
AgentNameOrID: req.AgentID.String(),
},
SessionToken: httpmw.APITokenFromRequest(r),
// The following fields aren't required as long as the request is authed
// with a valid API key.
PathAppBaseURL: "",
AppHostname: "",
// The following fields are empty for terminal apps.
AppPath: "",
AppQuery: "",
})
if !ok {
return
}
httpapi.Write(ctx, rw, http.StatusOK, codersdk.IssueReconnectingPTYSignedTokenResponse{
SignedToken: tokenStr,
})
}