Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,25 @@ build/

### jte ###
/jte-classes/

# Environment variables
.env
.env.local
.env.*.local

# Logs
logs/
*.log

# OS files
.DS_Store
Thumbs.db

# temp
tmp/
temp/

# secrets
secrets/
*.key
*.pem
10 changes: 0 additions & 10 deletions .idea/.gitignore

This file was deleted.

14 changes: 0 additions & 14 deletions .idea/misc.xml

This file was deleted.

9 changes: 0 additions & 9 deletions .idea/project-backend-alfs.iml

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/vcs.xml

This file was deleted.

14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# The ALFS Whistleblower Ticket System

## A secure case management system built with Spring Boot for handling whistleblower reports.
The system allows anonymous reporting, secure file uploads, role-based access control, and full audit logging.

### Logs should look like this:
```text
action = HANDLER_ASSIGNED
fieldName = assignedHandler
oldValue = null
newValue = userId:5
createdAt = 2026-03-27
```

4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/org/example/alfs/Role.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.example.alfs;

public enum Role {
REPORTER,
INVESTIGATOR,
ADMIN
}
36 changes: 36 additions & 0 deletions src/main/java/org/example/alfs/entities/Attachment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.example.alfs.entities;

import jakarta.persistence.*;
import lombok.*;

import java.time.LocalDateTime;

/*
Represent a file uploaded with Ticket.
Stores metadata about the file and the file reference s3key.
*/
@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Attachment {

@Id
@GeneratedValue
private Long id;

private String fileName;

private String s3Key;

private LocalDateTime uploadedAt;

@PrePersist
public void prePersist() {
uploadedAt = LocalDateTime.now();
}

@ManyToOne
private Ticket ticket;
}
49 changes: 49 additions & 0 deletions src/main/java/org/example/alfs/entities/AuditLog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.example.alfs.entities;

import jakarta.persistence.*;
import lombok.*;

import java.time.LocalDateTime;

/*
Represents audit log for Ticket.
Logs all events such as status change, assignment and comments.
Should be written automatically.
*/
@Entity
@Table(name = "audit_log")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class AuditLog {

@Id
@GeneratedValue
private Long id;

private String action;

private String fieldName;

@Column(length = 4000)
private String oldValue;

@Column(length = 4000)
private String newValue;

private LocalDateTime createdAt;

@PrePersist
public void prePersist() {
createdAt = LocalDateTime.now();
}

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ticket_id")
private Ticket ticket;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
}
48 changes: 48 additions & 0 deletions src/main/java/org/example/alfs/entities/Ticket.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.example.alfs.entities;

import jakarta.persistence.*;
import lombok.*;


import java.time.LocalDateTime;
import java.util.List;

/*
Representing a whistleblower report.
The ticket can be followed by the anonymous reporter by using the reporterToken.
*/

@Entity
@Table(name = "ticket")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Ticket {

@Id
@GeneratedValue
private Long id;

private String title;

private String description;

private String status;

@Column(nullable = false, unique = true, length = 128, updatable = false)
private String reporterToken;

private LocalDateTime createdAt;

@PrePersist
public void prePersist() {
createdAt = LocalDateTime.now();
}

@OneToMany(mappedBy = "ticket", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Attachment> attachments;

@ManyToOne
private User assignedHandler;
}
39 changes: 39 additions & 0 deletions src/main/java/org/example/alfs/entities/TicketComment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.example.alfs.entities;

import jakarta.persistence.*;
import lombok.*;


import java.time.LocalDateTime;

/*
Represents comment on a ticket.
The comments are written by a User.
*/
@Entity
@Table(name="ticket_comment")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class TicketComment {

@Id
@GeneratedValue
private Long id;

private String message;

private LocalDateTime createdAt;

@PrePersist
public void prePersist() {
createdAt = LocalDateTime.now();
}

@ManyToOne
private Ticket ticket;

@ManyToOne
private User author;
}
31 changes: 31 additions & 0 deletions src/main/java/org/example/alfs/entities/User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.example.alfs.entities;

import jakarta.persistence.*;
import lombok.*;
import org.example.alfs.Role;

/*
Represents a system user.
Users have different roles such as admin or investigator.
*/
@Entity
@Table(name = "users")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class User {

@GeneratedValue
@Id
private Long id;

private String username;

@Column(name = "password_hash", nullable = false, length = 255)
private String passwordHash;

@Enumerated(EnumType.STRING)
private Role role;

}
1 change: 1 addition & 0 deletions src/main/jte/placeholder.jte
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%-- This is a placeholder jte file for CI test --%>
Loading