diff --git a/pom.xml b/pom.xml index 0ad1c1c..cd4c2e1 100644 --- a/pom.xml +++ b/pom.xml @@ -71,6 +71,20 @@ org.springframework.boot spring-boot-starter-data-mongodb + + + org.springframework.boot + spring-boot-starter-security + + + + + jakarta.servlet + jakarta.servlet-api + 6.1.0 + provided + + diff --git a/src/main/java/io/titan/portfolio/config/SecurityConfig.java b/src/main/java/io/titan/portfolio/config/SecurityConfig.java new file mode 100644 index 0000000..672519b --- /dev/null +++ b/src/main/java/io/titan/portfolio/config/SecurityConfig.java @@ -0,0 +1,30 @@ +package io.titan.portfolio.config; + +import io.titan.portfolio.security.ApiKeyFilter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +public class SecurityConfig { + + private final ApiKeyFilter apiKeyFilter; + + public SecurityConfig(ApiKeyFilter apiKeyFilter) { + this.apiKeyFilter = apiKeyFilter; + } + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(csrf -> csrf.disable()) // Disable CSRF for APIs + .addFilterBefore(apiKeyFilter, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.class) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/api/portfolio").authenticated() // Secure the endpoint + .anyRequest().permitAll() // Allow other requests + ); + + return http.build(); + } +} diff --git a/src/main/java/io/titan/portfolio/controller/EducationController.java b/src/main/java/io/titan/portfolio/controller/EducationController.java index 3d11c7a..1129f15 100644 --- a/src/main/java/io/titan/portfolio/controller/EducationController.java +++ b/src/main/java/io/titan/portfolio/controller/EducationController.java @@ -3,22 +3,14 @@ package io.titan.portfolio.controller; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -// import org.springframework.web.bind.annotation.DeleteMapping; -// import org.springframework.web.bind.annotation.GetMapping; -// import org.springframework.web.bind.annotation.PathVariable; -// import org.springframework.web.bind.annotation.PostMapping; -// import org.springframework.web.bind.annotation.PutMapping; -// import org.springframework.web.bind.annotation.RequestBody; -// import org.springframework.web.bind.annotation.RequestMapping; -// import org.springframework.web.bind.annotation.RestController; import io.titan.portfolio.model.Education; import io.titan.portfolio.service.EducationService; import java.util.List; -@RestController -@RequestMapping("/api/portfolio/education") +// @RestController +// @RequestMapping("/api/portfolio/education") public class EducationController { private final EducationService educationService; @@ -26,25 +18,25 @@ public class EducationController { this.educationService = educationService; } - @GetMapping + // @GetMapping List getAllEducation(){ return educationService.getAllEducation(); } - @GetMapping("/{id}") + // @GetMapping("/{id}") ResponseEntity getEducationById(@PathVariable String id) { return educationService.getEducationById(id) .map(ResponseEntity::ok) .orElse(ResponseEntity.notFound().build()); } - @PostMapping + // @PostMapping ResponseEntity createEducation(@RequestBody Education education) { educationService.createEducation(education); return ResponseEntity.status(HttpStatus.CREATED).build(); } - @PutMapping("/{id}") + // @PutMapping("/{id}") ResponseEntity updateEducation(@PathVariable String id, @RequestBody Education education) { if (!id.equals(education.id())) { return ResponseEntity.badRequest().build(); @@ -53,7 +45,7 @@ public class EducationController { return ResponseEntity.ok(education); } - @DeleteMapping("/{id}") + // @DeleteMapping("/{id}") ResponseEntity deleteEducation(@PathVariable String id) { boolean deleted = educationService.deleteEducation(id); return deleted ? ResponseEntity.ok().build() : ResponseEntity.notFound().build(); diff --git a/src/main/java/io/titan/portfolio/controller/PortfolioController.java b/src/main/java/io/titan/portfolio/controller/PortfolioController.java new file mode 100644 index 0000000..a64585d --- /dev/null +++ b/src/main/java/io/titan/portfolio/controller/PortfolioController.java @@ -0,0 +1,39 @@ +package io.titan.portfolio.controller; + +import io.titan.portfolio.model.Education; +// import io.titan.portfolio.model.Experience; +import io.titan.portfolio.service.EducationService; +// import io.titan.portfolio.service.ExperienceService; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +// import org.springframework.beans.factory.annotation.Value; + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/portfolio") +public class PortfolioController { + private final EducationService educationService; + + // @Value("${api.key}") + // private String apiKey; + + PortfolioController(EducationService educationService){ + this.educationService = educationService; + } + + @GetMapping + public ResponseEntity> getPortfolio() { + List educationList = educationService.getAllEducation(); + // List experienceList = experienceService.getAllExperience(); + + Map portfolio = Map.of( + "education", educationList + // "experience", experienceList + ); + + return ResponseEntity.ok(portfolio); + } +} diff --git a/src/main/java/io/titan/portfolio/repository/EducationRepository.java b/src/main/java/io/titan/portfolio/repository/EducationRepository.java index 8ab7452..1636632 100644 --- a/src/main/java/io/titan/portfolio/repository/EducationRepository.java +++ b/src/main/java/io/titan/portfolio/repository/EducationRepository.java @@ -4,17 +4,6 @@ import io.titan.portfolio.model.Education; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.stereotype.Repository; -// import org.springframework.jdbc.core.JdbcTemplate; -// import org.springframework.jdbc.core.RowMapper; -// import org.springframework.jdbc.support.GeneratedKeyHolder; -// import org.springframework.jdbc.support.KeyHolder; -// import org.springframework.stereotype.Repository; - -// import java.sql.PreparedStatement; -// import java.sql.Statement; -// import java.util.Arrays; -// import java.util.List; -// import java.util.Optional; @Repository public interface EducationRepository extends MongoRepository{ diff --git a/src/main/java/io/titan/portfolio/security/ApiKeyFilter.java b/src/main/java/io/titan/portfolio/security/ApiKeyFilter.java new file mode 100644 index 0000000..7d2e48f --- /dev/null +++ b/src/main/java/io/titan/portfolio/security/ApiKeyFilter.java @@ -0,0 +1,39 @@ +package io.titan.portfolio.security; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +public class ApiKeyFilter extends OncePerRequestFilter { + + @Value("${api.key}") + private String apiKey; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + String requestApiKey = request.getHeader("X-API-KEY"); + + if (apiKey.equals(requestApiKey)) { + Authentication auth = new PreAuthenticatedAuthenticationToken("apiKeyUser", null, null); + SecurityContextHolder.getContext().setAuthentication(auth); + } else { + response.sendError(HttpStatus.UNAUTHORIZED.value(), "Invalid API Key"); + return; + } + + filterChain.doFilter(request, response); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1672902..452ffdb 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,18 @@ -spring.data.mongodb.uri=mongodb://portfolio_user:portfolio_P%40ssword@localhost:27017/portfolio_db -spring.application.name=titan_backend -spring.devtools.restart.enabled=true +# spring.application.name=titan_backend +# spring.devtools.restart.enabled=true +# server.port=8282 +# spring.data.mongodb.uri=${MONGO_URL} +# api.key=${API_KEY} + +spring.profiles.active=dev + +# # Logging level +# logging.level.root=INFO +# Development MongoDB connection +spring.data.mongodb.uri=mongodb://portfolio_user:portfolio_P%40ssword@localhost:27017/portfolio_db +# api.key=dev-api-key-12345 server.port=8282 -# logging.level.root=DEBUG + +# Logging Level +logging.level.root=DEBUG