diff --git a/.idea/libraries/Maven__io_jsonwebtoken_jjwt_0_9_1.xml b/.idea/libraries/Maven__io_jsonwebtoken_jjwt_0_9_1.xml
new file mode 100644
index 000000000..f25b99b8f
--- /dev/null
+++ b/.idea/libraries/Maven__io_jsonwebtoken_jjwt_0_9_1.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_security_2_5_4.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_security_2_5_4.xml
new file mode 100644
index 000000000..68781cd55
--- /dev/null
+++ b/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_security_2_5_4.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_springframework_security_spring_security_config_5_5_2.xml b/.idea/libraries/Maven__org_springframework_security_spring_security_config_5_5_2.xml
new file mode 100644
index 000000000..2c8cd9a55
--- /dev/null
+++ b/.idea/libraries/Maven__org_springframework_security_spring_security_config_5_5_2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Maven__org_springframework_security_spring_security_web_5_5_2.xml b/.idea/libraries/Maven__org_springframework_security_spring_security_web_5_5_2.xml
new file mode 100644
index 000000000..3562f1210
--- /dev/null
+++ b/.idea/libraries/Maven__org_springframework_security_spring_security_web_5_5_2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
deleted file mode 100644
index 797acea53..000000000
--- a/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/demo.iml b/demo.iml
index 173b7c31f..50784e268 100644
--- a/demo.iml
+++ b/demo.iml
@@ -34,7 +34,9 @@
+
+
@@ -56,6 +58,7 @@
+
@@ -117,12 +120,13 @@
+
+
+
+
-
-
-
-
+
diff --git a/pom.xml b/pom.xml
index bb3ff4ffd..1702b33ee 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,11 +46,16 @@
test
-
+
- org.springframework.security
- spring-security-core
- 5.5.2
+ io.jsonwebtoken
+ jjwt
+ 0.9.1
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
diff --git a/src/main/java/com/example/demo/config/ProfileConfig.java b/src/main/java/com/example/demo/config/ProfileConfig.java
index 8305e7783..1b8d5689b 100644
--- a/src/main/java/com/example/demo/config/ProfileConfig.java
+++ b/src/main/java/com/example/demo/config/ProfileConfig.java
@@ -7,9 +7,4 @@
@Configuration
public class ProfileConfig {
-
- @Bean
- public PasswordEncoder passwordEncoder() {
- return new BCryptPasswordEncoder();
- }
}
diff --git a/src/main/java/com/example/demo/controller/ProfileController.java b/src/main/java/com/example/demo/controller/ProfileController.java
index b84624126..f0046619a 100644
--- a/src/main/java/com/example/demo/controller/ProfileController.java
+++ b/src/main/java/com/example/demo/controller/ProfileController.java
@@ -1,10 +1,18 @@
package com.example.demo.controller;
import com.example.demo.models.Profile;
+import com.example.demo.security.JwtGenerator;
+import com.example.demo.security.LoginRequest;
+import com.example.demo.security.LoginResponse;
import com.example.demo.service.ProfileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
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.security.core.context.SecurityContextHolder;
+import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@@ -13,11 +21,27 @@
@RequestMapping(value = "/profile")
public class ProfileController {
+ @Autowired
+ AuthenticationManager authenticationManager;
+
+ @Autowired
+ PasswordEncoder passwordEncoder;
+
+ @Autowired
+ JwtGenerator jwtGenerator;
+
@Autowired
ProfileService service;
@PostMapping(value = "/register")
- public ResponseEntity createProfile(@RequestBody Profile profile) {
+ public ResponseEntity> createProfile(@RequestBody Profile profile) {
+ if (service.existsByUsername(profile.getUsername())) {
+ return ResponseEntity.badRequest().body("Username is taken");
+ }
+ if (service.existsByEmail(profile.getEmail())) {
+ return ResponseEntity.badRequest().body("Email is taken");
+ }
+ profile.setPassword(passwordEncoder.encode(profile.getPassword()));
return new ResponseEntity<>(service.createProfile(profile), HttpStatus.CREATED);
}
@@ -31,9 +55,19 @@ public ResponseEntity> findAllProfiles() {
return new ResponseEntity<>(service.findAllProfiles(), HttpStatus.OK);
}
- @GetMapping(value = "/login/{username}/{password}")
- public ResponseEntity login(@PathVariable String username, @PathVariable String password) {
- return new ResponseEntity<>(service.login(username, password), HttpStatus.OK);
+ @PostMapping(value = "/login/{username}/{password}")
+ public ResponseEntity> login(@PathVariable String username, @PathVariable String password) {
+ Authentication authentication = authenticationManager
+ .authenticate(new UsernamePasswordAuthenticationToken(
+ username, password));
+
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ String token = jwtGenerator.generateToken(username);
+ Profile profile = service.findByUsername(username);
+ return new ResponseEntity<>(new LoginResponse(profile.getId(),
+ token, profile.getFirstName(), profile.getLastName(),
+ profile.getUsername(), profile.getEmail()
+ ), HttpStatus.OK);
}
@PutMapping(value = "/update")
diff --git a/src/main/java/com/example/demo/models/Channel.java b/src/main/java/com/example/demo/models/Channel.java
index 2d6a08f2f..2ebe3e9e5 100644
--- a/src/main/java/com/example/demo/models/Channel.java
+++ b/src/main/java/com/example/demo/models/Channel.java
@@ -1,5 +1,8 @@
package com.example.demo.models;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
import javax.persistence.*;
import java.util.List;
@@ -11,11 +14,24 @@ public class Channel {
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
- @ManyToMany(mappedBy = "channels")
+ @Enumerated(value = EnumType.STRING)
+ private ChannelType type;
+ @ManyToMany(fetch = FetchType.LAZY)
private List profileList;
- @OneToMany(mappedBy = "channel")
+ @OneToMany(fetch = FetchType.LAZY)
private List messages;
+ public Channel() {
+ }
+
+ public Channel(Long id, String name, ChannelType type, List profileList, List messages) {
+ this.id = id;
+ this.name = name;
+ this.type = type;
+ this.profileList = profileList;
+ this.messages = messages;
+ }
+
public Long getId() {
return id;
}
@@ -47,4 +63,12 @@ public List getMessages() {
public void setMessages(List messages) {
this.messages = messages;
}
+
+ public ChannelType getType() {
+ return type;
+ }
+
+ public void setType(ChannelType type) {
+ this.type = type;
+ }
}
diff --git a/src/main/java/com/example/demo/models/ChannelType.java b/src/main/java/com/example/demo/models/ChannelType.java
new file mode 100644
index 000000000..f0d76de03
--- /dev/null
+++ b/src/main/java/com/example/demo/models/ChannelType.java
@@ -0,0 +1,6 @@
+package com.example.demo.models;
+
+public enum ChannelType {
+ DM,
+ CHANNEL;
+}
diff --git a/src/main/java/com/example/demo/models/Message.java b/src/main/java/com/example/demo/models/Message.java
index b422f4078..7105aa569 100644
--- a/src/main/java/com/example/demo/models/Message.java
+++ b/src/main/java/com/example/demo/models/Message.java
@@ -13,26 +13,25 @@ public class Message {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Long id;
- @ManyToOne
- @JoinColumn(name = "profile_id")
- // @JsonIgnoreProperties("messages")
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "profile_id", referencedColumnName = "id")
Profile profile;
String body;
String timestamp;
- @ManyToOne
- @JoinColumn(name = "channel_id")
- // @JsonIgnoreProperties("messages")
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "channel_id", referencedColumnName = "id")
Channel channel;
public Message() {
}
- public Message(Long id, Profile profile, String body, String timestamp) {
+ public Message(Long id, Profile profile, String body, String timestamp, Channel channel) {
this.id = id;
this.profile = profile;
this.body = body;
this.timestamp = timestamp;
+ this.channel = channel;
}
public Long getId() {
@@ -43,14 +42,6 @@ public void setId(Long id) {
this.id = id;
}
- public Profile getProfile() {
- return profile;
- }
-
- public void setProfile(Profile profile) {
- this.profile = profile;
- }
-
public String getBody() {
return body;
}
@@ -67,4 +58,19 @@ public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
+ public Profile getProfile() {
+ return profile;
+ }
+
+ public void setProfile(Profile profile) {
+ this.profile = profile;
+ }
+
+ public Channel getChannel() {
+ return channel;
+ }
+
+ public void setChannel(Channel channel) {
+ this.channel = channel;
+ }
}
diff --git a/src/main/java/com/example/demo/models/Profile.java b/src/main/java/com/example/demo/models/Profile.java
index 28f3fdb20..c0c41f6fd 100644
--- a/src/main/java/com/example/demo/models/Profile.java
+++ b/src/main/java/com/example/demo/models/Profile.java
@@ -2,48 +2,111 @@
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonManagedReference;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
+import java.util.Collection;
import java.util.List;
@Entity
-public class Profile {
+public class Profile implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
+ private String token;
private String firstName;
private String lastName;
private String username;
private String password;
private String email;
- @ManyToMany
- @JoinColumn(name = "channel_id", referencedColumnName = "id")
- private List channels;
- @OneToMany(mappedBy = "profile")
- List messages;
+ private boolean enabled = true;
+// @ManyToMany(cascade = CascadeType.ALL)
+// @JoinTable(joinColumns = @JoinColumn(name = "profile_id"),
+// inverseJoinColumns = @JoinColumn(name = "channel_id"))
+// private List channels;
+// @OneToMany(mappedBy = "profile")
+// private List messages;
public Profile() {
}
- public Profile(Long id, String firstName, String lastName, String username, String password, String email, List channels, List messages) {
+ public Profile(String username, String password) {
+ this.username = username;
+ this.password = password;
+ }
+
+ public Profile(Long id, String token, String firstName, String lastName, String username, String password, String email, boolean enabled) {
this.id = id;
+ this.token = token;
this.firstName = firstName;
this.lastName = lastName;
this.username = username;
this.password = password;
this.email = email;
- this.channels = channels;
- this.messages = messages;
+ this.enabled = enabled;
+ }
+ // public Profile(Long id, String token, String firstName, String lastName, String username, String password, String email, boolean enabled, List channels) {
+// this.id = id;
+// this.token = token;
+// this.firstName = firstName;
+// this.lastName = lastName;
+// this.username = username;
+// this.password = password;
+// this.email = email;
+// this.enabled = enabled;
+// this.channels = channels;
+// }
+//
+// public Profile(Long id, String token, String firstName, String lastName, String username, String password, String email, boolean enabled, List channels, List messages) {
+// this.id = id;
+// this.token = token;
+// this.firstName = firstName;
+// this.lastName = lastName;
+// this.username = username;
+// this.password = password;
+// this.email = email;
+// this.enabled = enabled;
+// this.channels = channels;
+// this.messages = messages;
+// }
+
+ @Override
+ public String getUsername() {
+ return username;
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return enabled;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return enabled;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return enabled;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
}
- public List getMessages() {
- return messages;
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ return null;
}
- public void setMessages(List messages) {
- this.messages = messages;
+ @Override
+ public String getPassword() {
+ return password;
}
public Long getId() {
@@ -70,18 +133,10 @@ public void setLastName(String lastName) {
this.lastName = lastName;
}
- public String getUsername() {
- return username;
- }
-
public void setUsername(String username) {
this.username = username;
}
- public String getPassword() {
- return password;
- }
-
public void setPassword(String password) {
this.password = password;
}
@@ -94,11 +149,31 @@ public void setEmail(String email) {
this.email = email;
}
- public List getChannels() {
- return channels;
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
}
- public void setChannels(List channels) {
- this.channels = channels;
+// public List getChannels() {
+// return channels;
+// }
+//
+// public void setChannels(List channels) {
+// this.channels = channels;
+// }
+
+ public String getToken() {
+ return token;
}
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+// public List getMessages() {
+// return messages;
+// }
+//
+// public void setMessages(List messages) {
+// this.messages = messages;
+// }
}
diff --git a/src/main/java/com/example/demo/repository/ProfileRepo.java b/src/main/java/com/example/demo/repository/ProfileRepo.java
index 7b4b5d8b3..3ca7101a5 100644
--- a/src/main/java/com/example/demo/repository/ProfileRepo.java
+++ b/src/main/java/com/example/demo/repository/ProfileRepo.java
@@ -6,7 +6,11 @@
@Repository
public interface ProfileRepo extends JpaRepository {
- Profile findByUsernameAndPassword(String username, String password);
+// Profile findByUsernameAndPassword(String username, String password);
Profile findByUsername(String username);
+
+ boolean existsByUsername(String username);
+
+ boolean existsByEmail(String email);
}
diff --git a/src/main/java/com/example/demo/security/JwtAuthenticationEntryPoint.java b/src/main/java/com/example/demo/security/JwtAuthenticationEntryPoint.java
new file mode 100644
index 000000000..b3cfd6105
--- /dev/null
+++ b/src/main/java/com/example/demo/security/JwtAuthenticationEntryPoint.java
@@ -0,0 +1,19 @@
+package com.example.demo.security;
+
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@Component
+public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
+
+ @Override
+ public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
+ httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
+ }
+}
diff --git a/src/main/java/com/example/demo/security/JwtFilter.java b/src/main/java/com/example/demo/security/JwtFilter.java
new file mode 100644
index 000000000..0a812d70b
--- /dev/null
+++ b/src/main/java/com/example/demo/security/JwtFilter.java
@@ -0,0 +1,41 @@
+package com.example.demo.security;
+
+import com.example.demo.models.Profile;
+import com.example.demo.service.ProfileService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.WebAuthenticationDetails;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class JwtFilter extends OncePerRequestFilter {
+
+ @Autowired
+ private ProfileService service;
+
+ @Autowired
+ private JwtGenerator jwtGenerator;
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
+ String token = jwtGenerator.getToken(httpServletRequest);
+ if (token != null) {
+ String username = jwtGenerator.getUsernameFromToken(token);
+ Profile profile = service.findByUsername(username);
+ if (jwtGenerator.validateToken(token)) {
+ UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(profile, null, null);
+ authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
+
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ }
+ }
+ filterChain.doFilter(httpServletRequest, httpServletResponse);
+ }
+}
diff --git a/src/main/java/com/example/demo/security/JwtGenerator.java b/src/main/java/com/example/demo/security/JwtGenerator.java
new file mode 100644
index 000000000..c397dfba2
--- /dev/null
+++ b/src/main/java/com/example/demo/security/JwtGenerator.java
@@ -0,0 +1,66 @@
+package com.example.demo.security;
+
+import com.example.demo.models.Profile;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
+
+@Component
+public class JwtGenerator {
+
+ @Value("${chatter-box.app.jwtSecret}")
+ private String secret;
+ @Value("${chatter-box.app.jwtExpirationMs}")
+ private int expiration;
+
+ private Claims getTokenBody(String token) {
+ return Jwts.parser()
+ .setSigningKey(secret)
+ .parseClaimsJws(token)
+ .getBody();
+ }
+
+ public String getUsernameFromToken(String token) {
+ return getTokenBody(token)
+ .getSubject();
+ }
+
+ public Date getExpirationDate(String token) {
+ return getTokenBody(token)
+ .getExpiration();
+ }
+
+ public String generateToken(String username) {
+ return Jwts.builder()
+ .setSubject(username)
+ .setIssuedAt(new Date())
+ .setExpiration(new Date(new Date().getTime() + expiration))
+ .signWith(SignatureAlgorithm.HS256, secret)
+ .compact();
+ }
+
+ public String getToken(HttpServletRequest request) {
+ String header = request.getHeader("Authorization");
+ if (header != null && header.startsWith("Bearer")) {
+ return header.substring(7);
+ }
+ return null;
+ }
+
+ public boolean validateToken(String token) {
+ if (token != null) {
+ try {
+ getTokenBody(token);
+ return true;
+ } catch(Exception e) {
+ System.out.println("Error: " + e);
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/com/example/demo/security/LoginRequest.java b/src/main/java/com/example/demo/security/LoginRequest.java
new file mode 100644
index 000000000..b30c68be2
--- /dev/null
+++ b/src/main/java/com/example/demo/security/LoginRequest.java
@@ -0,0 +1,23 @@
+package com.example.demo.security;
+
+public class LoginRequest {
+
+ private String username;
+ private String password;
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
diff --git a/src/main/java/com/example/demo/security/LoginResponse.java b/src/main/java/com/example/demo/security/LoginResponse.java
new file mode 100644
index 000000000..10885d878
--- /dev/null
+++ b/src/main/java/com/example/demo/security/LoginResponse.java
@@ -0,0 +1,92 @@
+package com.example.demo.security;
+
+import com.example.demo.models.Channel;
+import com.example.demo.models.Message;
+
+import java.util.List;
+
+public class LoginResponse {
+
+ private Long id;
+ private String token;
+ private String firstName;
+ private String lastName;
+ private String username;
+ private String email;
+ private List channels;
+
+ public LoginResponse(Long id, String token, String firstName, String lastName, String username, String email, List channels) {
+ this.id = id;
+ this.token = token;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.username = username;
+ this.email = email;
+ this.channels = channels;
+ }
+
+ public LoginResponse(Long id, String token, String firstName, String lastName, String username, String email) {
+ this.id = id;
+ this.token = token;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.username = username;
+ this.email = email;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public List getChannels() {
+ return channels;
+ }
+
+ public void setChannels(List channels) {
+ this.channels = channels;
+ }
+}
diff --git a/src/main/java/com/example/demo/security/WebSecurityConfiguration.java b/src/main/java/com/example/demo/security/WebSecurityConfiguration.java
new file mode 100644
index 000000000..228e861b9
--- /dev/null
+++ b/src/main/java/com/example/demo/security/WebSecurityConfiguration.java
@@ -0,0 +1,63 @@
+package com.example.demo.security;
+
+import com.example.demo.service.ProfileService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+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;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+@Configuration
+@EnableWebSecurity
+public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+ @Autowired
+ private ProfileService service;
+
+ @Autowired
+ private JwtGenerator jwtGenerator;
+
+ @Autowired
+ private JwtAuthenticationEntryPoint entryPoint;
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean JwtFilter jwtFilter() {
+ return new JwtFilter();
+ }
+
+ @Bean
+ @Override
+ public AuthenticationManager authenticationManagerBean() throws Exception {
+ return super.authenticationManagerBean();
+ }
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ auth.userDetailsService(service).passwordEncoder(passwordEncoder());
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class)
+ .cors().and().csrf().disable()
+ .exceptionHandling().authenticationEntryPoint(entryPoint)
+ .and()
+ .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ .and()
+ .authorizeRequests().antMatchers("/profile/login/**", "/profile/register").permitAll()
+ .anyRequest().authenticated();
+ }
+}
diff --git a/src/main/java/com/example/demo/service/ChannelService.java b/src/main/java/com/example/demo/service/ChannelService.java
index 649bf9091..97d89c17e 100644
--- a/src/main/java/com/example/demo/service/ChannelService.java
+++ b/src/main/java/com/example/demo/service/ChannelService.java
@@ -1,6 +1,7 @@
package com.example.demo.service;
import com.example.demo.models.Channel;
+import com.example.demo.models.Profile;
import com.example.demo.repository.ChannelRepo;
import com.example.demo.repository.ProfileRepo;
import org.springframework.beans.factory.annotation.Autowired;
@@ -8,6 +9,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
@Service
public class ChannelService {
@@ -36,6 +38,23 @@ public List readAllChannels() {
return result;
}
+ public List findByProfileUsername(String username) {
+ List allChannels = readAllChannels();
+ return allChannels
+ .stream()
+ .filter(channel -> {
+ List profilesInChannel = channel.getProfileList();
+ for (Profile profile : profilesInChannel) {
+ if (profile.getUsername().equals(username)) {
+ return true;
+ }
+ }
+ return false;
+ })
+ .collect(Collectors.toList());
+
+ }
+
public Channel update(Long id, Channel channel) {
Channel channelInDb = readChannel(id);
channelInDb.setId(channel.getId());
diff --git a/src/main/java/com/example/demo/service/ProfileService.java b/src/main/java/com/example/demo/service/ProfileService.java
index c145e0896..50076f205 100644
--- a/src/main/java/com/example/demo/service/ProfileService.java
+++ b/src/main/java/com/example/demo/service/ProfileService.java
@@ -3,13 +3,16 @@
import com.example.demo.models.Profile;
import com.example.demo.repository.ProfileRepo;
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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
-public class ProfileService {
+public class ProfileService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@@ -17,14 +20,14 @@ public class ProfileService {
@Autowired
private ProfileRepo repository;
- public Profile createProfile(Profile profileData) {
- Profile profile = new Profile();
- profile.setFirstName(profileData.getFirstName());
- profile.setLastName(profileData.getLastName());
- profile.setUsername(profileData.getUsername());
- profile.setPassword(passwordEncoder.encode(profileData.getPassword()));
- profile.setEmail(profileData.getEmail());
- profile.setChannels(profileData.getChannels());
+ public Profile createProfile(Profile profile) {
+// Profile profile = new Profile();
+// profile.setFirstName(profileData.getFirstName());
+// profile.setLastName(profileData.getLastName());
+// profile.setUsername(profileData.getUsername());
+// profile.setPassword(passwordEncoder.encode(profileData.getPassword()));
+// profile.setEnabled(true);
+// profile.setEmail(profileData.getEmail());
return repository.save(profile);
}
@@ -32,16 +35,33 @@ public Profile findById(Long id) {
return repository.findById(id).get();
}
- public List findAllProfiles() {
- return repository.findAll();
+ @Override
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ Profile profile = repository.findByUsername(username);
+ if (profile == null) {
+ throw new UsernameNotFoundException("Profile with username " + username + " not found");
+ }
+ return profile;
}
- public Profile login(String username, String password) {
- Profile profileToCheckPassword = repository.findByUsername(username);
- if (verifyPassword(password, profileToCheckPassword)) {
- return profileToCheckPassword;
+ public Profile findByUsername(String username) {
+ Profile profile = repository.findByUsername(username);
+ if (profile == null) {
+ throw new UsernameNotFoundException("Profile with username " + username + " not found");
}
- return null;
+ return profile;
+ }
+
+ public boolean existsByUsername(String username) {
+ return repository.existsByUsername(username);
+ }
+
+ public boolean existsByEmail(String email) {
+ return repository.existsByEmail(email);
+ }
+
+ public List findAllProfiles() {
+ return repository.findAll();
}
public Profile update(Profile profileData) {
@@ -51,8 +71,4 @@ public Profile update(Profile profileData) {
public void deleteProfileById(Long id) {
repository.deleteById(id);
}
-
- private boolean verifyPassword(String password, Profile profile) {
- return passwordEncoder.matches(password, profile.getPassword());
- }
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 0de30be77..bd00f719a 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,6 +1,8 @@
spring.datasource.url=jdbc:mysql://localhost:3306/chatter_box
-spring.datasource.username=root
+spring.datasource.username=zach
spring.datasource.password=zipcode0
-spring.jpa.hibernate.ddl-auto=update
+spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
+chatter-box.app.jwtSecret=someSecretKey
+chatter-box.app.jwtExpirationMs=36000000
diff --git a/src/test/java/com/example/demo/controllers/TestProfileController.java b/src/test/java/com/example/demo/controllers/TestProfileController.java
index b5f2ea750..2af11d731 100644
--- a/src/test/java/com/example/demo/controllers/TestProfileController.java
+++ b/src/test/java/com/example/demo/controllers/TestProfileController.java
@@ -1,4 +1,55 @@
package com.example.demo.controllers;
+import com.example.demo.controller.ProfileController;
+import com.example.demo.models.Profile;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mockito;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.web.servlet.MockMvc;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@ExtendWith(SpringExtension.class)
+@WebMvcTest(controllers = ProfileController.class)
public class TestProfileController {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @MockBean
+ private ProfileController controller;
+
+ @Before
+ public void setUp() throws Exception {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ objectMapper = mapper;
+ }
+
+ private Profile profile = new Profile(2L, "", "Ben", "Smith", "Ben123", "secretpassword", "ben@gmail.com", true);
+ private Profile profile1 = new Profile("Ben123", "secretpassword");
+
+// @Test
+// public void registerTest() throws Exception {
+// Mockito.when(controller.createProfile(Mockito.any(Profile.class))).thenReturn(ResponseEntity.class);
+//
+// mockMvc.perform(post("/profile/register")
+// .contentType("application/json")
+// .content(objectMapper.writeValueAsString(profile)))
+// .andExpect(status().isOk());
+// }
}
diff --git a/src/test/java/com/example/demo/models/TestProfile.java b/src/test/java/com/example/demo/models/TestProfile.java
index d25a09f3c..7a9b119ac 100644
--- a/src/test/java/com/example/demo/models/TestProfile.java
+++ b/src/test/java/com/example/demo/models/TestProfile.java
@@ -18,7 +18,7 @@ public void nullableConstructorTest() {
Assert.assertNull(profile.getId()); Assert.assertNull(profile.getFirstName());
Assert.assertNull(profile.getLastName()); Assert.assertNull(profile.getUsername());
Assert.assertNull(profile.getPassword()); Assert.assertNull(profile.getEmail());
- Assert.assertNull(profile.getChannels()); Assert.assertNull(profile.getMessages());
+// Assert.assertNull(profile.getChannels());
}
@Test
@@ -34,13 +34,13 @@ public void constructorTest() {
List expectedMessages = Stream.of(new Message(), new Message(), new Message(), new Message()).collect(Collectors.toList());
// When
- Profile profile = new Profile(expectedId, expectedFirstName, expectedLastName, expectedUsername, expectedPassword, expectedEmail, expectedChannels, expectedMessages);
+ Profile profile = new Profile(expectedId, "", expectedFirstName, expectedLastName, expectedUsername, expectedPassword, expectedEmail, true);
// Then
Assert.assertEquals(expectedId, profile.getId()); Assert.assertEquals(expectedFirstName, profile.getFirstName());
Assert.assertEquals(expectedLastName, profile.getLastName()); Assert.assertEquals(expectedUsername, profile.getUsername());
Assert.assertEquals(expectedPassword, profile.getPassword()); Assert.assertEquals(expectedEmail, profile.getEmail());
- Assert.assertEquals(expectedChannels, profile.getChannels()); Assert.assertEquals(expectedMessages, profile.getMessages());
+// Assert.assertEquals(expectedChannels, profile.getChannels());
}
@Test
@@ -63,13 +63,12 @@ public void settersTest() {
profile.setUsername(expectedUsername);
profile.setPassword(expectedPassword);
profile.setEmail(expectedEmail);
- profile.setChannels(expectedChannels);
- profile.setMessages(expectedMessages);
+// profile.setChannels(expectedChannels);
// Then
Assert.assertEquals(expectedId, profile.getId()); Assert.assertEquals(expectedFirstName, profile.getFirstName());
Assert.assertEquals(expectedLastName, profile.getLastName()); Assert.assertEquals(expectedUsername, profile.getUsername());
Assert.assertEquals(expectedPassword, profile.getPassword()); Assert.assertEquals(expectedEmail, profile.getEmail());
- Assert.assertEquals(expectedChannels, profile.getChannels()); Assert.assertEquals(expectedMessages, profile.getMessages());
+// Assert.assertEquals(expectedChannels, profile.getChannels());
}
}
diff --git a/src/test/java/com/example/demo/services/TestProfileService.java b/src/test/java/com/example/demo/services/TestProfileService.java
index a3de52991..bafbcd09d 100644
--- a/src/test/java/com/example/demo/services/TestProfileService.java
+++ b/src/test/java/com/example/demo/services/TestProfileService.java
@@ -66,20 +66,6 @@ public void findAllProfilesTest() {
Assert.assertEquals(expectedProfiles, actualProfiles);
}
- @Test
- public void loginTest() {
- Profile profile = new Profile();
- String username = "test username";
- String password = "test password";
- profile.setUsername(username); profile.setPassword(password);
-
- Mockito.when(repository.findByUsername(username)).thenReturn(profile);
- service.login(username, password);
- service.login(username, password);
-
- Mockito.verify(repository, Mockito.times(2)).findByUsername(username);
- }
-
@Test
public void updateTest() {
Profile expectedProfile = new Profile();
@@ -103,4 +89,52 @@ public void deleteByIdTest() {
Mockito.verify(repository, Mockito.times(2)).deleteById(id);
}
+
+ @Test
+ public void existsByUsernameTest() {
+ Profile profile = new Profile();
+ String username = "Ben";
+ profile.setUsername(username);
+
+ Mockito.when(repository.existsByUsername(username)).thenReturn(true);
+ boolean existsByUsername = service.existsByUsername(username);
+
+ Assert.assertTrue(existsByUsername);
+ }
+
+ @Test
+ public void existsByEmailTest() {
+ Profile profile = new Profile();
+ String email = "Ben@gmail.com";
+ profile.setEmail(email);
+
+ Mockito.when(repository.existsByEmail(email)).thenReturn(true);
+ boolean existsByEmail = service.existsByEmail(email);
+
+ Assert.assertTrue(existsByEmail);
+ }
+
+ @Test
+ public void findByUsernameTest() {
+ Profile expectedProfile = new Profile();
+ String username = "Ben";
+ expectedProfile.setUsername(username);
+
+ Mockito.when(repository.findByUsername(username)).thenReturn(expectedProfile);
+ Profile actualProfile = service.findByUsername(username);
+
+ Assert.assertEquals(expectedProfile, actualProfile);
+ }
+
+ @Test
+ public void loadByUsernameTest() {
+ Profile expectedProfile = new Profile();
+ String username = "Ben";
+ expectedProfile.setUsername(username);
+
+ Mockito.when(repository.findByUsername(username)).thenReturn(expectedProfile);
+ Profile actualProfile = (Profile) service.loadUserByUsername(username);
+
+ Assert.assertEquals(expectedProfile, actualProfile);
+ }
}
diff --git a/target/classes/application.properties b/target/classes/application.properties
index 0de30be77..aae8e178e 100644
--- a/target/classes/application.properties
+++ b/target/classes/application.properties
@@ -1,6 +1,9 @@
spring.datasource.url=jdbc:mysql://localhost:3306/chatter_box
-spring.datasource.username=root
+spring.datasource.username=zach
spring.datasource.password=zipcode0
-spring.jpa.hibernate.ddl-auto=update
+spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
+chatter-box.app.jwtSecret=someSecretKey
+chatter-box.app.jwtExpirationMs=36000000
+
diff --git a/target/classes/com/example/demo/config/ProfileConfig.class b/target/classes/com/example/demo/config/ProfileConfig.class
index b88a9368c..bb215688e 100644
Binary files a/target/classes/com/example/demo/config/ProfileConfig.class and b/target/classes/com/example/demo/config/ProfileConfig.class differ
diff --git a/target/classes/com/example/demo/controller/ProfileController.class b/target/classes/com/example/demo/controller/ProfileController.class
index 880528534..ef3371fa9 100644
Binary files a/target/classes/com/example/demo/controller/ProfileController.class and b/target/classes/com/example/demo/controller/ProfileController.class differ
diff --git a/target/classes/com/example/demo/models/Channel.class b/target/classes/com/example/demo/models/Channel.class
index 37ca6a28f..5261695bf 100644
Binary files a/target/classes/com/example/demo/models/Channel.class and b/target/classes/com/example/demo/models/Channel.class differ
diff --git a/target/classes/com/example/demo/models/ChannelType.class b/target/classes/com/example/demo/models/ChannelType.class
new file mode 100644
index 000000000..015a40d74
Binary files /dev/null and b/target/classes/com/example/demo/models/ChannelType.class differ
diff --git a/target/classes/com/example/demo/models/Message.class b/target/classes/com/example/demo/models/Message.class
index 1b59deae1..48af9080c 100644
Binary files a/target/classes/com/example/demo/models/Message.class and b/target/classes/com/example/demo/models/Message.class differ
diff --git a/target/classes/com/example/demo/models/Profile.class b/target/classes/com/example/demo/models/Profile.class
index 523eba33e..7bb52ad81 100644
Binary files a/target/classes/com/example/demo/models/Profile.class and b/target/classes/com/example/demo/models/Profile.class differ
diff --git a/target/classes/com/example/demo/repository/ProfileRepo.class b/target/classes/com/example/demo/repository/ProfileRepo.class
index d94a94bdf..4a994ea8a 100644
Binary files a/target/classes/com/example/demo/repository/ProfileRepo.class and b/target/classes/com/example/demo/repository/ProfileRepo.class differ
diff --git a/target/classes/com/example/demo/security/JwtAuthenticationEntryPoint.class b/target/classes/com/example/demo/security/JwtAuthenticationEntryPoint.class
new file mode 100644
index 000000000..0099235e3
Binary files /dev/null and b/target/classes/com/example/demo/security/JwtAuthenticationEntryPoint.class differ
diff --git a/target/classes/com/example/demo/security/JwtFilter.class b/target/classes/com/example/demo/security/JwtFilter.class
new file mode 100644
index 000000000..52c4f5f08
Binary files /dev/null and b/target/classes/com/example/demo/security/JwtFilter.class differ
diff --git a/target/classes/com/example/demo/security/JwtGenerator.class b/target/classes/com/example/demo/security/JwtGenerator.class
new file mode 100644
index 000000000..063daf804
Binary files /dev/null and b/target/classes/com/example/demo/security/JwtGenerator.class differ
diff --git a/target/classes/com/example/demo/security/LoginRequest.class b/target/classes/com/example/demo/security/LoginRequest.class
new file mode 100644
index 000000000..0d7aecc46
Binary files /dev/null and b/target/classes/com/example/demo/security/LoginRequest.class differ
diff --git a/target/classes/com/example/demo/security/LoginResponse.class b/target/classes/com/example/demo/security/LoginResponse.class
new file mode 100644
index 000000000..b584809ce
Binary files /dev/null and b/target/classes/com/example/demo/security/LoginResponse.class differ
diff --git a/target/classes/com/example/demo/security/WebSecurityConfiguration.class b/target/classes/com/example/demo/security/WebSecurityConfiguration.class
new file mode 100644
index 000000000..f8dffa446
Binary files /dev/null and b/target/classes/com/example/demo/security/WebSecurityConfiguration.class differ
diff --git a/target/classes/com/example/demo/service/ChannelService.class b/target/classes/com/example/demo/service/ChannelService.class
index e33923115..d9d28084e 100644
Binary files a/target/classes/com/example/demo/service/ChannelService.class and b/target/classes/com/example/demo/service/ChannelService.class differ
diff --git a/target/classes/com/example/demo/service/ProfileService.class b/target/classes/com/example/demo/service/ProfileService.class
index 0504bc6fa..dce92d078 100644
Binary files a/target/classes/com/example/demo/service/ProfileService.class and b/target/classes/com/example/demo/service/ProfileService.class differ
diff --git a/target/test-classes/com/example/demo/controllers/TestProfileController.class b/target/test-classes/com/example/demo/controllers/TestProfileController.class
index f6fc918ed..e1363f97c 100644
Binary files a/target/test-classes/com/example/demo/controllers/TestProfileController.class and b/target/test-classes/com/example/demo/controllers/TestProfileController.class differ
diff --git a/target/test-classes/com/example/demo/models/TestProfile.class b/target/test-classes/com/example/demo/models/TestProfile.class
index 57155895f..2e8c2b2b0 100644
Binary files a/target/test-classes/com/example/demo/models/TestProfile.class and b/target/test-classes/com/example/demo/models/TestProfile.class differ
diff --git a/target/test-classes/com/example/demo/services/TestProfileService.class b/target/test-classes/com/example/demo/services/TestProfileService.class
index ea65c92c0..e610ff590 100644
Binary files a/target/test-classes/com/example/demo/services/TestProfileService.class and b/target/test-classes/com/example/demo/services/TestProfileService.class differ