diff --git a/pkg/service/v0/errorcode/errorcode.go b/pkg/service/v0/errorcode/errorcode.go new file mode 100644 index 0000000000..6567d02128 --- /dev/null +++ b/pkg/service/v0/errorcode/errorcode.go @@ -0,0 +1,75 @@ +package errorcode + +import ( + "net/http" + + "github.com/go-chi/render" + msgraph "github.com/yaegashi/msgraph.go/v1.0" +) + +// ErrorCode defines code as used in MS Graph - see https://docs.microsoft.com/en-us/graph/errors?context=graph%2Fapi%2F1.0&view=graph-rest-1.0 +type ErrorCode int + +const ( + // AccessDenied defines the error if the caller doesn't have permission to perform the action. + AccessDenied ErrorCode = iota + // ActivityLimitReached defines the error if the app or user has been throttled. + ActivityLimitReached + // GeneralException defines the error if an unspecified error has occurred. + GeneralException + // InvalidRange defines the error if the specified byte range is invalid or unavailable. + InvalidRange + // InvalidRequest defines the error if the request is malformed or incorrect. + InvalidRequest + // ItemNotFound defines the error if the resource could not be found. + ItemNotFound + // MalwareDetected defines the error if malware was detected in the requested resource. + MalwareDetected + // NameAlreadyExists defines the error if the specified item name already exists. + NameAlreadyExists + // NotAllowed defines the error if the action is not allowed by the system. + NotAllowed + // NotSupported defines the error if the request is not supported by the system. + NotSupported + // ResourceModified defines the error if the resource being updated has changed since the caller last read it, usually an eTag mismatch. + ResourceModified + // ResyncRequired defines the error if the delta token is no longer valid, and the app must reset the sync state. + ResyncRequired + // ServiceNotAvailable defines the error if the service is not available. Try the request again after a delay. There may be a Retry-After header. + ServiceNotAvailable + // QuotaLimitReached the user has reached their quota limit. + QuotaLimitReached + // Unauthenticated the caller is not authenticated. + Unauthenticated +) + +var errorCodes = [...]string{ + "accessDenied", + "activityLimitReached", + "generalException", + "invalidRange", + "invalidRequest", + "itemNotFound", + "malwareDetected", + "nameAlreadyExists", + "notAllowed", + "notSupported", + "resourceModified", + "resyncRequired", + "serviceNotAvailable", + "quotaLimitReached", + "unauthenticated", +} + +// Render writes an Graph ErrorObject to the response writer +func (e ErrorCode) Render(w http.ResponseWriter, r *http.Request, status int) { + resp := &msgraph.ErrorObject{ + Code: e.String(), + } + render.Status(r, status) + render.JSON(w, r, resp) +} + +func (e ErrorCode) String() string { + return errorCodes[e] +} diff --git a/pkg/service/v0/graph.go b/pkg/service/v0/graph.go index a7f871f8ce..5dcdbe5e0d 100644 --- a/pkg/service/v0/graph.go +++ b/pkg/service/v0/graph.go @@ -7,6 +7,8 @@ import ( "fmt" "net/http" + "github.com/owncloud/ocis-graph/pkg/service/v0/errorcode" + "github.com/go-chi/chi" "github.com/go-chi/render" "github.com/owncloud/ocis-graph/pkg/config" @@ -35,17 +37,15 @@ func (g Graph) UserCtx(next http.Handler) http.Handler { var user *ldap.Entry var err error - if userID := chi.URLParam(r, "userID"); userID != "" { - user, err = g.ldapGetUser(userID) - } else { - // TODO: we should not give this error out to users - // http.Error(w, err.Error(), http.StatusInternalServerError) - render.Status(r, http.StatusNotFound) + userID := chi.URLParam(r, "userID") + if userID == "" { + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest) return } + user, err = g.ldapGetUser(userID) if err != nil { - g.logger.Info().Msgf("error reading user: %s", err.Error()) - render.Status(r, http.StatusNotFound) + g.logger.Info().Err(err).Msgf("Failed to read user %s", userID) + errorcode.ItemNotFound.Render(w, r, http.StatusNotFound) return } @@ -64,7 +64,8 @@ func (g Graph) GetMe(w http.ResponseWriter, r *http.Request) { resp, err := json.Marshal(me) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + g.logger.Error().Err(err).Msgf("Failed to marshal object %s", me) + errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError) return } @@ -76,16 +77,16 @@ func (g Graph) GetMe(w http.ResponseWriter, r *http.Request) { func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) { con, err := g.initLdap() if err != nil { - // TODO: we should not give this error out to users - http.Error(w, err.Error(), http.StatusInternalServerError) + g.logger.Error().Err(err).Msg("Failed to initialize ldap") + errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError) return } result, err := g.ldapSearch(con, "(objectclass=*)") if err != nil { - // TODO: we should not give this error out to users - http.Error(w, err.Error(), http.StatusInternalServerError) + g.logger.Error().Err(err).Msg("Failed search ldap with filter: '(objectclass=*)'") + errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError) return }