From d9aaffca0d10ac9fb5f562c2779dfcc7b9376569 Mon Sep 17 00:00:00 2001
From: Sarah176 <120200645+Sarah176@users.noreply.github.com>
Date: Fri, 17 May 2024 20:37:12 +0300
Subject: [PATCH 1/4] Update .gitignore
---
.gitignore | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/.gitignore b/.gitignore
index f98b419fc..8b7d5ec12 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,30 @@
.idea/
/starter_code/target/
+
+## removed target/ in order to deploy from docker image
+
+### STS ###
+
+.apt_generated
+
+.classpath
+
+.factorypath
+
+.project
+
+.settings
+
+.springBeans
+
+.sts4-cache
+
+### IntelliJ IDEA ###
+
+.idea
+
+*.iws
+
+*.iml
+
+*.ipr
From 7c4039eda46d9b77ebae5182b2f84d8751de6250 Mon Sep 17 00:00:00 2001
From: Sarah176 <120200645+Sarah176@users.noreply.github.com>
Date: Fri, 17 May 2024 23:29:26 +0300
Subject: [PATCH 2/4] Update create user
---
starter_code/pom.xml | 20 +++++++++++++++++++
.../com/example/demo/SareetaApplication.java | 9 ++++++++-
.../demo/controllers/UserController.java | 12 +++++++++++
.../example/demo/model/persistence/User.java | 8 ++++++++
.../model/requests/CreateUserRequest.java | 7 +++++++
5 files changed, 55 insertions(+), 1 deletion(-)
diff --git a/starter_code/pom.xml b/starter_code/pom.xml
index 608bb212f..77c64bc58 100644
--- a/starter_code/pom.xml
+++ b/starter_code/pom.xml
@@ -50,6 +50,26 @@
tomcat-maven-plugin
1.1
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ com.auth0
+ java-jwt
+ 3.10.3
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
diff --git a/starter_code/src/main/java/com/example/demo/SareetaApplication.java b/starter_code/src/main/java/com/example/demo/SareetaApplication.java
index f161f699d..7b5595b1e 100644
--- a/starter_code/src/main/java/com/example/demo/SareetaApplication.java
+++ b/starter_code/src/main/java/com/example/demo/SareetaApplication.java
@@ -3,12 +3,19 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
+import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@EnableJpaRepositories("com.example.demo.model.persistence.repositories")
@EntityScan("com.example.demo.model.persistence")
-@SpringBootApplication
+@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class SareetaApplication {
+ @Bean
+ public BCryptPasswordEncoder bCryptPasswordEncoder(){
+ return new BCryptPasswordEncoder();
+ }
public static void main(String[] args) {
SpringApplication.run(SareetaApplication.class, args);
diff --git a/starter_code/src/main/java/com/example/demo/controllers/UserController.java b/starter_code/src/main/java/com/example/demo/controllers/UserController.java
index 87e8089df..8d011144f 100644
--- a/starter_code/src/main/java/com/example/demo/controllers/UserController.java
+++ b/starter_code/src/main/java/com/example/demo/controllers/UserController.java
@@ -2,9 +2,12 @@
import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
@@ -20,6 +23,8 @@
@RestController
@RequestMapping("/api/user")
+@RequiredArgsConstructor
+@Slf4j
public class UserController {
@Autowired
@@ -27,6 +32,7 @@ public class UserController {
@Autowired
private CartRepository cartRepository;
+ private final BCryptPasswordEncoder bCryptPasswordEncoder;
@GetMapping("/id/{id}")
public ResponseEntity findById(@PathVariable Long id) {
@@ -46,6 +52,12 @@ public ResponseEntity createUser(@RequestBody CreateUserRequest createUser
Cart cart = new Cart();
cartRepository.save(cart);
user.setCart(cart);
+ if(createUserRequest.getPassword().length()<7 ||
+ !createUserRequest.getPassword().equals(createUserRequest.getConfirmPassword())){
+ log.error("Error - Either length is less than 7 or pass and conf pass do not match. Unable to create {}",createUserRequest.getUsername());
+ return ResponseEntity.badRequest().build();
+ }
+ user.setPassword(bCryptPasswordEncoder.encode(createUserRequest.getPassword()));
userRepository.save(user);
return ResponseEntity.ok(user);
}
diff --git a/starter_code/src/main/java/com/example/demo/model/persistence/User.java b/starter_code/src/main/java/com/example/demo/model/persistence/User.java
index ab85ccc60..b8d8aee22 100644
--- a/starter_code/src/main/java/com/example/demo/model/persistence/User.java
+++ b/starter_code/src/main/java/com/example/demo/model/persistence/User.java
@@ -12,10 +12,14 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.Setter;
@Entity
@Table(name = "user")
+@Setter
+@Getter
public class User {
@Id
@@ -26,6 +30,10 @@ public class User {
@Column(nullable = false, unique = true)
@JsonProperty
private String username;
+
+ @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
+ @Column(nullable = false)
+ private String password;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "cart_id", referencedColumnName = "id")
diff --git a/starter_code/src/main/java/com/example/demo/model/requests/CreateUserRequest.java b/starter_code/src/main/java/com/example/demo/model/requests/CreateUserRequest.java
index a92d0bbb6..eff138c66 100644
--- a/starter_code/src/main/java/com/example/demo/model/requests/CreateUserRequest.java
+++ b/starter_code/src/main/java/com/example/demo/model/requests/CreateUserRequest.java
@@ -1,11 +1,18 @@
package com.example.demo.model.requests;
import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+@Data
public class CreateUserRequest {
@JsonProperty
private String username;
+ @JsonProperty
+ private String password;
+
+ @JsonProperty
+ private String confirmPassword;
public String getUsername() {
return username;
From 136454ae207d5303602cadfbddb2e224a3b4c08e Mon Sep 17 00:00:00 2001
From: Sarah176 <120200645+Sarah176@users.noreply.github.com>
Date: Fri, 17 May 2024 23:33:07 +0300
Subject: [PATCH 3/4] Update .gitignore
---
.gitignore | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index 8b7d5ec12..1e7380961 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
.idea/
/starter_code/target/
-
+*/target/**
## removed target/ in order to deploy from docker image
### STS ###
From a5db5cdccb124d223fc253320b18b8c5457f3640 Mon Sep 17 00:00:00 2001
From: Sarah176 <120200645+Sarah176@users.noreply.github.com>
Date: Fri, 17 May 2024 23:42:32 +0300
Subject: [PATCH 4/4] add security package
---
.../security/JWTAuthenticationFilter.java | 61 +++++++++++++++++++
.../JWTAuthenticationVerficationFilter.java | 58 ++++++++++++++++++
.../demo/security/SecurityConstants.java | 10 +++
.../demo/security/UserDetailsServiceImpl.java | 28 +++++++++
.../security/WebSecurityConfiguration.java | 48 +++++++++++++++
5 files changed, 205 insertions(+)
create mode 100644 starter_code/src/main/java/com/example/demo/security/JWTAuthenticationFilter.java
create mode 100644 starter_code/src/main/java/com/example/demo/security/JWTAuthenticationVerficationFilter.java
create mode 100644 starter_code/src/main/java/com/example/demo/security/SecurityConstants.java
create mode 100644 starter_code/src/main/java/com/example/demo/security/UserDetailsServiceImpl.java
create mode 100644 starter_code/src/main/java/com/example/demo/security/WebSecurityConfiguration.java
diff --git a/starter_code/src/main/java/com/example/demo/security/JWTAuthenticationFilter.java b/starter_code/src/main/java/com/example/demo/security/JWTAuthenticationFilter.java
new file mode 100644
index 000000000..cae76cee3
--- /dev/null
+++ b/starter_code/src/main/java/com/example/demo/security/JWTAuthenticationFilter.java
@@ -0,0 +1,61 @@
+package com.example.demo.security;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+import com.auth0.jwt.JWT;
+import com.example.demo.model.persistence.User;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import static com.auth0.jwt.algorithms.Algorithm.HMAC512;
+
+public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
+
+ private AuthenticationManager authenticationManager;
+
+ public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
+ this.authenticationManager = authenticationManager;
+ }
+
+ @Override
+ public Authentication attemptAuthentication(HttpServletRequest req,
+ HttpServletResponse res) throws AuthenticationException {
+ try {
+ User credentials = new ObjectMapper()
+ .readValue(req.getInputStream(), User.class);
+
+ return authenticationManager.authenticate(
+ new UsernamePasswordAuthenticationToken(
+ credentials.getUsername(),
+ credentials.getPassword(),
+ new ArrayList<>()));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected void successfulAuthentication(HttpServletRequest req,
+ HttpServletResponse res,
+ FilterChain chain,
+ Authentication auth) throws IOException, ServletException {
+
+ String token = JWT.create()
+ .withSubject(((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername())
+ .withExpiresAt(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME))
+ .sign(HMAC512(SecurityConstants.SECRET.getBytes()));
+ res.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token);
+ }
+}
diff --git a/starter_code/src/main/java/com/example/demo/security/JWTAuthenticationVerficationFilter.java b/starter_code/src/main/java/com/example/demo/security/JWTAuthenticationVerficationFilter.java
new file mode 100644
index 000000000..131b625f1
--- /dev/null
+++ b/starter_code/src/main/java/com/example/demo/security/JWTAuthenticationVerficationFilter.java
@@ -0,0 +1,58 @@
+package com.example.demo.security;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
+import org.springframework.stereotype.Component;
+
+import com.auth0.jwt.JWT;
+
+import static com.auth0.jwt.algorithms.Algorithm.HMAC512;
+
+@Component
+public class JWTAuthenticationVerficationFilter extends BasicAuthenticationFilter {
+
+ public JWTAuthenticationVerficationFilter(AuthenticationManager authManager) {
+ super(authManager);
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
+ throws IOException, ServletException {
+ String header = req.getHeader(SecurityConstants.HEADER_STRING);
+
+ if (header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX)) {
+ chain.doFilter(req, res);
+ return;
+ }
+
+ UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
+
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ chain.doFilter(req, res);
+ }
+
+ private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest req) {
+ String token = req.getHeader(SecurityConstants.HEADER_STRING);
+ if (token != null) {
+ String user = JWT.require(HMAC512(SecurityConstants.SECRET.getBytes())).build()
+ .verify(token.replace(SecurityConstants.TOKEN_PREFIX, ""))
+ .getSubject();
+ if (user != null) {
+ return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
+ }
+ return null;
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/starter_code/src/main/java/com/example/demo/security/SecurityConstants.java b/starter_code/src/main/java/com/example/demo/security/SecurityConstants.java
new file mode 100644
index 000000000..16db3794c
--- /dev/null
+++ b/starter_code/src/main/java/com/example/demo/security/SecurityConstants.java
@@ -0,0 +1,10 @@
+package com.example.demo.security;
+
+public class SecurityConstants {
+
+ public static final String SECRET = "oursecretkey";
+ public static final long EXPIRATION_TIME = 864_000_000; // 10 days
+ public static final String TOKEN_PREFIX = "Bearer ";
+ public static final String HEADER_STRING = "Authorization";
+ public static final String SIGN_UP_URL = "/api/user/create";
+}
\ No newline at end of file
diff --git a/starter_code/src/main/java/com/example/demo/security/UserDetailsServiceImpl.java b/starter_code/src/main/java/com/example/demo/security/UserDetailsServiceImpl.java
new file mode 100644
index 000000000..4def95b68
--- /dev/null
+++ b/starter_code/src/main/java/com/example/demo/security/UserDetailsServiceImpl.java
@@ -0,0 +1,28 @@
+package com.example.demo.security;
+
+import java.util.Collections;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+import com.example.demo.model.persistence.User;
+import com.example.demo.model.persistence.repositories.UserRepository;
+
+@Service
+public class UserDetailsServiceImpl implements UserDetailsService {
+
+ @Autowired
+ private UserRepository userRepository;
+
+ @Override
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ User user = userRepository.findByUsername(username);
+ if (user == null) {
+ throw new UsernameNotFoundException(username);
+ }
+ return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), Collections.emptyList());
+ }
+}
\ No newline at end of file
diff --git a/starter_code/src/main/java/com/example/demo/security/WebSecurityConfiguration.java b/starter_code/src/main/java/com/example/demo/security/WebSecurityConfiguration.java
new file mode 100644
index 000000000..ded486a1b
--- /dev/null
+++ b/starter_code/src/main/java/com/example/demo/security/WebSecurityConfiguration.java
@@ -0,0 +1,48 @@
+package com.example.demo.security;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+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.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+@EnableWebSecurity
+public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+ private UserDetailsServiceImpl userDetailsService;
+ private BCryptPasswordEncoder bCryptPasswordEncoder;
+
+ public WebSecurityConfiguration(UserDetailsServiceImpl userDetailsService,
+ BCryptPasswordEncoder bCryptPasswordEncoder) {
+ this.userDetailsService = userDetailsService;
+ this.bCryptPasswordEncoder = bCryptPasswordEncoder;
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.cors().and().csrf().disable().authorizeRequests()
+ .antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL).permitAll()
+ .anyRequest().authenticated()
+ .and()
+ .addFilter(new JWTAuthenticationFilter(authenticationManager()))
+ .addFilter(new JWTAuthenticationVerficationFilter(authenticationManager()))
+ .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
+ }
+
+ @Override
+ @Bean
+ public AuthenticationManager authenticationManagerBean() throws Exception {
+ return super.authenticationManagerBean();
+ }
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ auth.parentAuthenticationManager(authenticationManagerBean())
+ .userDetailsService(userDetailsService)
+ .passwordEncoder(bCryptPasswordEncoder);
+ }
+}
\ No newline at end of file