From aca2240a50a12e849cfb6e1aa56390b000aebae0 Mon Sep 17 00:00:00 2001 From: Elias Schneider Date: Mon, 11 Nov 2024 18:25:57 +0100 Subject: [PATCH] feat: add audit log event for one time access token sign in --- backend/internal/bootstrap/router_bootstrap.go | 2 +- backend/internal/controller/user_controller.go | 2 +- backend/internal/model/audit_log.go | 7 ++++--- backend/internal/service/audit_log_service.go | 4 ++-- backend/internal/service/user_service.go | 13 ++++++++----- backend/internal/service/webauthn_service.go | 2 +- frontend/src/lib/components/ui/badge/index.ts | 2 +- 7 files changed, 18 insertions(+), 14 deletions(-) diff --git a/backend/internal/bootstrap/router_bootstrap.go b/backend/internal/bootstrap/router_bootstrap.go index dd938bd..a70e945 100644 --- a/backend/internal/bootstrap/router_bootstrap.go +++ b/backend/internal/bootstrap/router_bootstrap.go @@ -38,7 +38,7 @@ func initRouter(db *gorm.DB, appConfigService *service.AppConfigService) { auditLogService := service.NewAuditLogService(db, appConfigService, emailService) jwtService := service.NewJwtService(appConfigService) webauthnService := service.NewWebAuthnService(db, jwtService, auditLogService, appConfigService) - userService := service.NewUserService(db, jwtService) + userService := service.NewUserService(db, jwtService, auditLogService) customClaimService := service.NewCustomClaimService(db) oidcService := service.NewOidcService(db, jwtService, appConfigService, auditLogService, customClaimService) testService := service.NewTestService(db, appConfigService) diff --git a/backend/internal/controller/user_controller.go b/backend/internal/controller/user_controller.go index 2990e1f..54df2ae 100644 --- a/backend/internal/controller/user_controller.go +++ b/backend/internal/controller/user_controller.go @@ -141,7 +141,7 @@ func (uc *UserController) createOneTimeAccessTokenHandler(c *gin.Context) { return } - token, err := uc.UserService.CreateOneTimeAccessToken(input.UserID, input.ExpiresAt) + token, err := uc.UserService.CreateOneTimeAccessToken(input.UserID, input.ExpiresAt, c.ClientIP(), c.Request.UserAgent()) if err != nil { c.Error(err) return diff --git a/backend/internal/model/audit_log.go b/backend/internal/model/audit_log.go index 736b006..b8cadab 100644 --- a/backend/internal/model/audit_log.go +++ b/backend/internal/model/audit_log.go @@ -23,9 +23,10 @@ type AuditLogData map[string]string type AuditLogEvent string const ( - AuditLogEventSignIn AuditLogEvent = "SIGN_IN" - AuditLogEventClientAuthorization AuditLogEvent = "CLIENT_AUTHORIZATION" - AuditLogEventNewClientAuthorization AuditLogEvent = "NEW_CLIENT_AUTHORIZATION" + AuditLogEventSignIn AuditLogEvent = "SIGN_IN" + AuditLogEventOneTimeAccessTokenSignIn AuditLogEvent = "TOKEN_SIGN_IN" + AuditLogEventClientAuthorization AuditLogEvent = "CLIENT_AUTHORIZATION" + AuditLogEventNewClientAuthorization AuditLogEvent = "NEW_CLIENT_AUTHORIZATION" ) // Scan and Value methods for GORM to handle the custom type diff --git a/backend/internal/service/audit_log_service.go b/backend/internal/service/audit_log_service.go index f01193b..09973a2 100644 --- a/backend/internal/service/audit_log_service.go +++ b/backend/internal/service/audit_log_service.go @@ -48,8 +48,8 @@ func (s *AuditLogService) Create(event model.AuditLogEvent, ipAddress, userAgent } // CreateNewSignInWithEmail creates a new audit log entry in the database and sends an email if the device hasn't been used before -func (s *AuditLogService) CreateNewSignInWithEmail(ipAddress, userAgent, userID string, data model.AuditLogData) model.AuditLog { - createdAuditLog := s.Create(model.AuditLogEventSignIn, ipAddress, userAgent, userID, data) +func (s *AuditLogService) CreateNewSignInWithEmail(ipAddress, userAgent, userID string) model.AuditLog { + createdAuditLog := s.Create(model.AuditLogEventSignIn, ipAddress, userAgent, userID, model.AuditLogData{}) // Count the number of times the user has logged in from the same device var count int64 diff --git a/backend/internal/service/user_service.go b/backend/internal/service/user_service.go index e00f5fe..1ef0e0a 100644 --- a/backend/internal/service/user_service.go +++ b/backend/internal/service/user_service.go @@ -12,12 +12,13 @@ import ( ) type UserService struct { - db *gorm.DB - jwtService *JwtService + db *gorm.DB + jwtService *JwtService + auditLogService *AuditLogService } -func NewUserService(db *gorm.DB, jwtService *JwtService) *UserService { - return &UserService{db: db, jwtService: jwtService} +func NewUserService(db *gorm.DB, jwtService *JwtService, auditLogService *AuditLogService) *UserService { + return &UserService{db: db, jwtService: jwtService, auditLogService: auditLogService} } func (s *UserService) ListUsers(searchTerm string, page int, pageSize int) ([]model.User, utils.PaginationResponse, error) { @@ -88,7 +89,7 @@ func (s *UserService) UpdateUser(userID string, updatedUser dto.UserCreateDto, u return user, nil } -func (s *UserService) CreateOneTimeAccessToken(userID string, expiresAt time.Time) (string, error) { +func (s *UserService) CreateOneTimeAccessToken(userID string, expiresAt time.Time, ipAddress, userAgent string) (string, error) { randomString, err := utils.GenerateRandomAlphanumericString(16) if err != nil { return "", err @@ -104,6 +105,8 @@ func (s *UserService) CreateOneTimeAccessToken(userID string, expiresAt time.Tim return "", err } + s.auditLogService.Create(model.AuditLogEventOneTimeAccessTokenSignIn, ipAddress, userAgent, userID, model.AuditLogData{}) + return oneTimeAccessToken.Token, nil } diff --git a/backend/internal/service/webauthn_service.go b/backend/internal/service/webauthn_service.go index ada633f..6376b1e 100644 --- a/backend/internal/service/webauthn_service.go +++ b/backend/internal/service/webauthn_service.go @@ -165,7 +165,7 @@ func (s *WebAuthnService) VerifyLogin(sessionID, userID string, credentialAssert return model.User{}, "", err } - s.auditLogService.CreateNewSignInWithEmail(ipAddress, userAgent, user.ID, model.AuditLogData{}) + s.auditLogService.CreateNewSignInWithEmail(ipAddress, userAgent, user.ID) return *user, token, nil } diff --git a/frontend/src/lib/components/ui/badge/index.ts b/frontend/src/lib/components/ui/badge/index.ts index e907993..10968a1 100644 --- a/frontend/src/lib/components/ui/badge/index.ts +++ b/frontend/src/lib/components/ui/badge/index.ts @@ -2,7 +2,7 @@ import { type VariantProps, tv } from "tailwind-variants"; export { default as Badge } from "./badge.svelte"; export const badgeVariants = tv({ - base: "inline-flex select-none items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + base: "inline-flex select-none items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 break-keep whitespace-nowrap", variants: { variant: { default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",