Skip to content

Commit

Permalink
- Feature: Securing endpoints.
Browse files Browse the repository at this point in the history
  • Loading branch information
FarukBraimo committed Jun 18, 2024
1 parent 0937184 commit 2afef92
Show file tree
Hide file tree
Showing 22 changed files with 349 additions and 28 deletions.
11 changes: 11 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: '3.8'
services:
db:
image: postgres
container_name: falcon_pg
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: 123
POSTGRES_DB: falcon
ports:
- "5432:5432"
23 changes: 14 additions & 9 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,35 @@
<groupId>com.vodacom</groupId>
<artifactId>falcon</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>falcon</name>
<name>falcon-api</name>
<description>Falcon: Travel Assistant API</description>
<properties>
<java.version>17</java.version>
<failsafe.version>3.3.2</failsafe.version>

</properties>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-jpa</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-data-redis</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-security</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>dev.failsafe</groupId>
<artifactId>failsafe</artifactId>
Expand Down
73 changes: 73 additions & 0 deletions src/main/java/com/vodacom/falcon/config/security/CustomFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.vodacom.falcon.config.security;

import com.vodacom.falcon.model.User;
import com.vodacom.falcon.repository.UserRepository;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Base64;
import java.util.Objects;

import static com.vodacom.falcon.util.FalconDefaults.AUTHORIZATION;
import static com.vodacom.falcon.util.FalconDefaults.BASIC;

@Slf4j
@Component
@RequiredArgsConstructor
public class CustomFilter extends OncePerRequestFilter {
private final UserRepository userRepository;
private final PasswordEncoderProvider encoderProvider;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
log.info("Internal filter {}", request.getPathInfo());
if (isBasicAuth(request)) {
String base64 = this.getHeader(request)
.replace(BASIC, "");
String[] credentials = new String(Base64.getDecoder()
.decode(base64))
.split(":");

String username = credentials[0];
String pass = credentials[1];

User user = userRepository.findByUsername(username);

if (Objects.isNull(user)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("User not found!");
return;
}

if (encoderProvider.passwordEncoder().matches(user.getPassword(), pass)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Wrong Password");
return;
}

Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, null);
SecurityContextHolder.getContext().setAuthentication(authentication);
}

filterChain.doFilter(request, response);
}

private boolean isBasicAuth(HttpServletRequest request) {
String header = getHeader(request);
return header != null && header.startsWith(BASIC);
}

private String getHeader(HttpServletRequest request) {
return request.getHeader(AUTHORIZATION);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.vodacom.falcon.config.security;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component
public class PasswordEncoderProvider {
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.vodacom.falcon.config.security;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import static jakarta.servlet.DispatcherType.ERROR;
import static jakarta.servlet.DispatcherType.FORWARD;

@Configuration
@EnableMethodSecurity
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private CustomFilter filter;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(authorize -> authorize
.dispatcherTypeMatchers(FORWARD, ERROR)
.permitAll()
.requestMatchers("falcon/auth/**", "falcon/insight/**")
.permitAll()
.anyRequest().denyAll()
)
.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class)
.build();
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/vodacom/falcon/config/security/UserDetails.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.vodacom.falcon.config.security;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class UserDetails {
private String username;
private String password;
}
29 changes: 29 additions & 0 deletions src/main/java/com/vodacom/falcon/controller/AuthController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.vodacom.falcon.controller;


import com.vodacom.falcon.model.request.UserRegistrationRequest;
import com.vodacom.falcon.service.AuthService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@CrossOrigin(value = "*")
@RequiredArgsConstructor
@Slf4j
@RequestMapping("falcon/auth")
public class AuthController {
private final AuthService service;

@PostMapping("/signup")
public ResponseEntity<Void> signup(@RequestBody UserRegistrationRequest userRegistrationRequest) {
service.createUser(userRegistrationRequest);
return new ResponseEntity<>(HttpStatus.ACCEPTED);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@CrossOrigin(value = "*")
@RequiredArgsConstructor
@Slf4j
@RequestMapping("falcon/insight")
public class InsightController {
private final InsightService falconInsightService;

@GetMapping("/insight")
@GetMapping()
public ResponseEntity<InsightResponse> getInsight(@RequestParam("city") String city) {
InsightResponse response = falconInsightService.getInsight(city);
return new ResponseEntity<>(response, HttpStatus.OK);
Expand Down
32 changes: 32 additions & 0 deletions src/main/java/com/vodacom/falcon/model/User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.vodacom.falcon.model;


import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.LocalDateTime;
import java.util.UUID;

@Entity
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Table(name = "falcon_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
private String username;
private String password;
private LocalDateTime createdAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.vodacom.falcon.model.request;

public record UserRegistrationRequest(String username, String password) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

import java.util.Map;

public record ExchangeRateResponse(String base, Map<String, Object> rates) {
public record ExchangeRateResponse(String date, String base, Map<String, Object> rates) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
@AllArgsConstructor
@Builder
public class InsightResponse {
private MetadataResponse metadata;
private EconomyInsightResponse economyInsight;
private ExchangeRateResponse exchangeRate;
private WeatherForecastResponse weatherForecast;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.vodacom.falcon.model.response;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Setter
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MetadataResponse {
private boolean isAuthenticatedUser;
private String message;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.vodacom.falcon.model.response.openweathermap;

import java.util.List;

public record OpenDailyWhetherResponse(String dt, String sunrise, String sunset,
String summary, OpenDailyTemperatureWeatherResponse temp) {
String summary, List<OpenCurrentWeatherDescResponse> weather,
OpenDailyTemperatureWeatherResponse temp) {
}
13 changes: 13 additions & 0 deletions src/main/java/com/vodacom/falcon/repository/UserRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.vodacom.falcon.repository;


import com.vodacom.falcon.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.UUID;

@Repository
public interface UserRepository extends JpaRepository<User, UUID> {
User findByUsername(String username);
}
34 changes: 34 additions & 0 deletions src/main/java/com/vodacom/falcon/service/AuthService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.vodacom.falcon.service;


import com.vodacom.falcon.config.security.PasswordEncoderProvider;
import com.vodacom.falcon.model.User;
import com.vodacom.falcon.model.request.UserRegistrationRequest;
import com.vodacom.falcon.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
@Slf4j
public class AuthService {
private final PasswordEncoderProvider encoderProvider;
private final UserRepository userRepository;

public void createUser(UserRegistrationRequest auth) {
User existingUser = userRepository.findByUsername(auth.username());
if (existingUser != null) {
throw new Error("User already exists! Please login");
}

User user = User.builder()
.username(auth.username())
.password(encoderProvider.passwordEncoder().encode(auth.password()))
.createdAt(LocalDateTime.now())
.build();
userRepository.save(user);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ public ExchangeRateResponse getExchangeRates(String countryCode) {

ExchangeRateResponse ratesFromMainSource = buildExchangeRates(mainExchangeRateUrl);

if (ratesFromMainSource != null) {
return ratesFromMainSource;
}
// if (ratesFromMainSource != null) {
// return ratesFromMainSource;
// }
return buildExchangeRates(optionalExchangeRateUrl);
}

Expand Down
Loading

0 comments on commit 2afef92

Please sign in to comment.