security: Add proper authentication, RBAC, and tenant isolation

- Add password hashing with bcrypt
- Add AuthService with proper login
- Add password strength validation
- Add RBAC middleware (AdminOnly, ManagerOrAdmin)
- Add tenant isolation middleware
- Update CRM handler with tenant filtering
- Add JWT fallback for development mode
- Add user context helpers
- Build successful
This commit is contained in:
Bernt
2026-08-10 12:52:48 +00:00
parent 8921fd1467
commit 78b57273e2
141 changed files with 29192 additions and 180 deletions
+59
View File
@@ -242,3 +242,62 @@ func getStringClaim(claims jwt.MapClaims, key string) string {
}
return ""
}
// JWTAuthWithFallback middleware för utveckling - stödjer både RS256 och HS256
func JWTAuthWithFallback(jwtSecret string) func(http.Handler) http.Handler {
validator := NewJWTValidator("http://localhost:3208/.well-known/jwks.json")
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
writeError(w, http.StatusUnauthorized, "missing authorization header")
return
}
if !strings.HasPrefix(authHeader, "Bearer ") {
writeError(w, http.StatusUnauthorized, "invalid authorization header format")
return
}
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
// Försök validera med RS256 först
_, claims, err := validator.ValidateToken(tokenString)
if err != nil {
// Fallback: Tillåt HS256 tokens för utveckling
token, parseErr := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok {
return []byte(jwtSecret), nil
}
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
})
if parseErr != nil || !token.Valid {
log.Warn().Err(err).Str("path", r.URL.Path).Msg("JWT validation failed")
writeError(w, http.StatusUnauthorized, "invalid token")
return
}
claims = token.Claims.(jwt.MapClaims)
}
// Extrahera claims
userClaims := Claims{
Sub: getStringClaim(claims, "sub"),
Email: getStringClaim(claims, "email"),
Name: getStringClaim(claims, "name"),
}
// Hantera roles som kan vara []interface{}
if roles, ok := claims["roles"].([]interface{}); ok {
for _, r := range roles {
if s, ok := r.(string); ok {
userClaims.Roles = append(userClaims.Roles, s)
}
}
}
ctx := WithContext(r.Context(), &userClaims)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}