Files
headscale/hscontrol/api/v1/errors.go
Michael Lopez 42a97be25f state: validate the new name and refuse OIDC users in RenameUser (#3642)
State.RenameUser went through UpdateUser and skipped the checks
db.RenameUser applies, so the API could rename an OIDC user, whose
name the identity provider owns, or rename any user to an invalid
name. Apply both checks in the update function and map the username
validation errors to 400.
2026-11-02 20:22:23 +01:00

67 lines
2.3 KiB
Go

package apiv1
import (
"errors"
"github.com/danielgtaylor/huma/v2"
"github.com/juanfont/headscale/hscontrol/db"
"github.com/juanfont/headscale/hscontrol/state"
"github.com/juanfont/headscale/hscontrol/types"
"github.com/juanfont/headscale/hscontrol/util"
"gorm.io/gorm"
)
// mapError translates a state/db-layer error into a Huma HTTP error
// (NotFound→404, invalid input→400, conflict→409, everything else→500).
// Handlers use this default mapping and may return a more specific huma.ErrorN
// directly. msg is a human context prefix, e.g. "getting node".
func mapError(msg string, err error) error {
if err == nil {
return nil
}
switch {
case errors.Is(err, types.ErrRegistrationPayloadTooLarge):
return huma.Error413RequestEntityTooLarge(msg, err)
case errors.Is(err, state.ErrAuthCacheFull),
errors.Is(err, state.ErrAuthCacheClosed):
return huma.Error503ServiceUnavailable(msg, err)
case errors.Is(err, gorm.ErrRecordNotFound),
errors.Is(err, state.ErrNodeNotFound),
errors.Is(err, state.ErrNodeNotInNodeStore),
errors.Is(err, db.ErrUserNotFound),
errors.Is(err, db.ErrNodeNotFoundRegistrationCache),
errors.Is(err, state.ErrRegistrationExpired):
return huma.Error404NotFound(msg, err)
case errors.Is(err, state.ErrGivenNameInvalid),
errors.Is(err, state.ErrGivenNameTaken),
errors.Is(err, state.ErrNodeNameNotUnique),
errors.Is(err, state.ErrNodeMarkedTaggedButHasNoTags),
errors.Is(err, state.ErrNodeHasNeitherUserNorTags),
errors.Is(err, state.ErrRequestedTagsInvalidOrNotPermitted),
errors.Is(err, types.ErrExpiryOutOfRange),
errors.Is(err, types.ErrHostinfoTooLarge),
errors.Is(err, db.ErrUserStillHasNodes),
errors.Is(err, db.ErrCannotChangeOIDCUser),
errors.Is(err, util.ErrUsernameTooShort),
errors.Is(err, util.ErrUsernameMustStartLetter),
errors.Is(err, util.ErrUsernameTooManyAt),
errors.Is(err, util.ErrUsernameInvalidChar),
errors.Is(err, db.ErrPreAuthKeyNotTaggedOrOwned),
errors.Is(err, db.ErrSingleUseAuthKeyHasBeenUsed),
errors.Is(err, state.ErrInvalidPolicy):
return huma.Error400BadRequest(msg, err)
case errors.Is(err, state.ErrNodeKeyInUse),
errors.Is(err, state.ErrAmbiguousNodeOwnership),
errors.Is(err, state.ErrAuthCacheEntryExists):
return huma.Error409Conflict(msg, err)
default:
return huma.Error500InternalServerError(msg, err)
}
}