Improve overall app security by: - using dynamic statements for all sql querries - introducing environment variables for initial admin password - introducing enironment variable for cors address - improving error handling
205 lines
4.7 KiB
Go
205 lines
4.7 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
)
|
|
|
|
type ErrorCode string
|
|
|
|
const (
|
|
// Authentifizierung
|
|
ErrInvalidCredentials ErrorCode = "INVALID_CREDENTIALS"
|
|
ErrUnauthorized ErrorCode = "UNAUTHORIZED"
|
|
ErrTokenExpired ErrorCode = "TOKEN_EXPIRED"
|
|
ErrAccessDenied ErrorCode = "ACCESS_DENIED"
|
|
|
|
// Validierung
|
|
ErrInvalidInput ErrorCode = "INVALID_INPUT"
|
|
ErrMissingField ErrorCode = "MISSING_FIELD"
|
|
ErrInvalidDateFormat ErrorCode = "INVALID_DATE_FORMAT"
|
|
ErrInvalidTimeFormat ErrorCode = "INVALID_TIME_FORMAT"
|
|
|
|
// Ressourcen
|
|
ErrNotFound ErrorCode = "NOT_FOUND"
|
|
ErrAlreadyExists ErrorCode = "ALREADY_EXISTS"
|
|
ErrCannotDelete ErrorCode = "CANNOT_DELETE"
|
|
ErrProtectedUser ErrorCode = "PROTECTED_USER"
|
|
ErrNoActiveSchool ErrorCode = "NO_ACTIVE_SCHOOL_YEAR"
|
|
|
|
// Datenbank
|
|
ErrDatabase ErrorCode = "DATABASE_ERROR"
|
|
ErrTransaction ErrorCode = "TRANSACTION_ERROR"
|
|
ErrQueryFailed ErrorCode = "QUERY_FAILED"
|
|
|
|
// Server
|
|
ErrInternal ErrorCode = "INTERNAL_ERROR"
|
|
ErrServiceUnavail ErrorCode = "SERVICE_UNAVAILABLE"
|
|
)
|
|
|
|
type AppError struct {
|
|
Code ErrorCode `json:"code"`
|
|
Message string `json:"message"`
|
|
UserMsg string `json:"user_message"`
|
|
HTTPStatus int `json:"-"`
|
|
Internal error `json:"-"`
|
|
}
|
|
|
|
func (e *AppError) Error() string {
|
|
if e.Internal != nil {
|
|
return fmt.Sprintf("[%s] %s: %v", e.Code, e.Message, e.Internal)
|
|
}
|
|
return fmt.Sprintf("[%s] %s", e.Code, e.Message)
|
|
}
|
|
|
|
func NewAppError(code ErrorCode, message, userMsg string, httpStatus int, internal error) *AppError {
|
|
return &AppError{
|
|
Code: code,
|
|
Message: message,
|
|
UserMsg: userMsg,
|
|
HTTPStatus: httpStatus,
|
|
Internal: internal,
|
|
}
|
|
}
|
|
|
|
func ErrInvalidCredentialsMsg() *AppError {
|
|
return NewAppError(
|
|
ErrInvalidCredentials,
|
|
"Invalid username or password",
|
|
"Benutzername oder Passwort ungültig",
|
|
http.StatusUnauthorized,
|
|
nil,
|
|
)
|
|
}
|
|
|
|
func ErrUnauthorizedMsg() *AppError {
|
|
return NewAppError(
|
|
ErrUnauthorized,
|
|
"Unauthorized access",
|
|
"Keine Berechtigung für diese Aktion",
|
|
http.StatusUnauthorized,
|
|
nil,
|
|
)
|
|
}
|
|
|
|
func ErrTokenExpiredMsg() *AppError {
|
|
return NewAppError(
|
|
ErrTokenExpired,
|
|
"Token has expired",
|
|
"Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an",
|
|
http.StatusUnauthorized,
|
|
nil,
|
|
)
|
|
}
|
|
|
|
func ErrAccessDeniedMsg() *AppError {
|
|
return NewAppError(
|
|
ErrAccessDenied,
|
|
"Access denied - admin privileges required",
|
|
"Zugriff verweigert. Administrator-Rechte erforderlich",
|
|
http.StatusForbidden,
|
|
nil,
|
|
)
|
|
}
|
|
|
|
func ErrInvalidInputMsg(field string) *AppError {
|
|
return NewAppError(
|
|
ErrInvalidInput,
|
|
fmt.Sprintf("Invalid input for field: %s", field),
|
|
fmt.Sprintf("Ungültige Eingabe im Feld: %s", field),
|
|
http.StatusBadRequest,
|
|
nil,
|
|
)
|
|
}
|
|
|
|
func ErrMissingFieldMsg(field string) *AppError {
|
|
return NewAppError(
|
|
ErrMissingField,
|
|
fmt.Sprintf("Required field missing: %s", field),
|
|
fmt.Sprintf("Pflichtfeld fehlt: %s", field),
|
|
http.StatusBadRequest,
|
|
nil,
|
|
)
|
|
}
|
|
|
|
func ErrNotFoundMsg(resource string) *AppError {
|
|
return NewAppError(
|
|
ErrNotFound,
|
|
fmt.Sprintf("%s not found", resource),
|
|
fmt.Sprintf("%s nicht gefunden", resource),
|
|
http.StatusNotFound,
|
|
nil,
|
|
)
|
|
}
|
|
|
|
func ErrAlreadyExistsMsg(resource string) *AppError {
|
|
return NewAppError(
|
|
ErrAlreadyExists,
|
|
fmt.Sprintf("%s already exists", resource),
|
|
fmt.Sprintf("%s existiert bereits", resource),
|
|
http.StatusConflict,
|
|
nil,
|
|
)
|
|
}
|
|
|
|
func ErrCannotDeleteMsg(resource, reason string) *AppError {
|
|
return NewAppError(
|
|
ErrCannotDelete,
|
|
fmt.Sprintf("Cannot delete %s: %s", resource, reason),
|
|
fmt.Sprintf("%s kann nicht gelöscht werden: %s", resource, reason),
|
|
http.StatusBadRequest,
|
|
nil,
|
|
)
|
|
}
|
|
|
|
func ErrProtectedUserMsg() *AppError {
|
|
return NewAppError(
|
|
ErrProtectedUser,
|
|
"Cannot modify protected admin user",
|
|
"Der Admin-Benutzer ist geschützt und kann nicht geändert werden",
|
|
http.StatusForbidden,
|
|
nil,
|
|
)
|
|
}
|
|
|
|
func ErrNoActiveSchoolYearMsg() *AppError {
|
|
return NewAppError(
|
|
ErrNoActiveSchool,
|
|
"No active school year configured",
|
|
"Kein aktives Schuljahr konfiguriert. Bitte aktivieren Sie ein Schuljahr",
|
|
http.StatusNotFound,
|
|
nil,
|
|
)
|
|
}
|
|
|
|
func ErrDatabaseMsg(internal error) *AppError {
|
|
return NewAppError(
|
|
ErrDatabase,
|
|
"Database operation failed",
|
|
"Ein Datenbankfehler ist aufgetreten. Bitte versuchen Sie es erneut",
|
|
http.StatusInternalServerError,
|
|
internal,
|
|
)
|
|
}
|
|
|
|
func ErrInternalMsg(internal error) *AppError {
|
|
return NewAppError(
|
|
ErrInternal,
|
|
"Internal server error",
|
|
"Ein interner Fehler ist aufgetreten. Bitte versuchen Sie es später erneut",
|
|
http.StatusInternalServerError,
|
|
internal,
|
|
)
|
|
}
|
|
|
|
type ErrorResponse struct {
|
|
Code ErrorCode `json:"code"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
func (e *AppError) ToResponse() ErrorResponse {
|
|
return ErrorResponse{
|
|
Code: e.Code,
|
|
Message: e.UserMsg,
|
|
}
|
|
}
|