mirror of
https://github.com/coder/coder.git
synced 2026-06-03 21:18:24 +00:00
bddb808b25
Fixes all our Go file imports to match the preferred spec that we've _mostly_ been using. For example: ``` import ( "context" "time" "github.com/prometheus/client_golang/prometheus" "golang.org/x/xerrors" "gopkg.in/natefinch/lumberjack.v2" "cdr.dev/slog/v3" "github.com/coder/coder/v2/codersdk/agentsdk" "github.com/coder/serpent" ) ``` 3 groups: standard library, 3rd partly libs, Coder libs. This PR makes the change across the codebase. The PR in the stack above modifies our formatting to maintain this state of affairs, and is a separate PR so it's possible to review that one in detail.
107 lines
3.5 KiB
Go
107 lines
3.5 KiB
Go
package workspaceapps
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/url"
|
|
"time"
|
|
|
|
"cdr.dev/slog/v3"
|
|
"github.com/coder/coder/v2/codersdk"
|
|
)
|
|
|
|
const (
|
|
// TODO(@deansheather): configurable expiry
|
|
DefaultTokenExpiry = time.Minute
|
|
|
|
// RedirectURIQueryParam is the query param for the app URL to be passed
|
|
// back to the API auth endpoint on the main access URL.
|
|
RedirectURIQueryParam = "redirect_uri"
|
|
)
|
|
|
|
type ResolveRequestOptions struct {
|
|
Logger slog.Logger
|
|
SignedTokenProvider SignedTokenProvider
|
|
Cookies AppCookies
|
|
CookieCfg codersdk.HTTPCookieConfig
|
|
|
|
DashboardURL *url.URL
|
|
PathAppBaseURL *url.URL
|
|
AppHostname string
|
|
|
|
AppRequest Request
|
|
// TODO: Replace these 2 fields with a "BrowserURL" field which is used for
|
|
// redirecting the user back to their initial request after authenticating.
|
|
// AppPath is the path under the app that was hit.
|
|
AppPath string
|
|
// AppQuery is the raw query of the request.
|
|
AppQuery string
|
|
}
|
|
|
|
func ResolveRequest(rw http.ResponseWriter, r *http.Request, opts ResolveRequestOptions) (*SignedToken, bool) {
|
|
appReq := opts.AppRequest.Normalize()
|
|
err := appReq.Check()
|
|
if err != nil {
|
|
// This is a 500 since it's a coder server or proxy that's making this
|
|
// request struct based on details from the request. The values should
|
|
// already be validated before they are put into the struct.
|
|
WriteWorkspaceApp500(opts.Logger, opts.DashboardURL, rw, r, &appReq, err, "invalid app request")
|
|
return nil, false
|
|
}
|
|
|
|
token, ok := opts.SignedTokenProvider.FromRequest(r)
|
|
if ok && token.MatchesRequest(appReq) {
|
|
// The request has a valid signed app token and it matches the request.
|
|
return token, true
|
|
}
|
|
|
|
issueReq := IssueTokenRequest{
|
|
AppRequest: appReq,
|
|
PathAppBaseURL: opts.PathAppBaseURL.String(),
|
|
AppHostname: opts.AppHostname,
|
|
SessionToken: opts.Cookies.TokenFromRequest(r, appReq.AccessMethod),
|
|
AppPath: opts.AppPath,
|
|
AppQuery: opts.AppQuery,
|
|
}
|
|
|
|
token, tokenStr, ok := opts.SignedTokenProvider.Issue(r.Context(), rw, r, issueReq)
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
|
|
// Write the signed app token cookie.
|
|
//
|
|
// For path apps, this applies to only the path app base URL on the current
|
|
// domain, e.g.
|
|
// /@user/workspace[.agent]/apps/path-app/
|
|
//
|
|
// For subdomain apps, this applies to the entire subdomain, e.g.
|
|
// app--agent--workspace--user.apps.example.com
|
|
http.SetCookie(rw, opts.CookieCfg.Apply(&http.Cookie{
|
|
Name: codersdk.SignedAppTokenCookie,
|
|
Value: tokenStr,
|
|
Path: appReq.BasePath,
|
|
HttpOnly: true,
|
|
Expires: token.Expiry.Time(),
|
|
}))
|
|
|
|
return token, true
|
|
}
|
|
|
|
// SignedTokenProvider provides signed workspace app tokens (aka. app tickets).
|
|
type SignedTokenProvider interface {
|
|
// FromRequest returns a parsed token from the request. If the request does
|
|
// not contain a signed app token or is is invalid (expired, invalid
|
|
// signature, etc.), it returns false.
|
|
FromRequest(r *http.Request) (*SignedToken, bool)
|
|
// Issue mints a new token for the given app request. It uses the long-lived
|
|
// session token in the HTTP request to authenticate and authorize the
|
|
// client for the given workspace app. The token is returned in struct and
|
|
// string form. The string form should be written as a cookie.
|
|
//
|
|
// If the request is invalid or the user is not authorized to access the
|
|
// app, false is returned. An error page is written to the response writer
|
|
// in this case.
|
|
Issue(ctx context.Context, rw http.ResponseWriter, r *http.Request, appReq IssueTokenRequest) (*SignedToken, string, bool)
|
|
}
|