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
34 changes: 0 additions & 34 deletions .github/workflows/ci-develop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,40 +49,6 @@ jobs:
npm run test -- --watch=false --code-coverage --browsers=ChromeHeadless
working-directory: frontend

e2e:
name: Cypress E2E
runs-on: ubuntu-latest
needs: [backend, frontend]

steps:
- uses: actions/checkout@v4

- name: Set up Environment (Java & Node)
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'

- uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json

- name: Install Frontend
run: npm ci
working-directory: frontend

- name: Run Backend + Frontend + Cypress
run: |
cd frontend
npx start-server-and-test \
"cd ../backend && mvn spring-boot:run -Dspring-boot.run.arguments='--spring.profiles.active=test --spring.datasource.url=jdbc:h2:mem:gameswap_db;DB_CLOSE_DELAY=-1;MODE=PostgreSQL --spring.datasource.driverClassName=org.h2.Driver --spring.datasource.username=sa --spring.datasource.password= --spring.jpa.database-platform=org.hibernate.dialect.H2Dialect --spring.flyway.enabled=true --spring.security.user.name=admin --spring.security.user.password=admin'" \
"http://localhost:8080/api/usuarios" \
"npm start" \
"http://localhost:4200" \
"npx cypress run"

sonar:
name: Unified Sonar Analysis
runs-on: ubuntu-latest
Expand Down
16 changes: 16 additions & 0 deletions backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,22 @@
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ public CarritoResponseDTO findByUser(@PathVariable Long id) {

@PostMapping("/add/{idPostVenta}")
public CarritoResponseDTO addProduct(@PathVariable Long idPostVenta) {

Long idUsuario = 1L;
return service.addProduct(idPostVenta, idUsuario);
return service.addProduct(idPostVenta);
}

@PostMapping("/remove")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tfg.angel.gameswap.backend.business.dto.request;

import com.tfg.angel.gameswap.backend.business.model.enums.Rol;
import lombok.*;

import java.time.LocalDate;
Expand All @@ -15,4 +16,5 @@ public class UsuarioRequestDTO {
private String nUsuario;
private LocalDate fechaNacimiento;
private String correo;
private Rol rol;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tfg.angel.gameswap.backend.business.dto.response;

import com.tfg.angel.gameswap.backend.business.model.enums.Rol;
import lombok.*;

import java.time.LocalDate;
Expand All @@ -18,4 +19,5 @@ public class UsuarioResponseDTO {
private Double saldo;
private String correo;
private Double estrellas;
private Rol rol;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public static Usuario toEntity(UsuarioRequestDTO dto) {
.correo(dto.getCorreo())
.saldo(0.0)
.estrellas(0.0)
.rol(dto.getRol())
.build();
}

Expand All @@ -27,6 +28,7 @@ public static UsuarioResponseDTO toDTO(Usuario usuario) {
.saldo(usuario.getSaldo())
.correo(usuario.getCorreo())
.estrellas(usuario.getEstrellas())
.rol(usuario.getRol())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tfg.angel.gameswap.backend.business.model;

import com.tfg.angel.gameswap.backend.business.model.enums.Rol;
import jakarta.persistence.*;
import lombok.*;

Expand Down Expand Up @@ -32,6 +33,12 @@ public class Usuario {

private Double estrellas = 0.0;

@Enumerated(EnumType.STRING)
@Column(name = "rol")
private Rol rol;

private String password;

// Relaciones

@OneToMany(mappedBy = "comprador", fetch = FetchType.LAZY)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.tfg.angel.gameswap.backend.business.model.enums;

public enum Rol {
ADMIN,
CLIENTE
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public interface CarritoService {

CarritoResponseDTO findByUser(Long idUsuario);

CarritoResponseDTO addProduct(Long idPostVenta, Long idUsuario);
CarritoResponseDTO addProduct(Long idPostVenta);

CarritoResponseDTO removeProduct(Long idProductoCarrito);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.tfg.angel.gameswap.backend.business.service.CarritoService;
import com.tfg.angel.gameswap.backend.exception.GSBadRequestException;
import com.tfg.angel.gameswap.backend.exception.GSNotFoundException;
import com.tfg.angel.gameswap.backend.security.SecurityUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

Expand Down Expand Up @@ -46,9 +47,14 @@ public CarritoResponseDTO findByUser(Long idUsuario) {
}

@Override
public CarritoResponseDTO addProduct(Long idPostVenta, Long idUsuario) {
public CarritoResponseDTO addProduct(Long idPostVenta) {

Carrito carrito = carritoRepository.findByUsuarioId(idUsuario)
String username = SecurityUtils.getUsername();

Usuario usuario = usuarioRepository.findByNombreUsuario(username)
.orElseThrow(() -> new GSNotFoundException("Usuario no encontrado"));

Carrito carrito = carritoRepository.findByUsuarioId(usuario.getId())
.orElseThrow(() -> new GSNotFoundException("Carrito no encontrado"));

PostVenta postVenta = postVentaRepository.findById(idPostVenta)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.tfg.angel.gameswap.backend.security;

import com.tfg.angel.gameswap.backend.business.model.Usuario;
import com.tfg.angel.gameswap.backend.business.model.enums.Rol;
import com.tfg.angel.gameswap.backend.exception.GSBadRequestException;
import com.tfg.angel.gameswap.backend.security.records.AuthRequest;
import com.tfg.angel.gameswap.backend.security.records.AuthResponse;
import com.tfg.angel.gameswap.backend.security.records.RegisterRequest;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import com.tfg.angel.gameswap.backend.business.repository.UsuarioRepository;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {

private final UsuarioRepository usuarioRepository;
private final JwtService jwtService;
private final PasswordEncoder passwordEncoder;
private final TokenBlacklistService tokenBlacklistService;

@PostMapping("/login")
public AuthResponse login(@RequestBody AuthRequest request) {

var usuario = usuarioRepository.findByNombreUsuario(request.username())
.orElseThrow(() -> new RuntimeException("Usuario no encontrado"));

if (!passwordEncoder.matches(request.password(), usuario.getPassword())) {
throw new GSBadRequestException("Constraseña incorrecta");
}

String accessToken = jwtService.generateToken(
usuario.getNombreUsuario(),
usuario.getRol().name()
);

String refreshToken = jwtService.generateRefreshToken(usuario.getNombreUsuario());

return new AuthResponse(accessToken, refreshToken);
}

@PostMapping("/register")
public AuthResponse register(@RequestBody RegisterRequest request) {

if (usuarioRepository.existsByNombreUsuario(request.username())) {
throw new GSBadRequestException("El usuario ya existe");
}

var usuario = Usuario.builder()
.nombre(request.name())
.nombreUsuario(request.username())
.correo(request.correo())
.rol(Rol.CLIENTE)
.saldo(0.0)
.estrellas(0.0)
.password(passwordEncoder.encode(request.password()))
.build();

usuarioRepository.save(usuario);

String accessToken = jwtService.generateToken(
usuario.getNombreUsuario(),
usuario.getRol().name()
);

String refreshToken = jwtService.generateRefreshToken(usuario.getNombreUsuario());

return new AuthResponse(accessToken, refreshToken);
}

@PostMapping("/refresh")
public String refresh(@RequestParam String refreshToken) {

if (!jwtService.isValid(refreshToken)) {
throw new GSBadRequestException("Refresh token inválido");
}

String username = jwtService.extractUsername(refreshToken);

var usuario = usuarioRepository.findByNombreUsuario(username)
.orElseThrow();

return jwtService.generateToken(
usuario.getNombreUsuario(),
usuario.getRol().name()
);
}

@PostMapping("/logout")
public void logout(HttpServletRequest request) {

String header = request.getHeader("Authorization");

if (header != null && header.startsWith("Bearer ")) {
String token = header.substring(7);
tokenBlacklistService.blacklist(token);
}
}

@GetMapping("/admin/test")
@PreAuthorize("hasRole('ADMIN')")
public String adminOnly() {
return "ok";
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.tfg.angel.gameswap.backend.config;
package com.tfg.angel.gameswap.backend.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand Down Expand Up @@ -35,7 +35,7 @@ public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();

source.registerCorsConfiguration("/api/**", config);
source.registerCorsConfiguration("/**", config);

return new CorsFilter(source);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.tfg.angel.gameswap.backend.security;

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

import java.io.IOException;

@Component
@RequiredArgsConstructor
public class JwtFilter extends OncePerRequestFilter {

private final JwtService jwtService;
private final UsuarioDetailsService userDetailsService;
private final TokenBlacklistService tokenBlacklistService;

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

String header = request.getHeader("Authorization");

if (header != null && header.startsWith("Bearer ")) {

String token = header.substring(7);

if (tokenBlacklistService.isBlacklisted(token)) {
chain.doFilter(request, response);
return;
}

if (jwtService.isValid(token)) {

String username = jwtService.extractUsername(token);

if (SecurityContextHolder.getContext().getAuthentication() == null) {

var userDetails = userDetailsService.loadUserByUsername(username);
var auth = new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
}
chain.doFilter(request, response);
}
}
Loading
Loading