refac: removed 7 direct and 12 indirect go modules for custom code instead
This commit is contained in:
1 parent
cfcf108488
commit
c68e9a7858
73 files changed
+2455
-428
No files matched your search
@@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/authtoken"
|
||||
domainAPIKey "RenewCMS/internal/domain/apikey"
|
||||
domainArticle "RenewCMS/internal/domain/article"
|
||||
domainAudit "RenewCMS/internal/domain/audit"
|
||||
@@ -37,14 +38,12 @@ import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/jwtauth/v5"
|
||||
)
|
||||
|
||||
type UseCases struct {
|
||||
apiKeyAuthOnce sync.Once
|
||||
apiKeyAuthMiddleware func(http.Handler) http.Handler
|
||||
Token *jwtauth.JWTAuth
|
||||
Token *authtoken.Auth
|
||||
PurgeScheduler *scheduler.Runner
|
||||
ActivityCleanupScheduler *scheduler.Runner
|
||||
CreateArticleUseCase *useCases.CreateArticleUseCase
|
||||
@@ -347,7 +346,7 @@ func getUseCases(repos *Repositories, imageStorage domainImage.Storage, settings
|
||||
return &UseCases{
|
||||
PurgeScheduler: scheduler.NewSystemRunner(purgeScheduler, log.Default()),
|
||||
ActivityCleanupScheduler: scheduler.NewSystemCleanupRunner(activityCleanup, log.Default()),
|
||||
Token: jwtauth.New("HS256", []byte(settings.JWTSecret), nil),
|
||||
Token: authtoken.New([]byte(settings.JWTSecret)),
|
||||
CreateArticleUseCase: useCases.NewCreateArticleUseCase(repos.ArticleRepo, renderer, recorder, repos.SiteRepo, repos.AuthorRepo),
|
||||
GetArticleUseCase: useCases.NewGetArticleUseCase(repos.ArticleRepo, renderer),
|
||||
ListArticlesUseCase: useCases.NewListArticlesUseCase(repos.ArticleRepo, renderer, repos.SiteRepo),
|
||||
|
||||
@@ -2,6 +2,7 @@ package auth
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/api/pkg/authtoken"
|
||||
validator "RenewCMS/api/pkg/validator"
|
||||
"RenewCMS/internal/domain/invite"
|
||||
"RenewCMS/internal/domain/user"
|
||||
@@ -9,12 +10,10 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/jwtauth/v5"
|
||||
)
|
||||
|
||||
type AcceptInviteHandler struct {
|
||||
Token *jwtauth.JWTAuth
|
||||
Token *authtoken.Auth
|
||||
GetInviteUseCase *useCases.GetInviteUseCase
|
||||
AcceptInviteUseCase *useCases.AcceptInviteUseCase
|
||||
IsProduction bool
|
||||
@@ -30,7 +29,7 @@ type inviteEmailResponse struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
func NewAcceptInviteHandler(getInvite *useCases.GetInviteUseCase, acceptInvite *useCases.AcceptInviteUseCase, token *jwtauth.JWTAuth, isProduction bool) *AcceptInviteHandler {
|
||||
func NewAcceptInviteHandler(getInvite *useCases.GetInviteUseCase, acceptInvite *useCases.AcceptInviteUseCase, token *authtoken.Auth, isProduction bool) *AcceptInviteHandler {
|
||||
return &AcceptInviteHandler{
|
||||
Token: token,
|
||||
GetInviteUseCase: getInvite,
|
||||
|
||||
@@ -2,6 +2,7 @@ package auth
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/api/pkg/authtoken"
|
||||
validator "RenewCMS/api/pkg/validator"
|
||||
"RenewCMS/internal/domain/user"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
@@ -9,12 +10,10 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/jwtauth/v5"
|
||||
)
|
||||
|
||||
type LoginHandler struct {
|
||||
Token *jwtauth.JWTAuth
|
||||
Token *authtoken.Auth
|
||||
CreateUseCase *useCases.CreateUserUseCase
|
||||
AuthenticateUseCase *useCases.AuthenticateUserUseCase
|
||||
IsProduction bool
|
||||
@@ -31,7 +30,7 @@ type LoginCredentials struct {
|
||||
Password string `json:"password" validate:"required,min=8"`
|
||||
}
|
||||
|
||||
func NewLoginHandler(token *jwtauth.JWTAuth, create *useCases.CreateUserUseCase, authenticate *useCases.AuthenticateUserUseCase, isProduction bool) *LoginHandler {
|
||||
func NewLoginHandler(token *authtoken.Auth, create *useCases.CreateUserUseCase, authenticate *useCases.AuthenticateUserUseCase, isProduction bool) *LoginHandler {
|
||||
return &LoginHandler{
|
||||
Token: token,
|
||||
CreateUseCase: create,
|
||||
@@ -52,15 +51,15 @@ func removeJwtCookie(w http.ResponseWriter, isProduction bool) {
|
||||
})
|
||||
}
|
||||
|
||||
func setJwtCookie(w http.ResponseWriter, token *jwtauth.JWTAuth, isProduction bool, u user.User) error {
|
||||
_, tokenString, err := token.Encode(map[string]any{"user_id": u.ID, "token_version": u.TokenVersion})
|
||||
func setJwtCookie(w http.ResponseWriter, token *authtoken.Auth, isProduction bool, u user.User) error {
|
||||
tokenString, err := token.Encode(map[string]any{"user_id": u.ID, "token_version": u.TokenVersion})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "jwt",
|
||||
Value: tokenString,
|
||||
Expires: time.Now().Add(24 * time.Hour),
|
||||
Expires: time.Now().Add(authtoken.SessionLifetime),
|
||||
Secure: isProduction,
|
||||
HttpOnly: true,
|
||||
Path: "/",
|
||||
|
||||
@@ -2,6 +2,7 @@ package middleware
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/api/pkg/authtoken"
|
||||
"RenewCMS/internal/domain/apikey"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
"context"
|
||||
@@ -10,9 +11,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/jwtauth/v5"
|
||||
"github.com/lestrrat-go/jwx/v3/jwt"
|
||||
)
|
||||
|
||||
const bearerScheme = "bearer "
|
||||
@@ -79,13 +77,7 @@ func APIKeyAuth(authenticate *useCases.AuthenticateAPIKeyUseCase, maxRequests in
|
||||
return
|
||||
}
|
||||
|
||||
token, err := sessionTokenFor(authenticated)
|
||||
if err != nil {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := jwtauth.NewContext(r.Context(), token, nil)
|
||||
ctx := authtoken.NewContext(r.Context(), sessionClaimsFor(authenticated), nil)
|
||||
ctx = context.WithValue(ctx, apiKeyIdContextKey{}, authenticated.Key.ID)
|
||||
ctx = context.WithValue(ctx, apiKeyNameContextKey{}, authenticated.Key.Name)
|
||||
limited.ServeHTTP(w, r.WithContext(ctx))
|
||||
@@ -101,15 +93,11 @@ func writeAPIKeyError(w http.ResponseWriter, err error) {
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
func sessionTokenFor(authenticated useCases.AuthenticatedAPIKey) (jwt.Token, error) {
|
||||
token := jwt.New()
|
||||
if err := token.Set("user_id", float64(authenticated.User.ID)); err != nil {
|
||||
return nil, err
|
||||
func sessionClaimsFor(authenticated useCases.AuthenticatedAPIKey) authtoken.Claims {
|
||||
return authtoken.Claims{
|
||||
"user_id": float64(authenticated.User.ID),
|
||||
"token_version": float64(authenticated.User.TokenVersion),
|
||||
}
|
||||
if err := token.Set("token_version", float64(authenticated.User.TokenVersion)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func apiKeyRateLimitKey(r *http.Request) string {
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
corsAllowAnyOrigin = "*"
|
||||
corsWildcardMarker = "*"
|
||||
corsPreflightMaxAge = 300
|
||||
corsPreflightSuccess = http.StatusNoContent
|
||||
)
|
||||
|
||||
var (
|
||||
corsAllowedMethods = []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete, http.MethodOptions}
|
||||
corsAllowedHeaders = []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"}
|
||||
corsExposedHeaders = []string{"Link", "X-Total-Count"}
|
||||
)
|
||||
|
||||
type originWildcard struct {
|
||||
prefix string
|
||||
suffix string
|
||||
}
|
||||
|
||||
func (w originWildcard) matches(origin string) bool {
|
||||
return len(origin) >= len(w.prefix)+len(w.suffix) && strings.HasPrefix(origin, w.prefix) && strings.HasSuffix(origin, w.suffix)
|
||||
}
|
||||
|
||||
type originPolicy struct {
|
||||
anyOrigin bool
|
||||
exact map[string]struct{}
|
||||
wildcards []originWildcard
|
||||
}
|
||||
|
||||
func newOriginPolicy(origins []string) originPolicy {
|
||||
policy := originPolicy{exact: map[string]struct{}{}}
|
||||
for _, origin := range origins {
|
||||
origin = strings.ToLower(strings.TrimSpace(origin))
|
||||
prefix, suffix, hasWildcard := strings.Cut(origin, corsWildcardMarker)
|
||||
switch {
|
||||
case origin == "":
|
||||
case origin == corsAllowAnyOrigin:
|
||||
policy.anyOrigin = true
|
||||
case hasWildcard:
|
||||
policy.wildcards = append(policy.wildcards, originWildcard{prefix: prefix, suffix: suffix})
|
||||
default:
|
||||
policy.exact[origin] = struct{}{}
|
||||
}
|
||||
}
|
||||
return policy
|
||||
}
|
||||
|
||||
func (p originPolicy) allows(origin string) bool {
|
||||
if p.anyOrigin {
|
||||
return true
|
||||
}
|
||||
origin = strings.ToLower(origin)
|
||||
if _, ok := p.exact[origin]; ok {
|
||||
return true
|
||||
}
|
||||
for _, wildcard := range p.wildcards {
|
||||
if wildcard.matches(origin) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isPreflight(r *http.Request) bool {
|
||||
return r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != ""
|
||||
}
|
||||
|
||||
func containsFold(allowed []string, value string) bool {
|
||||
for _, candidate := range allowed {
|
||||
if strings.EqualFold(candidate, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func requestedHeadersAllowed(r *http.Request) bool {
|
||||
requested := r.Header.Get("Access-Control-Request-Headers")
|
||||
if requested == "" {
|
||||
return true
|
||||
}
|
||||
for _, header := range strings.Split(requested, ",") {
|
||||
if header = strings.TrimSpace(header); header != "" && !containsFold(corsAllowedHeaders, header) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func CORS(allowedOrigins []string) func(http.Handler) http.Handler {
|
||||
policy := newOriginPolicy(allowedOrigins)
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
origin := r.Header.Get("Origin")
|
||||
if origin == "" {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Vary", "Origin")
|
||||
if isPreflight(r) {
|
||||
answerPreflight(w, r, origin, policy)
|
||||
return
|
||||
}
|
||||
|
||||
if policy.allows(origin) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
w.Header().Set("Access-Control-Expose-Headers", strings.Join(corsExposedHeaders, ", "))
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func answerPreflight(w http.ResponseWriter, r *http.Request, origin string, policy originPolicy) {
|
||||
w.Header().Add("Vary", "Access-Control-Request-Method")
|
||||
w.Header().Add("Vary", "Access-Control-Request-Headers")
|
||||
|
||||
method := r.Header.Get("Access-Control-Request-Method")
|
||||
if !policy.allows(origin) || !containsFold(corsAllowedMethods, method) || !requestedHeadersAllowed(r) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
w.Header().Set("Access-Control-Allow-Methods", strings.ToUpper(method))
|
||||
w.Header().Set("Access-Control-Allow-Headers", strings.Join(corsAllowedHeaders, ", "))
|
||||
w.Header().Set("Access-Control-Max-Age", strconv.Itoa(corsPreflightMaxAge))
|
||||
w.WriteHeader(corsPreflightSuccess)
|
||||
}
|
||||
@@ -2,16 +2,13 @@ package middleware
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/api/pkg/authtoken"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/jwtauth/v5"
|
||||
"github.com/lestrrat-go/jwx/v3/jwt"
|
||||
)
|
||||
|
||||
func RequireSignedIn(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
token, _, err := jwtauth.FromContext(r.Context())
|
||||
if err != nil || token == nil || jwt.Validate(token) != nil {
|
||||
if _, err := authtoken.FromContext(r.Context()); err != nil {
|
||||
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
package authtoken
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"maps"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
SessionLifetime = 24 * time.Hour
|
||||
|
||||
algorithmHS256 = "HS256"
|
||||
tokenType = "JWT"
|
||||
maxTokenBytes = 4096
|
||||
segmentCount = 3
|
||||
segmentSep = "."
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoToken = errors.New("no token found")
|
||||
ErrInvalid = errors.New("token is invalid")
|
||||
ErrExpired = errors.New("token is expired")
|
||||
ErrNotYetValid = errors.New("token is not yet valid")
|
||||
)
|
||||
|
||||
type Claims map[string]any
|
||||
|
||||
type header struct {
|
||||
Algorithm string `json:"alg"`
|
||||
Type string `json:"typ,omitempty"`
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
secret []byte
|
||||
clock func() time.Time
|
||||
}
|
||||
|
||||
type Option func(*Auth)
|
||||
|
||||
func WithClock(clock func() time.Time) Option {
|
||||
return func(a *Auth) { a.clock = clock }
|
||||
}
|
||||
|
||||
func New(secret []byte, options ...Option) *Auth {
|
||||
auth := &Auth{secret: secret, clock: time.Now}
|
||||
for _, option := range options {
|
||||
option(auth)
|
||||
}
|
||||
return auth
|
||||
}
|
||||
|
||||
func (a *Auth) Encode(claims map[string]any) (string, error) {
|
||||
payload := maps.Clone(claims)
|
||||
if payload == nil {
|
||||
payload = map[string]any{}
|
||||
}
|
||||
now := a.clock()
|
||||
if _, ok := payload["iat"]; !ok {
|
||||
payload["iat"] = now.Unix()
|
||||
}
|
||||
if _, ok := payload["exp"]; !ok {
|
||||
payload["exp"] = now.Add(SessionLifetime).Unix()
|
||||
}
|
||||
|
||||
headerSegment, err := encodeSegment(header{Algorithm: algorithmHS256, Type: tokenType})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
payloadSegment, err := encodeSegment(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
signingInput := headerSegment + segmentSep + payloadSegment
|
||||
return signingInput + segmentSep + a.sign(signingInput), nil
|
||||
}
|
||||
|
||||
func (a *Auth) Decode(token string) (Claims, error) {
|
||||
if token == "" {
|
||||
return nil, ErrNoToken
|
||||
}
|
||||
if len(token) > maxTokenBytes {
|
||||
return nil, ErrInvalid
|
||||
}
|
||||
|
||||
segments := strings.Split(token, segmentSep)
|
||||
if len(segments) != segmentCount {
|
||||
return nil, ErrInvalid
|
||||
}
|
||||
|
||||
var tokenHeader header
|
||||
if err := decodeSegment(segments[0], &tokenHeader); err != nil || tokenHeader.Algorithm != algorithmHS256 {
|
||||
return nil, ErrInvalid
|
||||
}
|
||||
|
||||
signingInput := segments[0] + segmentSep + segments[1]
|
||||
if !hmac.Equal([]byte(a.sign(signingInput)), []byte(segments[2])) {
|
||||
return nil, ErrInvalid
|
||||
}
|
||||
|
||||
var claims Claims
|
||||
if err := decodeSegment(segments[1], &claims); err != nil || claims == nil {
|
||||
return nil, ErrInvalid
|
||||
}
|
||||
|
||||
if err := a.validateTimes(claims); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
func (a *Auth) validateTimes(claims Claims) error {
|
||||
now := a.clock()
|
||||
|
||||
expiry, present, err := numericDate(claims, "exp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if present && !now.Before(expiry) {
|
||||
return ErrExpired
|
||||
}
|
||||
|
||||
notBefore, present, err := numericDate(claims, "nbf")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if present && now.Before(notBefore) {
|
||||
return ErrNotYetValid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func numericDate(claims Claims, name string) (time.Time, bool, error) {
|
||||
raw, present := claims[name]
|
||||
if !present {
|
||||
return time.Time{}, false, nil
|
||||
}
|
||||
seconds, ok := raw.(float64)
|
||||
if !ok {
|
||||
return time.Time{}, false, ErrInvalid
|
||||
}
|
||||
return time.Unix(int64(seconds), 0), true, nil
|
||||
}
|
||||
|
||||
func (a *Auth) sign(signingInput string) string {
|
||||
mac := hmac.New(sha256.New, a.secret)
|
||||
mac.Write([]byte(signingInput))
|
||||
return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
|
||||
}
|
||||
|
||||
func encodeSegment(value any) (string, error) {
|
||||
encoded, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(encoded), nil
|
||||
}
|
||||
|
||||
func decodeSegment(segment string, into any) error {
|
||||
decoded, err := base64.RawURLEncoding.DecodeString(segment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(decoded, into)
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package authtoken
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
cookieName = "jwt"
|
||||
bearerPrefix = "bearer "
|
||||
)
|
||||
|
||||
type contextKey struct{}
|
||||
|
||||
type verdict struct {
|
||||
claims Claims
|
||||
err error
|
||||
}
|
||||
|
||||
func Verifier(auth *Auth) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
claims, err := auth.Decode(tokenFromRequest(r))
|
||||
next.ServeHTTP(w, r.WithContext(NewContext(r.Context(), claims, err)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Authenticator(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := FromContext(r.Context()); err != nil {
|
||||
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func NewContext(ctx context.Context, claims Claims, err error) context.Context {
|
||||
return context.WithValue(ctx, contextKey{}, verdict{claims: claims, err: err})
|
||||
}
|
||||
|
||||
func FromContext(ctx context.Context) (Claims, error) {
|
||||
stored, ok := ctx.Value(contextKey{}).(verdict)
|
||||
switch {
|
||||
case !ok:
|
||||
return nil, ErrNoToken
|
||||
case stored.err != nil:
|
||||
return nil, stored.err
|
||||
case stored.claims == nil:
|
||||
return nil, ErrNoToken
|
||||
}
|
||||
return stored.claims, nil
|
||||
}
|
||||
|
||||
func tokenFromRequest(r *http.Request) string {
|
||||
if token := tokenFromHeader(r); token != "" {
|
||||
return token
|
||||
}
|
||||
return tokenFromCookie(r)
|
||||
}
|
||||
|
||||
func tokenFromHeader(r *http.Request) string {
|
||||
header := r.Header.Get("Authorization")
|
||||
if len(header) <= len(bearerPrefix) || !strings.EqualFold(header[:len(bearerPrefix)], bearerPrefix) {
|
||||
return ""
|
||||
}
|
||||
return header[len(bearerPrefix):]
|
||||
}
|
||||
|
||||
func tokenFromCookie(r *http.Request) string {
|
||||
cookie, err := r.Cookie(cookieName)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return cookie.Value
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
package claims
|
||||
|
||||
import (
|
||||
"RenewCMS/api/pkg/authtoken"
|
||||
"math"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/jwtauth/v5"
|
||||
)
|
||||
|
||||
func UserId(r *http.Request) (uint32, bool) {
|
||||
_, claims, err := jwtauth.FromContext(r.Context())
|
||||
claims, err := authtoken.FromContext(r.Context())
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
@@ -31,7 +30,7 @@ func UserId(r *http.Request) (uint32, bool) {
|
||||
}
|
||||
|
||||
func TokenVersion(r *http.Request) (uint32, bool) {
|
||||
_, claims, err := jwtauth.FromContext(r.Context())
|
||||
claims, err := authtoken.FromContext(r.Context())
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
+42
-47
@@ -18,21 +18,23 @@ import (
|
||||
"RenewCMS/api/handlers/update"
|
||||
"RenewCMS/api/handlers/user"
|
||||
"RenewCMS/api/middleware"
|
||||
"RenewCMS/api/pkg/authtoken"
|
||||
"RenewCMS/internal/infrastructure/config"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/MadAppGang/httplog"
|
||||
"github.com/go-chi/chi/v5"
|
||||
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/cors"
|
||||
"github.com/go-chi/jwtauth/v5"
|
||||
)
|
||||
|
||||
func enableLoggingIfDev(r *chi.Mux, routerName string, environment string) {
|
||||
if environment == "development" {
|
||||
r.Use(httplog.LoggerWithName(routerName))
|
||||
r.Use(chimiddleware.RequestLogger(&chimiddleware.DefaultLogFormatter{
|
||||
Logger: log.New(os.Stdout, "["+routerName+"] ", log.LstdFlags),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +70,7 @@ func NewArticleRouter(container *UseCases, trustedProxies []string) http.Handler
|
||||
r.Get("/{id}/seo", seoHandler.Resolve)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Post("/", articleHandler.PostArticle)
|
||||
@@ -83,7 +85,7 @@ func NewArticleRouter(container *UseCases, trustedProxies []string) http.Handler
|
||||
|
||||
func NewAuthRouter(container *UseCases, isProduction bool, rateLimit config.AuthRateLimitSettings, trustedProxies []string) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
||||
|
||||
loginHandler := auth.NewLoginHandler(container.Token, container.CreateUserUseCase, container.AuthenticateUserUseCase, isProduction)
|
||||
@@ -114,16 +116,16 @@ func NewAuthRouter(container *UseCases, isProduction bool, rateLimit config.Auth
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireSession)
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Post("/verify", verificationHandler.Verify)
|
||||
r.Delete("/register", verificationHandler.CancelRegistration)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(container.apiKeyAuth())
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Get("/me", meHandler.Me)
|
||||
})
|
||||
@@ -137,10 +139,10 @@ func NewUserRouter(container *UseCases, rateLimit config.AuthRateLimitSettings,
|
||||
lookupHandler := user.NewLookupHandler(container.LookupUserNamesUseCase)
|
||||
inviteHandler := user.NewInviteHandler(container.CreateInviteUseCase, container.CancelInviteUseCase, container.RenewInviteUseCase, container.ListUsersAndInvitesUseCase)
|
||||
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(container.apiKeyAuth())
|
||||
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Patch("/me", userHandler.UpdateMe)
|
||||
@@ -163,19 +165,19 @@ func NewSettingsRouter(container *UseCases, trustedProxies []string) http.Handle
|
||||
settingsHandler := settings.NewHandler(container.GetSettingsUseCase, container.GetActivityRetentionUseCase, container.UpdateActivityRetentionUseCase)
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(container.apiKeyAuth())
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Get("/", settingsHandler.GetSettings)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireSession)
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
||||
r.Use(middleware.RequireSignedIn)
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireOwner(container.GetUserUseCase))
|
||||
@@ -193,19 +195,19 @@ func NewSiteRouter(container *UseCases, trustedProxies []string) http.Handler {
|
||||
|
||||
r.Get("/{key}/sitemap", sitemapHandler.Sitemap)
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(container.apiKeyAuth())
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Get("/", siteHandler.List)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireSession)
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
||||
r.Use(middleware.RequireSignedIn)
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireOwner(container.GetUserUseCase))
|
||||
@@ -223,19 +225,19 @@ func NewAuthorRouter(container *UseCases, trustedProxies []string) http.Handler
|
||||
authorHandler := author.NewHandler(container.ListAuthorsUseCase, container.CreateAuthorUseCase, container.CreateMyAuthorUseCase, container.UpdateAuthorUseCase, container.DeleteAuthorUseCase, container.GetUserUseCase)
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(container.apiKeyAuth())
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Get("/", authorHandler.List)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireSession)
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
||||
r.Use(middleware.RequireSignedIn)
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Post("/me", authorHandler.CreateMine)
|
||||
@@ -256,11 +258,11 @@ func NewContentTypeRouter(container *UseCases, trustedProxies []string) http.Han
|
||||
|
||||
entryHandler := entry.NewHandler(container.CreateEntryUseCase, container.GetEntryUseCase, container.ListEntriesUseCase, container.UpdateEntryUseCase, container.DeleteEntryUseCase)
|
||||
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(container.apiKeyAuth())
|
||||
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
||||
r.Use(middleware.RequireSignedIn)
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Get("/", contentTypeHandler.List)
|
||||
@@ -284,11 +286,11 @@ func NewBuiltinTypeRouter(container *UseCases, trustedProxies []string) http.Han
|
||||
|
||||
builtinTypeHandler := builtintype.NewHandler(container.ListBuiltinTypesUseCase, container.GetBuiltinTypeUseCase)
|
||||
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(container.apiKeyAuth())
|
||||
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
||||
r.Use(middleware.RequireSignedIn)
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireOwnerOrAdmin(container.GetUserUseCase))
|
||||
@@ -303,10 +305,10 @@ func NewActivityRouter(container *UseCases, trustedProxies []string) http.Handle
|
||||
activityHandler := activity.NewHandler(container.ListActivityUseCase, container.GetActivitySummaryUseCase, container.ExportActivityUseCase)
|
||||
|
||||
r.Use(middleware.RequireSession)
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
||||
r.Use(middleware.RequireSignedIn)
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Get("/", activityHandler.List)
|
||||
@@ -331,9 +333,9 @@ func NewSystemRouter(container *UseCases, trustedProxies []string) http.Handler
|
||||
updateHandler := update.NewHandler(container.GetUpdateStatusUseCase, container.CheckForUpdateUseCase, container.StartUpdateDownloadUseCase, container.RequestRestartUseCase, container.GetUserUseCase)
|
||||
|
||||
r.Use(middleware.RequireSession)
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Get("/update", updateHandler.GetStatus)
|
||||
@@ -360,9 +362,9 @@ func NewAPIKeyRouter(container *UseCases, trustedProxies []string) http.Handler
|
||||
apiKeyHandler := apikey.NewHandler(container.CreateAPIKeyUseCase, container.ListAPIKeysUseCase, container.RevokeAPIKeyUseCase)
|
||||
|
||||
r.Use(middleware.RequireSession)
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Post("/", apiKeyHandler.Create)
|
||||
@@ -376,11 +378,11 @@ func NewMediaRouter(container *UseCases, trustedProxies []string) http.Handler {
|
||||
|
||||
mediaHandler := media.NewHandler(container.CreateImageUseCase, container.ListMediaUseCase, container.RenameMediaUseCase, container.DeleteMediaUseCase, container.MergeMediaUseCase, container.GetMediaUsageUseCase, container.GetPurgeSettingsUseCase, container.UpdatePurgeSettingsUseCase, container.PreviewPurgeUseCase, container.RunPurgeUseCase, container.ListImageVariantsUseCase, container.GenerateImageVariantUseCase, container.GenerateAllImageVariantsUseCase, container.PurgeImageVariantUseCase, container.GenerateVariantsOnUploadUseCase)
|
||||
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(container.apiKeyAuth())
|
||||
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
||||
r.Use(middleware.RequireSignedIn)
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Group(func(r chi.Router) {
|
||||
@@ -413,10 +415,10 @@ func NewImageVariantRouter(container *UseCases, trustedProxies []string) http.Ha
|
||||
|
||||
variantHandler := imagevariant.NewHandler(container.ListVariantsUseCase, container.CreateVariantUseCase, container.UpdateVariantUseCase, container.SetVariantEnabledUseCase, container.DeleteVariantUseCase, container.PurgeVariantUseCase, container.GetVariantCacheStatsUseCase, container.PurgeVariantCacheUseCase)
|
||||
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
||||
r.Use(middleware.RequireSignedIn)
|
||||
r.Use(jwtauth.Authenticator(container.Token))
|
||||
r.Use(authtoken.Authenticator)
|
||||
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
||||
r.Use(middleware.RequireOwnerOrAdmin(container.GetUserUseCase))
|
||||
@@ -440,7 +442,7 @@ func InitBackendRoutes(container *UseCases, settings config.Settings) *chi.Mux {
|
||||
r.Use(middleware.JsonContentTypeMiddleware)
|
||||
r.Get("/", GetHelloWorld)
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(jwtauth.Verifier(container.Token))
|
||||
r.Use(authtoken.Verifier(container.Token))
|
||||
r.Mount("/article", NewArticleRouter(container, settings.TrustedProxies))
|
||||
})
|
||||
r.Mount("/user", NewUserRouter(container, settings.AuthRateLimit, settings.TrustedProxies))
|
||||
@@ -478,14 +480,7 @@ func enableCorsIfConfigured(r *chi.Mux, origins []string) {
|
||||
if len(origins) == 0 {
|
||||
return
|
||||
}
|
||||
r.Use(cors.Handler(cors.Options{
|
||||
AllowedOrigins: origins,
|
||||
AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
|
||||
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
|
||||
ExposedHeaders: []string{"Link", "X-Total-Count"},
|
||||
AllowCredentials: false,
|
||||
MaxAge: 300,
|
||||
}))
|
||||
r.Use(middleware.CORS(origins))
|
||||
}
|
||||
|
||||
func InitRoutes(container *UseCases, settings config.Settings) *chi.Mux {
|
||||
|
||||
Reference in new issue
Block a user