Skip to content
Snippets Groups Projects
Commit 043ff189 authored by Ethan Allen-Harris's avatar Ethan Allen-Harris
Browse files

Merge branch 'develop' into 'issueFour'

Develop

See merge request !39
parents 9f240026 9b1bc9c1
No related branches found
No related tags found
3 merge requests!56tags will be saved to userFavTags table (needs user ID of current logged in user),!50Merging for latest changes,!39Develop
Showing
with 479 additions and 9 deletions
......@@ -23,6 +23,8 @@ repositories {
}
dependencies {
implementation 'io.jsonwebtoken:jjwt:0.9.1'
implementation 'org.springframework.session:spring-session-jdbc:2.4.3'
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'
......
......@@ -45,7 +45,7 @@ public class Shops {
* @param active - shop active status
*/
public Shops(String name, String website, String description, int earnings,
String image, String countries, boolean active) {
String image, String countries, boolean active, StampBoards stampBoard) {
this.shopName = name;
this.shopDescription = description;
this.shopWebsite = website;
......@@ -53,6 +53,7 @@ public class Shops {
this.shopImage = image;
this.shopCountries = countries;
this.shopActive = active;
this.stampBoard = stampBoard;
}
@ManyToMany(mappedBy="favouriteShops")
......
package com.example.clientproject.service.Utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpSession;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
@Component
public class JWTUtils {
private static String SECRET_KEY;
@Value("${jwt.secret_key}")
private void setSECRET_KEY(String aSECRET_KEY){
SECRET_KEY = aSECRET_KEY;
}
public static void getKey(){
System.out.println(SECRET_KEY);
}
// https://github.com/oktadev/okta-java-jwt-example/blob/master/src/main/java/com/okta/createverifytokens/JWTDemo.java
public static String createJWT(String id, String issuer, String subject, long ttlMillis) {
//The JWT signature algorithm we will be using to sign the token
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
//We will sign our JWT with our ApiKey secret
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET_KEY);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
//Let's set the JWT Claims
JwtBuilder builder = Jwts.builder().setId(id)
.setIssuedAt(now)
.setSubject(subject)
.setIssuer(issuer)
.signWith(signatureAlgorithm, signingKey);
//if it has been specified, let's add the expiration
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
builder.setExpiration(exp);
}
//Builds the JWT and serializes it to a compact, URL-safe string
return builder.compact();
}
public static Claims decodeJWT(String jwt) {
//This line will throw an exception if it is not a signed JWS (as expected)
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(SECRET_KEY))
.parseClaimsJws(jwt).getBody();
return claims;
}
public static String makeUserJWT(Integer userId, HttpSession session) {
String jwtId = "loginCred";
String jwtIssuer = "ShopHub";
int jwtTimeToLive = 800000;
String jwt = JWTUtils.createJWT(
jwtId, // claim = jti
jwtIssuer, // claim = iss
userId.toString(), // claim = sub
jwtTimeToLive // used to calculate expiration (claim = exp)
);
session.setAttribute("loginCredJWT", jwt);
return jwt.toString();
}
public static Optional<Integer> getLoggedInUserId(HttpSession session){
String loginJWT = (String) session.getAttribute("loginCredJWT");
if (loginJWT == null) {
System.out.println("Jwt is null");
return Optional.empty();
}
try{
Claims claims = JWTUtils.decodeJWT(loginJWT);
return Optional.of(Integer.parseInt(claims.getSubject()));
}catch (io.jsonwebtoken.MalformedJwtException e){
System.out.println("malformed jwt");
return Optional.empty();
}catch (io.jsonwebtoken.SignatureException e){
System.out.println("JWT was edited outside this scope");
return Optional.empty();
}catch (Exception e){
System.out.println(e);
return Optional.empty();
}
}
public static void logOutUser(HttpSession session){
session.removeAttribute("loginCredJWT");
}
}
......@@ -2,8 +2,12 @@ package com.example.clientproject.services;
import com.example.clientproject.data.shops.Shops;
import com.example.clientproject.data.shops.ShopsRepo;
import com.example.clientproject.data.stampBoards.StampBoards;
import com.example.clientproject.data.stampBoards.StampBoardsRepo;
import com.example.clientproject.data.tags.Tags;
import com.example.clientproject.data.tags.TagsRepo;
import com.example.clientproject.data.userStampBoards.UserStampBoards;
import com.example.clientproject.data.userStampBoards.UserStampBoardsRepo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
......@@ -17,6 +21,9 @@ public class BusinessRegisterSaver {
@Autowired
ShopsRepo shopsRepo;
@Autowired
StampBoardsRepo stampBoards;
@Autowired
TagsRepo tagsRepo;
......@@ -25,13 +32,16 @@ public class BusinessRegisterSaver {
public void save(BusinessRegisterDTO business){
StampBoards stampBoard = stampBoards.findById(1L).get();
Shops shop = new Shops(business.getBusiness_register_name(),
business.getBusiness_register_url(),
business.getBusiness_register_desc(),
business.getEarnings(),
"shopPic.png",
"UK United Kingdom",
false);
false,
stampBoard);
shopsRepo.save(shop);
List<Tags> tagsList = tagsRepo.findAll();
......
package com.example.clientproject.services;
import com.example.clientproject.web.forms.UserFavouriteForm;
import lombok.AllArgsConstructor;
import lombok.Value;
@Value
@AllArgsConstructor
public class UserFavouriteDTO {
long userId;
long shopId;
public UserFavouriteDTO(UserFavouriteForm urf){
this(
urf.getUserId(),
urf.getShopId()
);
}
}
package com.example.clientproject.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class UserFavouriteDeleter {
@Autowired
JdbcTemplate jdbc;
/**
* Takes a userfavourite DTO and removes it from the database.
* @param usfDTO
*/
public void delete(UserFavouriteDTO usfDTO){
String query = "DELETE FROM User_Shop_Links WHERE (Shop_Id = " +
usfDTO.getShopId() +" AND User_Id = " +
usfDTO.getUserId() +")";
//System.out.println(query);
jdbc.execute(query);
}
}
package com.example.clientproject.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class UserFavouriteSaver {
@Autowired
JdbcTemplate jdbc;
/**
* Takes a user dto and saves it to the DB with jdbc
* @param urfDTO UserfavouriteDTO
*/
public void save(UserFavouriteDTO urfDTO){
String query = "INSERT INTO User_Shop_Links (Shop_Id, User_Id) VALUES ("+ urfDTO.getShopId() +
","+urfDTO.getUserId() + ")";
//System.out.println(query);
jdbc.execute(query);
}
}
package com.example.clientproject.services;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.sql.ResultSet;
import java.util.List;
import java.util.Map;
@Service
public class UserFavouriteToggle {
@Autowired
JdbcTemplate jdbc;
/**
* Checks whether the user has already favourited a shop
* @param urfDTO, Userfavourite DTO
* @return Boolean, true if it's already favourited false if not.
* @throws Exception
*/
public boolean alreadyInDb(UserFavouriteDTO urfDTO) throws Exception{
String query = "SELECT s.User_Shop_Link_Id FROM User_Shop_Links s WHERE (Shop_Id = " +
urfDTO.getShopId() + " AND User_Id = " +
urfDTO.getUserId() + " )";
List<Map<String, Object>> rs = jdbc.queryForList(query);
return rs.size() != 0;
}
}
package com.example.clientproject.web.controllers;
import com.example.clientproject.data.shops.Shops;
import com.example.clientproject.data.shops.ShopsRepo;
import com.example.clientproject.service.searches.UsersSearch;
import com.example.clientproject.services.BusinessRegisterSaver;
import com.example.clientproject.services.UserFavouriteDTO;
import com.example.clientproject.services.UserFavouriteToggle;
import com.example.clientproject.web.forms.UserFavouriteForm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.ArrayList;
import java.util.List;
import static com.example.clientproject.web.controllers.SignInController.loggedIn;
@Controller
public class HomeController {
private ShopsRepo shopsRepo;
private UserFavouriteToggle toggleFavourite;
@Autowired
public HomeController(ShopsRepo ashopsRepo) {
public HomeController(ShopsRepo ashopsRepo, UserFavouriteToggle uft) {
shopsRepo = ashopsRepo;
toggleFavourite = uft;
}
@GetMapping({"/", "dashboard"})
public String index(Model model){
public String index(Model model) throws Exception{
loggedIn=true;
if (!loggedIn) {
model.addAttribute("loggedIn", loggedIn);
return "redirect:/login";
}
//System.out.println(shopsRepo.findAll());
model.addAttribute("shops", shopsRepo.findAll());
List<Shops> allShops = shopsRepo.findAll();
List<Shops> favouriteShops = new ArrayList();
List<Shops> normalShops = new ArrayList();
for(Shops s : allShops){
UserFavouriteForm uff = new UserFavouriteForm(2,s.getShopId());
if(toggleFavourite.alreadyInDb(new UserFavouriteDTO(uff))){
favouriteShops.add(s);
}else{
normalShops.add(s);
}
}
model.addAttribute("normalShops", normalShops);
model.addAttribute("favouriteShops", favouriteShops);
model.addAttribute("tags", new String[]{"Coffee", "Vegan", "Sustainable"});
return "index";
}
......
package com.example.clientproject.web.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class Redirect {
@GetMapping("/redirect")
public String redirect(@RequestParam(name="url") String url){
try{
return "redirect:/"+url;
}catch(Exception e){
return "redirect:/";
}
}
}
package com.example.clientproject.web.controllers;
import com.example.clientproject.data.users.Users;
import com.example.clientproject.service.Utils.JWTUtils;
import io.jsonwebtoken.Claims;
import org.dom4j.rule.Mode;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Controller
public class SessionTestController {
@GetMapping("/sessionJWTTest")
public String jwtTest(Model model, HttpSession session){
Optional<Integer> user = JWTUtils.getLoggedInUserId(session);
if(user.isPresent()){
System.out.println(user.get());
}else{
System.out.println("No User");
}
System.out.println("Making jwt");
String jwt = JWTUtils.makeUserJWT(6, session);
System.out.println(jwt);
user = JWTUtils.getLoggedInUserId(session);
if(user.isPresent()){
System.out.println(user.get());
}else{
System.out.println("No User");
}
model.addAttribute("sessionData",user.get());
return "session-test";
}
}
package com.example.clientproject.web.controllers;
import com.example.clientproject.exceptions.ForbiddenErrorException;
import com.example.clientproject.service.Utils.JWTUtils;
import com.example.clientproject.service.dtos.UsersDTO;
import com.example.clientproject.service.searches.UsersSearch;
import com.example.clientproject.services.BusinessRegisterDTO;
......@@ -17,6 +18,7 @@ import org.springframework.web.bind.annotation.Mapping;
import org.springframework.web.bind.annotation.PostMapping;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Arrays;
......@@ -42,7 +44,7 @@ public class SignInController {
return "registerbusiness.html";
}
saveBusiness.save(new BusinessRegisterDTO(brf));
return "redirect:/businessRegister";
return "redirect:/redirect?url=businessRegister";
}
@GetMapping("/businessRedirect")
......@@ -114,4 +116,16 @@ public class SignInController {
return "redirect:/dashboard";
}
/**
*
* @param model
* @param session the http session of the browser accessing the site
* @return returns a redirect to the login page after clearing the session jwt
*/
@GetMapping("/log_out")
public String jwtLogout(Model model, HttpSession session){
JWTUtils.logOutUser(session);
return "redirect:/login";
}
}
package com.example.clientproject.web.forms;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserFavouriteForm {
long userId;
long shopId;
}
package com.example.clientproject.web.restControllers;
import com.example.clientproject.service.searches.UsersSearch;
import com.example.clientproject.services.*;
import com.example.clientproject.web.forms.UserFavouriteForm;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class BusinessFavouriter {
private UserFavouriteSaver saveFavourite;
private UserFavouriteToggle toggleFavourite;
private UserFavouriteDeleter deleteFavourite;
public BusinessFavouriter(UserFavouriteSaver ufs, UserFavouriteToggle uft, UserFavouriteDeleter ufd) {
saveFavourite = ufs;
toggleFavourite = uft;
deleteFavourite = ufd;
}
/**
*
* @param Submitted form, contains a UserID and ShopID
* @return ERROR or OK depending on whether it any errors are thrown.
*/
@PostMapping("/favouriteBusiness")
public String favouriteBusiness(UserFavouriteForm uff){
UserFavouriteDTO ufDTO = new UserFavouriteDTO(uff);
try{
if(toggleFavourite.alreadyInDb(ufDTO)){
deleteFavourite.delete(ufDTO);
}else{
saveFavourite.save(ufDTO);
}
return "OK";
}catch(Exception e){
e.printStackTrace();
return "ERROR";
}
}
}
......@@ -8,4 +8,10 @@ spring.datasource.password=comsc
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.mvc.ignore-default-model-on-redirect=true
\ No newline at end of file
spring.mvc.ignore-default-model-on-redirect=true
spring.session.store-type=jdbc
spring.session.jdbc.initialize-schema=always
spring.session.timeout.seconds=900
jwt.secret_key=xfVBfHdprpZqn73pbPotfLyBXNR8IWZ7MxhD59sFuBB7QjnLnWppFiZp6Yhu
\ No newline at end of file
......@@ -216,6 +216,7 @@ CREATE TABLE IF NOT EXISTS `mydb`.`User_Stamp_Boards` (
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
INSERT INTO two_factor_methods (`Two_Factor_Method_Id`, `Two_Factor_Method_Name`) VALUES (1, 'None');
INSERT INTO two_factor_methods (`Two_Factor_Method_Id`, `Two_Factor_Method_Name`) VALUES (2, 'GAuth');
......
......@@ -25,4 +25,29 @@
background-position: center; /* Center the image */
background-repeat: no-repeat; /* Do not repeat the image */
background-size: cover;
}
.starContainer .fas.fa-star{
transform: scale(0);
}
.starContainer.active .fas.fa-star{
transform: scale(1);
}
.starContainer{
cursor:pointer;
}
.favouriteStar{
color:gold;
top:2px;
right:2px;
position:absolute;
transition: transform 200ms;
}
.favouriteStar i{
color:gold;
transition: transform 200ms;
}
\ No newline at end of file
function favouriteBusiness(e,shopId){
if(e.classList.contains("active")){
e.classList.remove("active")
}else{
e.classList.add("active")
}
var xhttp = new XMLHttpRequest();
let params= 'userId=' + "2" + "&shopId=" + shopId
xhttp.open("POST", '/favouriteBusiness', true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.onload = function() {
if (xhttp.readyState === 4 && xhttp.status === 200) {
var response = xhttp.responseText
if (response == "success"){
}else{
}
} else {
console.error(xhttp.statusText);
}
};
xhttp.send(params);
}
\ No newline at end of file
......@@ -3,10 +3,19 @@
<head>
<meta charset="UTF-8">
<title>Title</title>
<script th:fragment="card_js" src="/js/favouriteBusiness.js"></script>
</head>
<body th:fragment="business_card">
<div class="business_container box">
<div class="business_container box" style="position:relative;">
<div class="image" th:style="'background-image:url(' + ${img_path} + ');'"></div>
<div class="favouriteStar starContainer" th:onclick="'favouriteBusiness(this,' + ${shopId} + ');'">
<span class="icon favouriteStar">
<i class="far fa-star"></i>
</span>
<span class="icon favouriteStar">
<i class="fas fa-star"></i>
</span>
</div>
<div class="content">
<h1 class="title is-4 mb-1" th:text="${title}"></h1>
<p class="mb-1" th:text="${reward_text}"></p>
......
......@@ -39,7 +39,7 @@
Business
</a>
<hr class="navbar-divider">
<a class="navbar-item">
<a class="navbar-item" href="/log_out">
<span class="icon mr-2"><i class="fas fa-sign-out-alt"></i></span>
Log Out
</a>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment