Skip to content
Snippets Groups Projects
Commit 7ac5ad98 authored by Joshua Gill's avatar Joshua Gill
Browse files

Push so Carl can help with issues

parent 3a2df964
No related branches found
No related tags found
5 merge requests!56tags will be saved to userFavTags table (needs user ID of current logged in user),!30merge,!24All Acceptance Criteria Met, password fields on "script.sql" and...,!23IssueSix complete,!21IssueSix Complete, merging branch
Showing with 385 additions and 20 deletions
......@@ -19,16 +19,20 @@ repositories {
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
implementation 'org.jetbrains:annotations:20.1.0'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client:2.7.4'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
......
package com.example.clientproject.data.users;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
import java.util.Optional;
......
package com.example.clientproject.service.dtos;
import com.example.clientproject.data.shops.Shops;
import com.example.clientproject.data.stampBoards.StampBoards;
import com.example.clientproject.data.tags.Tags;
import com.example.clientproject.data.twoFactorMethods.TwoFactorMethods;
import com.example.clientproject.data.users.Users;
import lombok.AllArgsConstructor;
import lombok.Value;
import java.util.List;
import java.util.Set;
@Value
@AllArgsConstructor
public class UsersDTO {
long userId;
String userFirstName;
String userLastName;
String userEmail;
String userPassword;
String userProfilePicture;
String userResetCode;
String userResetCodeExpiry;
TwoFactorMethods twoFactorMethod;
List<Shops> favouriteShops;
Set<StampBoards> stampBoards;
List<Tags> favouriteTags;
public UsersDTO(Users user) {
this(
user.getUserId(),
user.getUserFirstName(),
user.getUserLastName(),
user.getUserEmail(),
user.getUserPassword(),
user.getUserProfilePicture(),
user.getUserResetCode(),
user.getUserResetCodeExpiry(),
user.getTwoFactorMethod(),
user.getFavouriteShops(),
user.getStampBoards(),
user.getFavouriteTags());
}
}
package com.example.clientproject.service.searches;
import com.example.clientproject.service.dtos.UsersDTO;
import java.util.List;
import java.util.Optional;
public interface UsersSearch {
List<UsersDTO> findAll();
Optional<UsersDTO> findByEmail(String email);
}
package com.example.clientproject.service.searches.impls;
import com.example.clientproject.data.users.Users;
import com.example.clientproject.data.users.UsersRepo;
import com.example.clientproject.service.dtos.UsersDTO;
import com.example.clientproject.service.searches.UsersSearch;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
public class UsersSearchImpl implements UsersSearch {
private final UsersRepo usersRepo;
public UsersSearchImpl(UsersRepo aUsersRepo) {
usersRepo = aUsersRepo;
}
@Override
public List<UsersDTO> findAll() {
return usersRepo
.findAll()
.stream()
.map(UsersDTO::new)
.collect(Collectors.toList());
}
@Override
public Optional<UsersDTO> findByEmail(String email) {
Optional<Users> usersOptional = usersRepo.findByUserEmail(email);
if (usersOptional.isPresent()) {
UsersDTO usersDTO = new UsersDTO(usersOptional.get());
return Optional.of(usersDTO);
} else {
return Optional.empty();
}
}
}
package com.example.clientproject.web.controllers;
import com.example.clientproject.service.dtos.UsersDTO;
import com.example.clientproject.service.searches.UsersSearch;
import com.example.clientproject.web.forms.LoginForm;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.ArrayList;
import java.util.Arrays;
import javax.validation.Valid;
import java.security.SecureRandom;
import java.util.*;
@Controller
public class SignInController {
public static boolean loggedIn = false;
private UsersSearch usersSearch;
public SignInController(UsersSearch aUsersSearch) {
usersSearch = aUsersSearch;
}
@GetMapping("/register")
public String registerBusiness(Model model){
ArrayList<String> categories = new ArrayList<>(Arrays.asList("Food and drink","Animals","Alcohol"));
model.addAttribute("categories", categories);
model.addAttribute("loggedIn", loggedIn);
return "registerbusiness.html";
}
/**
* Method for accessing the login page
* An attribute is assigned to the model called "loggedIn", which is false by default as all members accessing this page will not be logged in by this point
* @param model - Model object which will be populated and passed into the view
* @return - the page to redirect to
*/
@GetMapping("/login")
public String loginPageAccess(Model model) {
LoginForm loginForm = new LoginForm();
model.addAttribute("loginForm", loginForm);
model.addAttribute("loggedIn", loggedIn);
return "account-login.html";
}
@PostMapping("/login")
public String signInChecks(@Valid LoginForm loginForm,
BindingResult bindingResult,
Model model) {
if (bindingResult.hasErrors()) {
model.addAttribute("loggedIn", loggedIn);
return "account-login.html";
}
Optional<UsersDTO> usersDTOOptional = usersSearch.findByEmail(loginForm.getLoginEmail());
// If the optional is present - the search found a user with that email
if (usersDTOOptional.isPresent()) {
Random random = new SecureRandom();
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
// Credits to user "Assylias" https://stackoverflow.com/questions/18142745/how-do-i-generate-a-salt-in-java-for-salted-hash
byte[] salt = new byte[16];
random.nextBytes(salt);
// For testing, removed random salt generator
// String generatedSalt = Base64.getEncoder().encodeToString(salt);
// Check the password given (after encoding) and the user's DB password match
boolean passwordMatch = usersDTOOptional
.get()
.getUserPassword()
.equals(
passwordEncoder.encode(
loginForm
// Commented for testing purposes
//.getLoginPassword() + generatedSalt
.getLoginPassword() + "EXAMPLESALT"
)
);
// If they match, set the loggedIn flag to true
if (passwordMatch) {
loggedIn = true;
// Otherwise, redirect back to the login page with an appropriate error
} else {
model.addAttribute("incorrectPassword", true);
model.addAttribute("loggedIn", loggedIn);
return "account-login.html";
}
// Else - assumes that the email is incorrect
} else {
model.addAttribute("incorrectEmail", true);
model.addAttribute("loggedIn", loggedIn);
return "account-login.html";
}
model.addAttribute("loggedIn", loggedIn);
return "index.html";
}
}
package com.example.clientproject.web.forms;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.Email;
import javax.validation.constraints.Size;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginForm {
// Following validation found at: https://www.baeldung.com/javax-validation
@Email(message="Email must be valid")
private String loginEmail;
@Size(min=7, max=15, message="Password must be between 7 and 15 characters")
private String loginPassword;
}
.flex-container {
display: flex;
height: 100vh;
}
.vert-flex-container {
display: flex;
height: inherit;
flex-direction: column;
justify-content:center;
align-items:center;
}
.name-box-container {
justify-content: space-between;
align-items: center;
height: auto;
width: 50%
}
.signUpPart {
background: rgb(1,126,255);
background: linear-gradient(90deg, rgba(1,126,255,1) 0%, rgba(0,76,255,1) 100%);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
}
h1 {
color: rgb(1,126,255);
font-size: 35px;
text-align: center;
margin-bottom: 20px;
}
input {
color: #f4f8f7;
}
.top-and-bottom-margin {
margin-top: 10px;
margin-bottom: 10px;
}
.small-name-box {
margin-top: 10px;
margin-bottom: 10px;
width:46%;
background-color: #f4f8f7;
}
.large-input-box {
margin: 10px;
width: 50%;
background-color: #f4f8f7;
}
.signIn-Button {
margin: 10px;
background-color: rgb(1,126,255);
color: white;
width: 140px;
}
h2 {
font-size: 40px;
font-family: sans-serif;
color: white;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
}
.special-text {
font-size: 35px;
font-family: sans-serif;
color: white;
margin-bottom: 20px;
text-align: center;
font-weight: 100;
}
.signUp-Button {
margin: 10px;
background-color: transparent;
border-color: white;
color: white !important;
width: 140px;
border-width: 2px;
font-weight: bold;
}
.logo {
position: absolute !important;
top: 40px !important;
}
img {
position: absolute !important;
top: 40px !important;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ShopHub | Account Login</title>
<link th:replace="fragments/libs.html :: bulma"/>
<link th:replace="fragments/libs.html :: fa"/>
<link th:replace="fragments/nav.html :: nav-css"/>
<link rel="stylesheet" type="text/css" href="css/signIn.css">
</head>
<body>
<div th:replace="fragments/nav.html :: nav"></div>
<div class="alert alert-warning" th:if="${incorrectEmail}">Email Incorrect</div>
<div class="alert alert-warning" th:if="${incorrectPassword}">Password Incorrect</div>
<div class="flex-container">
<div class="signUpPart" style="flex-grow: 2;">
<img style="position: absolute; top: 45px" src="imgs/Logo.png" width="112" height="28" class="logo">
<h2>Welcome!</h2>
<h3 class="special-text">New Member of <br>ShopHub?</h3>
<button class="button is-rounded signUp-Button">Sign Up</button>
</div>
<div class="vert-flex-container" style="flex-grow: 5;">
<h1 class="has-text-weight-bold">Start collecting rewards <br>today!</h1>
<form th:action="@{/login}" th:method="post" th:objects="${loginForm}">
<div class="flex-container name-box-container">
<div>
<label th:for="loginEmail">Email:</label>
<input class="input small-name-box" type="text" placeholder="Email" th:field="*{loginEmail}"/>
<div class="alert alert-warning" th:errors="*{loginEmail}" th:if="${#fields.hasErrors('loginEmail')}">Email Error</div>
</div>
<br>
<div>
<label th:for="loginPassword">Password:</label>
<input class="input small-name-box" type="text" placeholder="Password" th:field="*{loginPassword}"/>
<div class="alert alert-warning" th:errors="*{loginPassword}" th:if="${#fields.hasErrors('loginPassword')}">Password Error</div>
</div>
</div>
<button type="submit" class="button is-rounded signIn-Button">Sign In</button>
</form>
</div>
</div>
</body>
</html>
\ No newline at end of file
......@@ -25,25 +25,31 @@
<div class="navbar-end">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">
Hi Alice!
<div class="nav_profile_img" style="background-image: url('imgs/profile.jpg')"></div>
</a>
<div class="navbar-dropdown is-right">
<a class="navbar-item">
<span class="icon mr-2"><i class="fas fa-cog"></i></span>
Settings
</a>
<a class="navbar-item">
<span class="icon mr-2"><i class="fas fa-briefcase"></i></span>
Business
</a>
<hr class="navbar-divider">
<a class="navbar-item">
<span class="icon mr-2"><i class="fas fa-sign-out-alt"></i></span>
Log Out
<div th:if="${loggedIn}">
<a class="navbar-link">
Hi Alice!
<div class="nav_profile_img" style="background-image: url('imgs/profile.jpg')"></div>
</a>
<div class="navbar-dropdown is-right">
<a class="navbar-item">
<span class="icon mr-2"><i class="fas fa-cog"></i></span>
Settings
</a>
<a class="navbar-item">
<span class="icon mr-2"><i class="fas fa-briefcase"></i></span>
Business
</a>
<hr class="navbar-divider">
<a class="navbar-item">
<span class="icon mr-2"><i class="fas fa-sign-out-alt"></i></span>
Log Out
</a>
</div>
</div>
<div th:if="!loggedIn">
<a class="navbar-link" href="/login">Login</a>
</div>
</div>
</div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment