-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/admin/authorization admin can authorize users #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1c93b63
7f6a559
e51fc7f
c258810
523b8d1
df24570
03db623
df9f9fc
acdaaaa
a7dc04d
5659cb6
0c4bbf5
5e7b32c
8d8161c
f9f9f37
0ed8eeb
440741f
fde6036
df567cc
05996a8
1903428
4c07155
011eb82
d666b5e
876ad8b
bc9aa1e
c96c0a3
9aa2bdf
5d6ac75
e21cfa2
2510bd4
63e1a3b
c095d34
fe9556a
c2cdda4
5ed223c
8b930e1
fd4491a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package backendlab.team4you.audit; | ||
|
|
||
|
|
||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.context.annotation.EnableAspectJAutoProxy; | ||
|
|
||
| @Configuration | ||
| @EnableAspectJAutoProxy | ||
| public class AspectConfig { | ||
|
|
||
|
|
||
|
|
||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package backendlab.team4you.audit; | ||
|
|
||
| import java.lang.annotation.ElementType; | ||
| import java.lang.annotation.Retention; | ||
| import java.lang.annotation.RetentionPolicy; | ||
| import java.lang.annotation.Target; | ||
|
|
||
| @Target(ElementType.METHOD) | ||
| @Retention(RetentionPolicy.RUNTIME) | ||
| public @interface AuditAction { | ||
| String action(); | ||
| String entity(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| package backendlab.team4you.audit; | ||
|
|
||
| import backendlab.team4you.caserecord.CaseRecordRequestDto; | ||
| import backendlab.team4you.controller.SignupController; | ||
| import org.aspectj.lang.JoinPoint; | ||
| import org.aspectj.lang.annotation.AfterReturning; | ||
| import org.aspectj.lang.annotation.AfterThrowing; | ||
| import org.aspectj.lang.annotation.Aspect; | ||
| import org.aspectj.lang.annotation.Pointcut; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| import org.springframework.security.core.Authentication; | ||
| import org.springframework.security.core.context.SecurityContextHolder; | ||
| import org.springframework.stereotype.Component; | ||
| import org.springframework.web.context.request.RequestContextHolder; | ||
| import org.springframework.web.context.request.ServletRequestAttributes; | ||
|
|
||
| @Aspect | ||
| @Component | ||
| public class AuditAspect { | ||
|
|
||
| private static final Logger log = LoggerFactory.getLogger(AuditAspect.class); | ||
|
|
||
| private final AuditService auditService; | ||
|
|
||
| public AuditAspect(AuditService auditService) { | ||
| this.auditService = auditService; | ||
| } | ||
|
|
||
| @Pointcut("within(backendlab.team4you..*)") | ||
| public void controllerMethods() {} | ||
|
|
||
| @AfterReturning(pointcut = "@annotation(auditAction)", returning = "result") | ||
| public void logAuditSuccess(JoinPoint joinPoint, AuditAction auditAction, Object result) { | ||
| record(joinPoint, auditAction, "SUCCESS"); | ||
| } | ||
|
|
||
| @AfterThrowing(pointcut = "@annotation(auditAction)", throwing = "ex") | ||
| public void logAuditFailure(JoinPoint joinPoint, AuditAction auditAction, Throwable ex) { | ||
| record(joinPoint, auditAction, "FAILURE"); | ||
| } | ||
|
|
||
| private void record(JoinPoint joinPoint, AuditAction auditAction, String status) { | ||
| try { | ||
| Authentication auth = SecurityContextHolder.getContext().getAuthentication(); | ||
| String username = (auth != null) ? auth.getName() : "anonymous"; | ||
|
|
||
| ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); | ||
| String ip = "unknown"; | ||
| String endpoint = "unknown"; | ||
| String httpMethod = "UNKNOWN"; | ||
|
|
||
| if (attrs != null) { | ||
| ip = attrs.getRequest().getRemoteAddr(); | ||
| endpoint = attrs.getRequest().getRequestURI(); | ||
| httpMethod = attrs.getRequest().getMethod(); | ||
|
|
||
| } | ||
|
|
||
|
|
||
|
|
||
|
|
||
| StringBuilder detailsBuilder = new StringBuilder(); | ||
| String methodName = joinPoint.getSignature().toShortString(); | ||
| String details = "Executed method: " + methodName; | ||
| String finalDetails = detailsBuilder.length() > 0 | ||
| ? detailsBuilder.toString() | ||
| : details; | ||
| int entityId = 0; | ||
|
|
||
| Object[] args = joinPoint.getArgs(); | ||
| for (Object arg : args) { | ||
| if (arg instanceof Long id) { | ||
| entityId = id.intValue(); | ||
| detailsBuilder.append("| ID: ").append(id).append(" "); | ||
| } | ||
|
|
||
|
|
||
| else { | ||
| if (arg instanceof CaseRecordRequestDto dto) { | ||
| detailsBuilder.append("Ärende: ").append(dto.title()) | ||
| .append(" (Register: ").append(dto.registryId()).append(")"); | ||
| } | ||
|
|
||
| else if (arg instanceof String str && methodName.toLowerCase().contains("update")) { | ||
|
|
||
| detailsBuilder.append("Tilldelad till: ").append(str).append(" "); | ||
| } | ||
| else if (arg instanceof String str && methodName.toLowerCase().contains("delete")) { | ||
| detailsBuilder.append("Raderad av: ").append(str).append(" "); | ||
| } | ||
| else if (arg instanceof String str && methodName.toLowerCase().contains("create")) { | ||
| detailsBuilder.append("Skapad av: ").append(str).append(" "); | ||
| } | ||
| else if (arg instanceof String str && methodName.toLowerCase().contains("assign")) { | ||
| detailsBuilder.append("Tilldelad till: ").append(str).append(" "); | ||
| } | ||
|
|
||
| else if (arg instanceof SignupController.SignupRequest req) { | ||
| detailsBuilder.append("Passkey registrerad för användare: ") | ||
| .append(req.getUsername()) | ||
| .append(" (Display: ").append(req.getDisplayName()).append(")"); | ||
| } | ||
|
|
||
| else { | ||
| detailsBuilder.append(arg).append(" "); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| } | ||
|
|
||
|
|
||
|
|
||
| auditService.saveLog( | ||
| username, | ||
| finalDetails, | ||
| auditAction.action(), | ||
| endpoint, | ||
| httpMethod, | ||
| ip, | ||
| status, | ||
| auditAction.entity(), | ||
| entityId | ||
|
|
||
| ); | ||
|
Comment on lines
+69
to
+126
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fragile arg-scraping heuristic for
Consider extracting 🔧 Minimal fix to remove the int narrowing- String details = "Executed method: " + methodName;
- int entityId = 0;
+ String details = "Executed method: " + methodName;
+ Long entityId = null;
@@
- if (arg instanceof Long) {
- entityId = ((Long) arg).intValue();
+ if (arg instanceof Long l) {
+ entityId = l;
} else if (arg instanceof String && !((String) arg).contains("/")) {
details = "File/Key: " + arg;
}
@@
- auditAction.entity(),
- entityId
+ auditAction.entity(),
+ entityId == null ? 0 : entityId.intValue() // or refactor saveLog to take LongBetter still: change 🤖 Prompt for AI Agents |
||
|
|
||
| } catch (Exception e) { | ||
| log.warn("Failed to persist audit log for {}: {}", | ||
| joinPoint.getSignature().toShortString(), e.getMessage(), e); | ||
| } | ||
|
|
||
|
|
||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| package backendlab.team4you.audit; | ||
|
|
||
|
|
||
| import jakarta.persistence.*; | ||
|
|
||
| import java.time.ZonedDateTime; | ||
|
|
||
| @Entity | ||
| @Table(name = "audit") | ||
| public class AuditLog { | ||
|
|
||
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| private Long id; | ||
|
|
||
|
|
||
| private String username; | ||
| private String email; | ||
|
|
||
| String details; | ||
|
|
||
| private String action; | ||
|
|
||
| private String httpMethod; | ||
|
|
||
| private String endpoint; | ||
|
|
||
| private String entityType; | ||
|
|
||
| private Long entityId; | ||
|
|
||
| @Column(name = "ip_address") | ||
| private String ipAddress; | ||
|
|
||
|
|
||
| private ZonedDateTime timestamp; | ||
|
|
||
| private String status; | ||
|
|
||
|
|
||
| public AuditLog() { | ||
| } | ||
|
|
||
| public void setId(Long id) { | ||
| this.id = id; | ||
| } | ||
| public Long getId() { | ||
| return id; | ||
| } | ||
| public String getUsername() { | ||
| return username; | ||
| } | ||
| public void setUsername(String username) { | ||
| this.username = username; | ||
| } | ||
| public String getEmail() { | ||
| return email; | ||
| } | ||
| public void setEmail(String email) { | ||
| this.email = email; | ||
| } | ||
| public String getAction() { | ||
| return action; | ||
| } | ||
| public void setAction(String action) { | ||
| this.action = action; | ||
| } | ||
| public String getEndpoint() { | ||
| return endpoint; | ||
| } | ||
| public void setEndpoint(String endpoint) { | ||
| this.endpoint = endpoint; | ||
| } | ||
| public String getEntityType() { | ||
| return entityType; | ||
| } | ||
| public void setEntityType(String entityType) { | ||
| this.entityType = entityType; | ||
| } | ||
| public Long getEntityId() { | ||
| return entityId; | ||
| } | ||
| public void setEntityId(Long entityId) { | ||
| this.entityId = entityId; | ||
| } | ||
|
|
||
| public String getIpAddress() { | ||
| return ipAddress; | ||
| } | ||
| public void setIpAddress(String ipAddress) { | ||
| this.ipAddress = ipAddress; | ||
| } | ||
| public ZonedDateTime getTimestamp() { | ||
| return timestamp; | ||
| } | ||
| public void setTimestamp(ZonedDateTime timestamp) { | ||
| this.timestamp = timestamp; | ||
| } | ||
| public String getStatus() { | ||
| return status; | ||
| } | ||
| public void setStatus(String status) { | ||
| this.status = status; | ||
| } | ||
| public String getDetails() { | ||
| return details; | ||
| } | ||
| public void setDetails(String details) { | ||
| this.details = details; | ||
| } | ||
| public String getHttpMethod() { | ||
| return httpMethod; | ||
| } | ||
| public void setHttpMethod(String httpMethod) { | ||
| this.httpMethod = httpMethod; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package backendlab.team4you.audit; | ||
|
|
||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
| import org.springframework.stereotype.Repository; | ||
|
|
||
| import java.time.ZonedDateTime; | ||
| import java.util.List; | ||
|
|
||
| @Repository | ||
| public interface AuditLogRepository extends JpaRepository<AuditLog, Long> { | ||
|
|
||
| void deleteByTimestampBefore(ZonedDateTime limit); | ||
| List<AuditLog> findAllByOrderByTimestampDesc(); | ||
|
|
||
| } |
Uh oh!
There was an error while loading. Please reload this page.