Skip to content
Snippets Groups Projects
Commit 739f4c54 authored by Gabriel Copat's avatar Gabriel Copat
Browse files

Added login validation with Spring Security

parent a797ec6e
Branches
No related tags found
1 merge request!37Resolve "As a repeat trail visitor , I want to be able to create an account so I can save and review my progress."
Showing
with 155 additions and 68 deletions
......@@ -27,7 +27,8 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.mariadb.jdbc:mariadb-java-client:2.1.2'
testImplementation 'junit:junit:4.13.1'
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'junit:junit:4.13.1'
compileOnly 'org.projectlombok:lombok'
testImplementation 'org.projectlombok:lombok:1.18.28'
compileOnly 'org.projectlombok:lombok'
......@@ -35,6 +36,7 @@ dependencies {
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-security'
// https://mvnrepository.com/artifact/org.webjars/openlayers
implementation group: 'org.webjars', name: 'openlayers', version: '5.2.0'
}
......
package Team5.SmartTowns.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import javax.sql.DataSource;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/user/**", "/userProfile").authenticated()
.anyRequest().permitAll()
)
.formLogin((login) -> login
.loginPage("/login").permitAll()
.defaultSuccessUrl("/userProfile")
)
.logout((logout) -> logout.permitAll());
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
@Bean
public UserDetailsManager userDetailsManager(DataSource dataSource){
JdbcUserDetailsManager manager = new JdbcUserDetailsManager(dataSource);
return manager;
}
}
package Team5.SmartTowns.users;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
......@@ -12,11 +13,14 @@ public class NewUser {
@NotEmpty(message = "You must type in a username.")
@NotNull
String name;
@NotEmpty(message = "You must type in a password.")
@NotNull
String password;
@NotEmpty(message = "You must type in an email.")
@NotNull
String email;
}
......@@ -27,8 +27,7 @@ public class User {
this.dragonProgress = dragonProgress;
imgPath = findImagePath();
}
public User(int id, String email, String name) {
this.id = id;
public User(String email, String name) {
this.email = email;
this.name = name;
imgPath = findImagePath();
......
......@@ -6,13 +6,14 @@ import Team5.SmartTowns.rewards.RewardsRepository;
import Team5.SmartTowns.rewards.Sticker;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.util.Arrays;
import java.util.List;
@Controller
......@@ -29,6 +30,7 @@ public class UserController {
public ModelAndView getLoginPage() {
ModelAndView mav = new ModelAndView("users/login");
mav.addObject("user", new NewUser( "", "", ""));
System.out.println(userRepository.findUserByName("Admin").getName());
return mav;
}
......@@ -38,16 +40,17 @@ public class UserController {
// TODO VALIDATE EMAIL INPUT
if (bindingResult.hasErrors()) {
System.out.println("Errors");
ModelAndView modelAndView = new ModelAndView("users/login");
modelAndView.addObject("errors", bindingResult);
return modelAndView;
}
System.out.println(user.getName());
System.out.println(user.getPassword());
if ( userRepository.doesUserExist(user.getEmail()) ) {
//TODO return modelandview for user already exists
System.out.println(user.getEmail() + " already exists");
System.out.print("LOG IN:");
System.out.println(userRepository.userLogIn(user.email, user.password));
return mav;
} else {
userRepository.addUser(user.name, user.email, user.password);
......@@ -56,28 +59,42 @@ public class UserController {
}
}
@GetMapping("/userProfile")
public ModelAndView userProfile(){
ModelAndView mav = new ModelAndView("users/userProfile");
List<Pack> allPacks = rewardsRepository.getAllPacks();
mav.addObject("packs", allPacks);
User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
System.out.println(user.getUsername());
mav.addObject("user", userRepository.findUserByName("Admin"));
mav.addAllObjects(getPackInfo("Admin", 1).getModelMap());
return mav;
}
/* USER MAPPING & FUNCTIONS */
@GetMapping("/user/{id}")
public ModelAndView getUserPage(@PathVariable int id) {
@GetMapping("/user/{username}")
public ModelAndView getUserPage(@PathVariable String username) {
ModelAndView mav = new ModelAndView("users/userProfile");
List<Pack> allPacks = rewardsRepository.getAllPacks();
mav.addObject("user", userRepository.getUserById(id));
mav.addObject("user", userRepository.findUserByName("Admin"));
mav.addObject("packs", allPacks);
userRepository.addUser("Maria", "MariaEmail", "MariaPassword");
userRepository.doesUserExist("MariaEmail");
mav.addAllObjects(getPackInfo(id, 1).getModelMap());
mav.addAllObjects(getPackInfo(username, 1).getModelMap());
return mav;
}
@GetMapping("/packInfo/{userID}/{packID}")
public ModelAndView getPackInfo(@PathVariable int userID, @PathVariable int packID) {
@GetMapping("/packInfo/{username}/{packID}")
public ModelAndView getPackInfo(@PathVariable String username, @PathVariable int packID) {
/* Displays on page the stickers present in the pack and colour the ones the
* user has acquired */
ModelAndView mav = new ModelAndView("users/userFrags :: stickersBox");
List<Sticker> allStickers = rewardsRepository.getAllStickersFromPack(packID);
List<Long> userStickers = userRepository.getUserStickersFromPack(userID, packID);
List<Long> userStickers = userRepository.getUserStickersFromPack(username, packID);
System.out.println(userStickers);
......
......@@ -5,10 +5,10 @@ import java.util.List;
public interface UserRepository {
List<User> getAllUsers();
List<Long> getUserStickersFromPack(int userID, int packID);
User getUserById(int userID);
boolean unlockSticker(int userID, int packID, int stickerID);
List<Long> getUserStickersFromPack(String username, int packID);
boolean unlockSticker(String username, int packID, int stickerID);
boolean addUser(String username, String email, String password);
boolean doesUserExist(String email);
boolean userLogIn(String username, String password);
User findUserByEmail(String email);
User findUserByName(String name);
}
......@@ -26,9 +26,8 @@ public class UserRepositoryJDBC implements UserRepository{
private void setUserMapper(){
userMapper = (rs, i) -> new User(
rs.getInt("id"),
rs.getString("email"),
rs.getString("name")
rs.getString("username")
);
}
......@@ -40,29 +39,24 @@ public class UserRepositoryJDBC implements UserRepository{
@Override
public User getUserById(int userID){
String sql= "SELECT * FROM users WHERE id=?";
List<User> result = jdbc.query(sql, userMapper, userID);
return result.isEmpty() ? null : result.get(0);
public List<Long> getUserStickersFromPack(String username, int packID) {
String sql = "SELECT stickerID FROM stickerprogress WHERE (username, packID)= (?,?)";
return jdbc.queryForList(sql, Long.class, username, packID);
}
@Override
public List<Long> getUserStickersFromPack(int userID, int packID) {
String sql = "SELECT stickerID FROM stickerprogress WHERE (userID, packID)= (?,?)";
return jdbc.queryForList(sql, Long.class, userID, packID);
}
@Override
public boolean unlockSticker(int userID, int packID, int stickerID){
String sql = "INSERT INTO stickerprogress (userID, packID, stickerID) VALUES (?,?,?)";
jdbc.update(sql, userID, packID, stickerID);
public boolean unlockSticker(String username, int packID, int stickerID){
String sql = "INSERT INTO stickerprogress (username, packID, stickerID) VALUES (?,?,?)";
jdbc.update(sql, username, packID, stickerID);
return true;
}
@Override
public boolean addUser(String username, String email, String password){
String query = "INSERT INTO users (name, email, password) VALUES (?, ?, ?)";
String query = "INSERT INTO users (username, email, password) VALUES (?, ?, ?);";
String query2= "INSERT INTO authorities (username, authority) VALUES (?,?);";
jdbc.update(query, username, email, password);
jdbc.update(query2, username, "USER");
return true;
}
@Override
......@@ -70,11 +64,18 @@ public class UserRepositoryJDBC implements UserRepository{
String query = "SELECT COUNT(email) FROM users WHERE (email) = (?)";
return !(jdbc.queryForObject(query, Integer.class, email) == 0);
}
@Override
public boolean userLogIn(String email, String password){
String query = "SELECT (password) FROM users WHERE (email) = (?)";
String dbpassword = jdbc.queryForObject(query, String.class, email);
return Objects.equals(dbpassword, password);
public User findUserByEmail(String email) {
String query = "SELECT * FROM users WHERE (email) = (?)";
List<User> result = jdbc.query(query, userMapper, email);
return result.isEmpty() ? null : result.get(0);
}
@Override
public User findUserByName(String name) {
String query = "SELECT * FROM users WHERE (username) = (?)";
List<User> result = jdbc.query(query, userMapper, name);
return result.isEmpty() ? null : result.get(0);
}
}
......@@ -3,3 +3,4 @@ spring.datasource.username=root
spring.datasource.password=comsc
spring.sql.init.mode=always
spring.sql.init.data-locations=classpath:data.sql, classpath:/static/sql/user-data.sql, classpath:/static/sql/user-progress-data.sql
DELETE FROM users;
INSERT INTO users (id, email, name, password) VALUE (1, 'admin@gmail.com', 'Admin', 'admin');
INSERT INTO users (email, name, password) VALUE ('hannah@gmail.com', 'Hannah', 'root');
INSERT INTO users (email, name, password) VALUE ('nigel@gmail.com', 'Nigel', 'root');
INSERT INTO users (email, name, password) VALUE ('oscar@gmail.com', 'Oscar', 'root');
delete from trails;
insert into trails ( Name,tru) value ( 'Caerphilly Coffee Trail',false);
insert into trails ( Name,tru) value ( 'Penarth Dragon Trail',true);
......@@ -56,10 +50,3 @@ INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (3, 1,
INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (3, 2, 'Welsh Outline', 'Welsh Heritage', '1');
INSERT INTO stickers (packID, stickerID, name, description, rarity) VALUE (3, 3, 'Welsh Spoon', 'Welsh Heritage', '1');
DELETE FROM stickerprogress;
INSERT INTO stickerprogress (userID, packID, stickerID) VALUE (1, 1, 1);
INSERT INTO stickerprogress (userID, packID, stickerID) VALUE (1, 1, 2);
INSERT INTO stickerprogress (userID, packID, stickerID) VALUE (1, 1, 3);
INSERT INTO stickerprogress (userID, packID, stickerID) VALUE (1, 1, 5);
INSERT INTO stickerprogress (userID, packID, stickerID) VALUE (1, 2, 1);
INSERT INTO stickerprogress (userID, packID, stickerID) VALUE (1, 2, 3);
\ No newline at end of file
......@@ -36,12 +36,17 @@ create table if not exists locations
CREATE TABLE IF NOT EXISTS users (
id bigint auto_increment primary key,
email varchar(128) NOT NULL ,
name varchar(30) NOT NULL,
username varchar(30) primary key NOT NULL,
email varchar(128),
password varchar(30) NOT NULL,
dragonProgress int,
dragonsLandmarkIDs longtext
enabled boolean default true,
roles varchar(128)
);
CREATE TABLE IF NOT EXISTS authorities (
id bigint primary key auto_increment NOT NULL,
username varchar(30) NOT NULL ,
authority varchar(45) NOT NULL
);
CREATE TABLE IF NOT EXISTS packs (
......@@ -64,8 +69,8 @@ CREATE TABLE IF NOT EXISTS stickers (
CREATE TABLE IF NOT EXISTS stickerProgress (
id bigint auto_increment primary key,
userID bigint NOT NULL,
FOREIGN KEY (userID) REFERENCES users(id)
username varchar(30) NOT NULL,
FOREIGN KEY (username) REFERENCES users(username)
ON DELETE CASCADE
ON UPDATE RESTRICT,
packID bigint NOT NULL,
......
......@@ -9,6 +9,8 @@
--font-size-1: 14px;
--font-size-2: 200px;
--font-size-3: 300px;
--error-colour: red;
}
*{
......@@ -363,5 +365,8 @@ and (max-device-width: 640px) {
border-color: #fff;
}
}
.alert {
color: var(--error-colour);
text-shadow: var(--error-colour) 0 0 10px;
}
function updatePack(userid, packid) {
/* Updates the trail being shown on screen to the one requested by ID */
$.get("/packInfo/" + userid + "/" + packid).done(function (fragment) {
function updatePack(url) {
/* Updates the trail being shown on screen */
$.get(url).done(function (fragment) {
let packRewardsWrapper = $("#packRewardsWrapper");
packRewardsWrapper.fadeTo("slow", 0, function () {
......
INSERT INTO users (username, password) VALUE ('Admin', 'admin');
INSERT INTO users (username, password) VALUE ('Hannah', 'root');
INSERT INTO users (username, password) VALUE ('Nigel', 'root');
INSERT INTO users (username, password) VALUE ('Oscar', 'root');
INSERT INTO authorities (username, authority) VALUE ('Admin', 'ADMIN');
INSERT INTO authorities (username, authority) VALUE ('Hannah', 'USER');
DELETE FROM stickerprogress;
INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 1, 1);
INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 1, 2);
INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 1, 3);
INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 1, 5);
INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 2, 1);
INSERT INTO stickerprogress (username, packID, stickerID) VALUE ('Admin', 2, 3);
\ No newline at end of file
......@@ -33,13 +33,15 @@
</form>
</div>
<div class="form-container sign-in">
<form th:object="${user}" action="#" th:action="@{/login/register}" th:method="POST" onsubmit="return loginFormValidation()">
<form name="f" th:action="@{/login}" th:method="POST">
<h1>Sign In</h1>
<div th:if="${param.error}" class="alert alert-error">Invalid Username or Password</div>
<div th:if="${param.logout}" class="alert alert-success">Successfully Logged out</div>
<label>
<input class="input" th:field="*{email}" id="login-email" type="email" placeholder="Email">
<input class="input" id="username" type="text" name="username" placeholder="Email">
</label>
<label>
<input class="input" th:field="*{password}" id="login-password" type="password" placeholder="Password">
<input class="input" id="password" type="password" name="password" placeholder="Password">
</label>
<a href="#" class="text">Forget Your Password?</a>
<button type="submit">Sign In</button>
......
......@@ -39,7 +39,8 @@
<div th:each="pack : ${packs}" class="packContainer">
<img class="packImg" th:src="@{'../' + ${pack.getDisplayImg()}}"
th:id="'packImg' + ${pack.getId()}" th:alt="${pack.getName()}"
th:onclick="'updatePack(' + ${user.getId()} +',' + ${pack.getId()} +');'">
th:data-url="@{/packInfo/{username}/{packID}(username=${user.getName()}, packID=${pack.getId()})}"
onclick="updatePack(this.getAttribute('data-url'))">
<h4 class="packName" th:text="${pack.getName()}"></h4>
</div>
</div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment