8000 chore: More UI friendly errors by Emyrk · Pull Request #1994 · coder/coder · GitHub
[go: up one dir, main page]

Skip to content

chore: More UI friendly errors #1994

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
chore: More friendly errors as responses
  • Loading branch information
Emyrk committed Jun 3, 2022
commit 46b52d5e316e785a5b5fbd15d18a4e75c4c2b679
8 changes: 5 additions & 3 deletions coderd/httpapi/httpapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ type Error struct {

func Forbidden(rw http.ResponseWriter) {
Write(rw, http.StatusForbidden, Response{
Message: "forbidden",
Message: "Forbidden",
})
}

Expand Down Expand Up @@ -104,7 +104,8 @@ func Read(rw http.ResponseWriter, r *http.Request, value interface{}) bool {
err := json.NewDecoder(r.Body).Decode(value)
if err != nil {
Write(rw, http.StatusBadRequest, Response{
Message: fmt.Sprintf("read body: %s", err.Error()),
Message: "Request body must be valid json",
Internal: err.Error(),
})
return false
}
Expand All @@ -126,7 +127,8 @@ func Read(rw http.ResponseWriter, r *http.Request, value interface{}) bool {
}
if err != nil {
Write(rw, http.StatusInternalServerError, Response{
Message: fmt.Sprintf("validation: %s", err.Error()),
Message: "Internal error validating request body payload",
Internal: err.Error(),
})
return false
}
Expand Down
29 changes: 16 additions & 13 deletions coderd/httpmw/apikey.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,15 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
}
if cookieValue == "" {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("%q cookie or query parameter must be provided", SessionTokenKey),
Message: fmt.Sprintf("Cookie %q or query parameter must be provided", SessionTokenKey),
})
return
}
parts := strings.Split(cookieValue, "-")
// APIKeys are formatted: ID-SECRET
if len(parts) != 2 {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("invalid %q cookie api key format", SessionTokenKey),
Message: fmt.Sprintf("Invalid %q cookie api key format", SessionTokenKey),
})
return
}
Expand All @@ -82,26 +82,27 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
// Ensuring key lengths are valid.
if len(keyID) != 10 {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("invalid %q cookie api key id", SessionTokenKey),
Message: fmt.Sprintf("Invalid %q cookie api key id", SessionTokenKey),
})
return
}
if len(keySecret) != 22 {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("invalid %q cookie api key secret", SessionTokenKey),
Message: fmt.Sprintf("Invalid %q cookie api key secret", SessionTokenKey),
})
return
}
key, err := db.GetAPIKeyByID(r.Context(), keyID)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: "api key is invalid",
Message: "Api key is invalid",
})
return
}
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get api key by id: %s", err.Error()),
Message: "Internal error fetching api key by id",
Internal: err.Error(),
})
return
}
Expand All @@ -110,7 +111,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
// Checking to see if the secret is valid.
if subtle.ConstantTimeCompare(key.HashedSecret, hashed[:]) != 1 {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: "api key secret is invalid",
Message: "Api key secret is invalid",
})
return
}
Expand All @@ -127,7 +128,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
oauthConfig = oauth.Github
default:
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("unexpected authentication type %q", key.LoginType),
Message: fmt.Sprintf("Unexpected authentication type %q", key.LoginType),
})
return
}
Expand All @@ -139,7 +140,8 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
}).Token()
if err != nil {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("couldn't refresh expired oauth token: %s", err.Error()),
Message: "Could not refresh expired oauth token",
Internal: err.Error(),
})
return
}
Expand All @@ -154,7 +156,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
// Checking if the key is expired.
if key.ExpiresAt.Before(now) {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("api key expired at %q", key.ExpiresAt.String()),
Message: fmt.Sprintf("Api key expired at %q", key.ExpiresAt.String()),
})
return
}
Expand Down Expand Up @@ -182,7 +184,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("api key couldn't update: %s", err.Error()),
Message: fmt.Sprintf("Api key couldn't update: %s", err.Error()),
})
return
}
Expand All @@ -194,14 +196,15 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
roles, err := db.GetAuthorizationUserRoles(r.Context(), key.UserID)
if err != nil {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: "roles not found",
Message: "Internal error fetching user's roles",
Internal: err.Error(),
})
return
}

if roles.Status != database.UserStatusActive {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("user is not active (status = %q), contact an admin to reactivate your account", roles.Status),
Message: fmt.Sprintf("User is not active (status = %q), contact an admin to reactivate your account", roles.Status),
})
return
}
Expand Down
7 changes: 5 additions & 2 deletions coderd/httpmw/httpmw.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@ func parseUUID(rw http.ResponseWriter, r *http.Request, param string) (uuid.UUID
rawID := chi.URLParam(r, param)
if rawID == "" {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("%q must be provided", param),
Message: "Missing uuid in url",
// Url params mean nothing to a user
Internal: fmt.Sprintf("%q url param missing", param),
})
return uuid.UUID{}, false
}

parsed, err := uuid.Parse(rawID)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("%q must be a uuid", param),
Message: fmt.Sprintf("Invalid uuid %q", param),
Internal: err.Error(),
})
return uuid.UUID{}, false
}
Expand Down 8000
12 changes: 7 additions & 5 deletions coderd/httpmw/oauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ func ExtractOAuth2(config OAuth2Config) func(http.Handler) http.Handler {
state, err := cryptorand.String(32)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("generate state string: %s", err),
Message: "Internal error generating state string",
Internal: err.Error(),
})
return
}
Expand Down Expand Up @@ -91,21 +92,21 @@ func ExtractOAuth2(config OAuth2Config) func(http.Handler) http.Handler {

if state == "" {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: "state must be provided",
Message: "State must be provided",
})
return
}

stateCookie, err := r.Cookie(oauth2StateCookieName)
if err != nil {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("%q cookie must be provided", oauth2StateCookieName),
Message: fmt.Sprintf("Cookie %q must be provided", oauth2StateCookieName),
})
return
}
if stateCookie.Value != state {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: "state mismatched",
Message: "State mismatched",
})
return
}
Expand All @@ -119,7 +120,8 @@ func ExtractOAuth2(config OAuth2Config) func(http.Handler) http.Handler {
oauthToken, err := config.Exchange(r.Context(), code)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("exchange oauth code: %s", err),
Message: "Internal error exchanging oauth code",
Internal: err.Error(),
})
return
}
Expand Down
10 changes: 6 additions & 4 deletions coderd/httpmw/organizationparam.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ func ExtractOrganizationParam(db database.Store) func(http.Handler) http.Handler
organization, err := db.GetOrganizationByID(r.Context(), orgID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("organization %q does not exist", orgID),
Message: fmt.Sprintf("Organization %q does not exist", orgID),
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get organization: %s", err.Error()),
Message: "Internal error fetching organization",
Internal: err.Error(),
})
return
}
Expand All @@ -76,13 +77,14 @@ func ExtractOrganizationMemberParam(db database.Store) func(http.Handler) http.H
})
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusForbidden, httpapi.Response{
Message: "not a member of the organization",
Message: "Not a member of the organization",
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get organization member: %s", err.Error()),
Message: "Internal error fetching organization member",
Internal: err.Error(),
})
return
}
Expand Down
7 changes: 4 additions & 3 deletions coderd/httpmw/templateparam.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,20 @@ func ExtractTemplateParam(db database.Store) func(http.Handler) http.Handler {
template, err := db.GetTemplateByID(r.Context(), templateID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("template %q does not exist", templateID),
Message: fmt.Sprintf("Template %q does not exist", templateID),
})
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template: %s", err),
Message: "Internal error fetching template",
Internal: err.Error(),
})
return
}

if template.Deleted {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("template %q does not exist", templateID),
Message: fmt.Sprintf("Template %q does not exist", templateID),
})
return
}
Expand Down
5 changes: 3 additions & 2 deletions coderd/httpmw/templateversionparam.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,14 @@ func ExtractTemplateVersionParam(db database.Store) func(http.Handler) http.Hand
templateVersion, err := db.GetTemplateVersionByID(r.Context(), templateVersionID)
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusNotFound, httpapi.Response{
Message: fmt.Sprintf("template version %q does not exist", templateVersionID),
Message: fmt.Sprintf("Template version %q does not exist", templateVersionID),
})
return
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get template version: %s", err.Error()),
Message: "Internal error fetching template version",
Internal: err.Error(),
})
return
}
Expand Down
4 changes: 2 additions & 2 deletions coderd/httpmw/userparam.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package httpmw

import (
"context"
"fmt"
"net/http"

"github.com/go-chi/chi/v5"
Expand Down Expand Up @@ -50,7 +49,8 @@ func ExtractUserParam(db database.Store) func(http.Handler) http.Handler {
user, err = db.GetUserByID(r.Context(), APIKey(r).UserID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get user: %s", err.Error()),
Message: "Internal error fetching user",
Internal: err.Error(),
})
return
}
Expand Down
9 changes: 5 additions & 4 deletions coderd/httpmw/workspaceagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,29 +31,30 @@ func ExtractWorkspaceAgent(db database.Store) func(http.Handler) http.Handler {
cookie, err := r.Cookie(SessionTokenKey)
if err != nil {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: fmt.Sprintf("%q cookie must be provided", SessionTokenKey),
Message: fmt.Sprintf("Cookie %q must be provided", SessionTokenKey),
})
return
}
token, err := uuid.Parse(cookie.Value)
if err != nil {
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
Message: fmt.Sprintf("parse token %q: %s", cookie.Value, err),
Message: fmt.Sprintf("Parse token %q: %s", cookie.Value, err),
})
return
}
agent, err := db.GetWorkspaceAgentByAuthToken(r.Context(), token)
if errors.Is(err, sql.ErrNoRows) {
if errors.Is(err, sql.ErrNoRows) {
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
Message: "agent token is invalid",
Message: "Agent token is invalid",
})
return
}
}
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: fmt.Sprintf("get workspace agent: %s", err),
Message: "Internal error fetching workspace agent",
Internal: err.Error(),
})
return
}
Expand Down
Loading
0