ExtractWorkspaceAgentAndLatestBuild requires authentication using a valid agent token.
(opts ExtractWorkspaceAgentAndLatestBuildConfig)
| 58 | |
| 59 | // ExtractWorkspaceAgentAndLatestBuild requires authentication using a valid agent token. |
| 60 | func ExtractWorkspaceAgentAndLatestBuild(opts ExtractWorkspaceAgentAndLatestBuildConfig) func(http.Handler) http.Handler { |
| 61 | return func(next http.Handler) http.Handler { |
| 62 | return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { |
| 63 | ctx := r.Context() |
| 64 | |
| 65 | // optionalWrite wraps httpapi.Write but runs the next handler if the |
| 66 | // token is optional. |
| 67 | // |
| 68 | // It should be used when the token is not provided or is invalid, but not |
| 69 | // when there are other errors. |
| 70 | optionalWrite := func(code int, response codersdk.Response) { |
| 71 | if opts.Optional { |
| 72 | next.ServeHTTP(rw, r) |
| 73 | return |
| 74 | } |
| 75 | httpapi.Write(ctx, rw, code, response) |
| 76 | } |
| 77 | |
| 78 | tokenValue := APITokenFromRequest(r) |
| 79 | if tokenValue == "" { |
| 80 | optionalWrite(http.StatusUnauthorized, codersdk.Response{ |
| 81 | Message: fmt.Sprintf("Cookie %q must be provided.", codersdk.SessionTokenCookie), |
| 82 | }) |
| 83 | return |
| 84 | } |
| 85 | token, err := uuid.Parse(tokenValue) |
| 86 | if err != nil { |
| 87 | optionalWrite(http.StatusUnauthorized, codersdk.Response{ |
| 88 | Message: "Workspace agent token invalid.", |
| 89 | Detail: fmt.Sprintf("An agent token must be a valid UUIDv4. (len %d)", len(tokenValue)), |
| 90 | }) |
| 91 | return |
| 92 | } |
| 93 | |
| 94 | //nolint:gocritic // System needs to be able to get workspace agents. |
| 95 | row, err := opts.DB.GetAuthenticatedWorkspaceAgentAndBuildByAuthToken(dbauthz.AsSystemRestricted(ctx), token) |
| 96 | if err != nil { |
| 97 | if errors.Is(err, sql.ErrNoRows) { |
| 98 | optionalWrite(http.StatusUnauthorized, codersdk.Response{ |
| 99 | Message: "Workspace agent not authorized.", |
| 100 | Detail: "The agent cannot authenticate until the workspace provision job has been completed. If the job is no longer running, this agent is invalid.", |
| 101 | }) |
| 102 | return |
| 103 | } |
| 104 | |
| 105 | httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ |
| 106 | Message: "Internal error checking workspace agent authorization.", |
| 107 | Detail: err.Error(), |
| 108 | }) |
| 109 | return |
| 110 | } |
| 111 | |
| 112 | subject, _, err := UserRBACSubject( |
| 113 | ctx, |
| 114 | opts.DB, |
| 115 | row.WorkspaceTable.OwnerID, |
| 116 | rbac.WorkspaceAgentScope(rbac.WorkspaceAgentScopeParams{ |
| 117 | WorkspaceID: row.WorkspaceTable.ID, |