Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
17514a3
Add initial README for P2P-shopping project
driedpampas Mar 20, 2026
ad79263
Add contribution guidelines to CONTRIBUTING.md
driedpampas Mar 20, 2026
2c80a8c
Add contribution guidelines reference to README
driedpampas Mar 20, 2026
cbcda2b
Add contribution guidelines and project setup files
driedpampas Mar 24, 2026
7167c8b
Add SonarQube configuration for code analysis
driedpampas Mar 24, 2026
11e2453
Add Spring Data JPA and MongoDB dependencies to build configuration
driedpampas Mar 24, 2026
df5872a
config(db): add PostgreSQL and MongoDB database configuration
driedpampas Mar 25, 2026
0b6a077
Add Spring Web, OpenAPI setup and mock RoutingController
bmbianca Mar 28, 2026
b02fdbf
fix: refactor DTOs, add unit test, and disable DB autoconfig
bmbianca Mar 28, 2026
3c56aee
(telemetry): add TelemetryPingDTO, TelemetryRecord and TelemetryRepos…
oliviaa28 Mar 29, 2026
b9c9081
(telemetry): add TelemetryPingDTO, TelemetryRecord and TelemetryRepos…
oliviaa28 Mar 29, 2026
3d9ef59
(telemetry): move MongoDB URI to environment variable
oliviaa28 Mar 29, 2026
99fc210
Revert "(telemetry): move MongoDB URI to environment variable"
oliviaa28 Mar 29, 2026
3d3ad0f
Update application configuration and add JaCoCo for coverage (#115)
driedpampas Mar 29, 2026
9c1454d
Merge branch 'main' into feature/core-api-openapi-setup
driedpampas Mar 29, 2026
d3ac83b
chore: update springdoc-openapi dependency version in build configura…
driedpampas Mar 29, 2026
f4ee687
Merge branch 'main' into olivia/telemetry-db-setup
driedpampas Mar 29, 2026
a70010b
Merge pull request #114 from P2P-Shopping/olivia/telemetry-db-setup
oliviaa28 Mar 29, 2026
2b7ba0c
Setup complete: Spring Boot, PostgreSQL, GlobalExceptionHandler
irinnaa Mar 29, 2026
9af5b45
Merge branch 'main' into feature/springboot-postgresql-setup
irinnaa Mar 29, 2026
83fa7a1
Refactor exception handler for security
irinnaa Mar 29, 2026
7cd062b
Added unit test for GlobalExceptionHandler to increase coverage
irinnaa Mar 29, 2026
675404a
Merge branch 'main' into feature/core-api-openapi-setup
bmbianca Mar 30, 2026
c638e74
Merge pull request #116 from P2P-Shopping/feature/springboot-postgres…
irinnaa Mar 30, 2026
7877dae
Merge pull request #112 from P2P-Shopping/feature/core-api-openapi-setup
bmbianca Mar 30, 2026
083495d
Rezolvat conflicte si adus scripturile SQL de pe main
iuliaaa20 Mar 31, 2026
0f4e2b2
final
iuliaaa20 Mar 31, 2026
f1ec012
Merge branch 'main' into feature/auth-backend-setup
iuliaaa20 Mar 31, 2026
9276f6f
sters chestii inutile(sper)
iuliaaa20 Mar 31, 2026
1acbc0e
Update init-scripts/users.sql
iuliaaa20 Mar 31, 2026
869041e
plangea iepurele
iuliaaa20 Mar 31, 2026
86087e3
curatenie
iuliaaa20 Mar 31, 2026
6a6a8f7
added tests
iuliaaa20 Mar 31, 2026
8c308c4
added login
iuliaaa20 Mar 31, 2026
b2d34f3
added login
iuliaaa20 Mar 31, 2026
fc6c373
added login
iuliaaa20 Mar 31, 2026
c9a20f7
test
iuliaaa20 Mar 31, 2026
ada55dd
test
iuliaaa20 Apr 1, 2026
3eea4b2
test
iuliaaa20 Apr 1, 2026
c797bfa
final hopefully
iuliaaa20 Apr 1, 2026
3a522e6
final hopefully
iuliaaa20 Apr 1, 2026
0be7f5a
fix: make qube happy by switching to authorization header
driedpampas Apr 1, 2026
35ff5b4
fix: disable csrf cause i forgor
driedpampas Apr 1, 2026
bfa6b56
chore: readd init scripts
driedpampas Apr 1, 2026
cc7fdc1
chore: sql scripts part 2
driedpampas Apr 1, 2026
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
3 changes: 2 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
POSTGRES_DB=p2p_shopping
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_PASSWORD=postgres
JWT_SECRET=your-secret-key-here-at-least-32-characters-long
14 changes: 13 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
}

val springdocVersion = "3.0.2"

val jjwtVersion = "0.13.0"
dependencies {

Check warning on line 25 in build.gradle.kts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Group dependencies by their destination.

See more on https://sonarcloud.io/project/issues?id=P2P-Shopping_P2P-Shopping&issues=AZ1FynS0PEzx69oR_XYL&open=AZ1FynS0PEzx69oR_XYL&pullRequest=133
annotationProcessor("org.projectlombok:lombok")

implementation("org.springframework.boot:spring-boot-starter")
Expand All @@ -32,11 +32,23 @@
implementation("org.projectlombok:lombok")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:$springdocVersion")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("io.jsonwebtoken:jjwt-api:${jjwtVersion}")

testImplementation("org.springframework.boot:spring-boot-starter-webmvc-test")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")

testImplementation("com.h2database:h2")

runtimeOnly("io.jsonwebtoken:jjwt-impl:${jjwtVersion}")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:${jjwtVersion}")
runtimeOnly("org.postgresql:postgresql")

testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.junit.platform:junit-platform-suite-api")
testImplementation("org.testcontainers:testcontainers:1.19.0")

Check warning on line 50 in build.gradle.kts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not hardcode version numbers.

See more on https://sonarcloud.io/project/issues?id=P2P-Shopping_P2P-Shopping&issues=AZ1GXqhoPU4l3pg_Xxgs&open=AZ1GXqhoPU4l3pg_Xxgs&pullRequest=133
testImplementation("org.testcontainers:postgresql:1.19.0")

Check warning on line 51 in build.gradle.kts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Do not hardcode version numbers.

See more on https://sonarcloud.io/project/issues?id=P2P-Shopping_P2P-Shopping&issues=AZ1GXqhoPU4l3pg_Xxgt&open=AZ1GXqhoPU4l3pg_Xxgt&pullRequest=133
testRuntimeOnly("org.junit.platform:junit-platform-suite-engine")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
Expand Down
4 changes: 1 addition & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '3.8'

services:
db:
image: postgis/postgis:16-3.4
Expand All @@ -10,7 +8,7 @@ services:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
ports:
- "5432:5432"
- "5433:5432"
volumes:
- pgdata:/var/lib/postgresql/data
- ./init-scripts:/docker-entrypoint-initdb.d
Expand Down
8 changes: 8 additions & 0 deletions init-scripts/users.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
57 changes: 57 additions & 0 deletions src/main/java/com/p2ps/auth/controller/AuthController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.p2ps.auth.controller;

import com.p2ps.auth.security.dto.LoginRequest;
import com.p2ps.auth.dto.RegisterRequest;
import com.p2ps.auth.security.JwtUtil;
import com.p2ps.auth.service.UserService;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

import java.util.Collections;
import java.util.Map;

@RestController
@RequestMapping("/api/auth")
public class AuthController { // Acolada clasei deschisă aici

private final UserService userService;
private final AuthenticationManager authenticationManager;
private final JwtUtil jwtUtil;

public AuthController(UserService userService, AuthenticationManager authenticationManager, JwtUtil jwtUtil) {
this.userService = userService;
this.authenticationManager = authenticationManager;
this.jwtUtil = jwtUtil;
}

@PostMapping("/register")
public ResponseEntity<Map<String, String>> register(@Valid @RequestBody RegisterRequest request) {
userService.registerUser(
request.getEmail(),
request.getPassword(),
request.getFirstName(),
request.getLastName()
);
return ResponseEntity.ok(Map.of("message", "User registered successfully!"));
}

@PostMapping("/login")
public ResponseEntity<Map<String, String>> login(@Valid @RequestBody LoginRequest request) {

Authentication authentication = authenticationManager.authenticate(

Check warning on line 45 in src/main/java/com/p2ps/auth/controller/AuthController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused "authentication" local variable.

See more on https://sonarcloud.io/project/issues?id=P2P-Shopping_P2P-Shopping&issues=AZ1GXqfCPU4l3pg_Xxgl&open=AZ1GXqfCPU4l3pg_Xxgl&pullRequest=133
new UsernamePasswordAuthenticationToken(
request.getEmail(),
request.getPassword()
)
);

Check warning on line 50 in src/main/java/com/p2ps/auth/controller/AuthController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this useless assignment to local variable "authentication".

See more on https://sonarcloud.io/project/issues?id=P2P-Shopping_P2P-Shopping&issues=AZ1GXqfCPU4l3pg_Xxgk&open=AZ1GXqfCPU4l3pg_Xxgk&pullRequest=133


String token = jwtUtil.generateToken(request.getEmail());

return ResponseEntity.ok(Collections.singletonMap("token", token));
}
}
38 changes: 38 additions & 0 deletions src/main/java/com/p2ps/auth/dto/RegisterRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.p2ps.auth.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;



import lombok.*;

@Getter
@Setter
public class RegisterRequest {

@NotBlank(message = "First name is required")
@Size(min = 2, max = 50, message = "First name must be between 2 and 50 characters")
@Pattern(regexp = "^[a-zA-Z\\s-]+$", message = "First name can only contain letters, spaces, or hyphens")
private String firstName;

@NotBlank(message = "Last name is required")
@Size(min = 2, max = 50, message = "Last name must be between 2 and 50 characters")
@Pattern(regexp = "^[a-zA-Z\\s-]+$", message = "Last name can only contain letters, spaces, or hyphens")
private String lastName;

@NotBlank(message = "Email is required")
@Email(message = "Invalid email format")
@Size(max = 255)
private String email;

@NotBlank(message = "Password is required")
@Size(min = 8, max = 100)
@Pattern(regexp = "^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$",
message = "Password must contain at least one digit, one lowercase letter, and one uppercase letter")
private String password;


}
40 changes: 40 additions & 0 deletions src/main/java/com/p2ps/auth/model/Users.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.p2ps.auth.model;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
@Table(name = "users")
public class Users {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

@Column(nullable = false, unique = true)
private String email;

@Column(nullable = false)
private String password;


public Users() {}
@Column(name = "first_name", nullable = false)
private String firstName;

@Column(name = "last_name", nullable = false)
private String lastName;

public Users(String email, String password, String firstName, String lastName) {
this.email = email;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
}



}
10 changes: 10 additions & 0 deletions src/main/java/com/p2ps/auth/repository/UserRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.p2ps.auth.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import com.p2ps.auth.model.Users;
import java.util.Optional;

public interface UserRepository extends JpaRepository<Users, Integer> {
Optional<Users> findByEmail(String email);
boolean existsByEmail(String email);
}
62 changes: 62 additions & 0 deletions src/main/java/com/p2ps/auth/security/JwtAuthFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.p2ps.auth.security;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.ArrayList;

@Component
public class JwtAuthFilter extends OncePerRequestFilter {

private final JwtUtil jwtUtil;

public JwtAuthFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}

@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {

String token = null;
String userEmail = null;

// Extract token from Authorization header
String authorizationHeader = request.getHeader("Authorization");
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
token = authorizationHeader.substring(7);
}

try {
if (token != null) {
userEmail = jwtUtil.extractEmail(token);
}

// Authenticate if user is not already in the SecurityContext
if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null && !jwtUtil.isTokenExpired(token)){
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
userEmail, null, new ArrayList<>()
);
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

// Set user as authenticated
SecurityContextHolder.getContext().setAuthentication(authToken);
}

} catch (Exception _) {
// Clear context if token is expired, malformed, or invalid
SecurityContextHolder.clearContext();
}

// Continue the filter chain
filterChain.doFilter(request, response);
}
}
67 changes: 67 additions & 0 deletions src/main/java/com/p2ps/auth/security/JwtUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.p2ps.auth.security;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;

@Component
public class JwtUtil {

@Value("${jwt.secret}")
private String secretKeyString;

private SecretKey secretKey;

private static final long EXPIRATION_TIME = 1000L * 60 * 60 * 24; // 24h

@PostConstruct
public void init() {

byte[] keyBytes = secretKeyString == null
? new byte[0]
: secretKeyString.getBytes(StandardCharsets.UTF_8);

if (keyBytes.length < 32) {
throw new IllegalStateException("JWT secret must be at least 32 bytes for HS256");
}
this.secretKey = Keys.hmacShaKeyFor(keyBytes);
}

public String generateToken(String email) {

return Jwts.builder()
.subject(email)
.issuedAt(new Date(System.currentTimeMillis()))
.expiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(secretKey)
.compact();
}

public String extractEmail(String token) {
return extractAllClaims(token).getSubject();
}

public boolean isTokenExpired(String token) {
return extractAllClaims(token).getExpiration().before(new Date());
}

public boolean isTokenValid(String token, String userEmailFromDatabase) {
final String email = extractEmail(token);
return (email.equals(userEmailFromDatabase) && !isTokenExpired(token));
}

private Claims extractAllClaims(String token) {
return Jwts.parser()
.verifyWith(secretKey)
.build()
.parseSignedClaims(token)
.getPayload();
}
}
Loading
Loading