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
+30 -22
View File
@@ -122,9 +122,20 @@ func main() {
jwtService := auth.NewJWTService(cfg.JWTSecret, "boc-auth", "boc")
_ = jwtService
// För utveckling: använd öppen auth
authMiddleware := middleware.APIKeyAuth("")
logger.Info().Msg("Development auth initialized (open access)")
// Auth service med databas
authService := auth.NewAuthService(database, cfg.JWTSecret, "boc-auth", "boc")
// Auth middleware - RS256 för produktion, HS256 för utveckling
var authMiddleware func(http.Handler) http.Handler
if cfg.Port == "9092" {
// Utveckling: tillåt HS256
authMiddleware = middleware.JWTAuthWithFallback(cfg.JWTSecret)
logger.Info().Msg("Development auth initialized (HS256 + RS256)")
} else {
// Produktion: endast RS256
authMiddleware = middleware.JWTAuth("http://localhost:3208/.well-known/jwks.json")
logger.Info().Msg("Production auth initialized (RS256 only)")
}
// Prometheus metrics
requestDuration := prometheus.NewHistogramVec(prometheus.HistogramOpts{
@@ -176,42 +187,38 @@ func main() {
// Auth endpoints (no auth required)
r.Post("/api/v1/auth/login", func(w http.ResponseWriter, r *http.Request) {
var req struct {
Email string `json:"email"`
Password string `json:"password"`
}
var req auth.LoginRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest)
return
}
// TODO: Implement proper password verification against database
// For now, reject all login attempts in production
if cfg.Port != "9092" {
http.Error(w, `{"error":"authentication service unavailable"}`, http.StatusServiceUnavailable)
// Validera input
if req.Email == "" || req.Password == "" {
http.Error(w, `{"error":"email and password required"}`, http.StatusBadRequest)
return
}
// Development only - generate token without password check
token, err := jwtService.GenerateToken("3847477b-3d56-4975-9157-ae8f9ce52aa7", req.Email, "admin")
// Försök logga in
resp, err := authService.Login(r.Context(), req)
if err != nil {
http.Error(w, `{"error":"token generation failed"}`, http.StatusInternalServerError)
// Generiskt felmeddelande för att inte avslöja om email finns
http.Error(w, `{"error":"invalid email or password"}`, http.StatusUnauthorized)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"ok": true,
"token": token,
"token_type": "Bearer",
"expires_in": 3600, // 1 hour - reduced from 30 days
"algorithm": "HS256",
"token": resp.Token,
"token_type": resp.TokenType,
"expires_in": resp.ExpiresIn,
"user": map[string]string{
"id": "3847477b-3d56-4975-9157-ae8f9ce52aa7",
"email": req.Email,
"name": "Erik Svensson",
"role": "admin",
"id": resp.User.ID,
"email": resp.User.Email,
"name": resp.User.Name,
"role": resp.User.Role,
},
})
})
@@ -219,6 +226,7 @@ func main() {
// Protected routes
r.Group(func(r chi.Router) {
r.Use(authMiddleware)
r.Use(middleware.TenantIsolation)
// Auth me
r.Get("/api/v1/auth/me", func(w http.ResponseWriter, r *http.Request) {