diff --git a/00-backend/datasource/database.sqlite b/00-backend/datasource/database.sqlite index 670744a..a0688fc 100644 Binary files a/00-backend/datasource/database.sqlite and b/00-backend/datasource/database.sqlite differ diff --git a/00-backend/src/main/java/de/htwsaar/webshop/config/ControllerPathConfig.java b/00-backend/src/main/java/de/htwsaar/webshop/config/ControllerPathConfig.java index e37e24b..1c8302f 100644 --- a/00-backend/src/main/java/de/htwsaar/webshop/config/ControllerPathConfig.java +++ b/00-backend/src/main/java/de/htwsaar/webshop/config/ControllerPathConfig.java @@ -16,6 +16,9 @@ public class ControllerPathConfig { public static final String ACCOUNT_BASE = "/account"; public static final String EMAIL_BASE = "/email"; + //SessionController + public static final String SESSION_BASE = "/session"; + //ArticleController public static final String ARTICLE_BASE = "/article"; public static final String ARTICLE_GET_ALL = ARTICLE_BASE + "/all"; @@ -34,9 +37,4 @@ public class ControllerPathConfig { //ReviewController public static final String REVIEW_BASE = "/review"; public static final String REVIEW_GET_ALL = REVIEW_BASE + "/all"; - - //ImageHardcodeController - - private static final String HARDCODE_IMAGE = "/images"; - public static final String HARDCODE_IMAGE_DPS_STICKER = HARDCODE_IMAGE + "/dps.webp"; } \ No newline at end of file diff --git a/00-backend/src/main/java/de/htwsaar/webshop/controller/SessionController.java b/00-backend/src/main/java/de/htwsaar/webshop/controller/SessionController.java new file mode 100644 index 0000000..1852587 --- /dev/null +++ b/00-backend/src/main/java/de/htwsaar/webshop/controller/SessionController.java @@ -0,0 +1,80 @@ +package de.htwsaar.webshop.controller; + +import de.htwsaar.webshop.model.SessionModel; +import de.htwsaar.webshop.repository.entities.Account; +import de.htwsaar.webshop.repository.entities.Session; +import de.htwsaar.webshop.service.AccountService; +import de.htwsaar.webshop.service.SessionService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.UUID; + +import static de.htwsaar.webshop.config.ControllerPathConfig.SESSION_BASE; + +@RestController +@Slf4j +public class SessionController { + + private final SessionService sessionService; + private final AccountService accountService; + + @Autowired + public SessionController(SessionService sessionService, AccountService accountService) { + this.sessionService = sessionService; + this.accountService = accountService; + } + + @RequestMapping(value = SESSION_BASE, method = RequestMethod.POST, produces = "application/json") + public ResponseEntity create(@RequestParam String email, @RequestParam String password) { + if (email == null || password == null) { + log.warn("Got bad request, email and password"); + return ResponseEntity.badRequest().build(); + } + Account acc = accountService.isValidLogin(email, password); + if (acc == null) { + log.warn("Got bad request, Account does not exist"); + return ResponseEntity.badRequest().build(); + } + return ResponseEntity.ok(sessionService.create(acc).toModel()); + } + + + @RequestMapping(value = SESSION_BASE, method = RequestMethod.DELETE, produces = "application/json") + public ResponseEntity delete(@RequestParam String email, @RequestParam UUID token) { + if(email == null || token == null) { + log.warn("Got bad request, email and token"); + return ResponseEntity.badRequest().body(false); + } + Session session = sessionService.getByToken(token); + if(!sessionService.isValid(session, email)) { + log.warn("Got bad request, session is invalid"); + return ResponseEntity.badRequest().body(false); + } + sessionService.delete(session); + return ResponseEntity.ok(true); + } + + @RequestMapping(value = SESSION_BASE, method = RequestMethod.GET, produces = "application/json") + public ResponseEntity isValid(@RequestParam String email, @RequestParam UUID token) { + if(email == null || token == null) { + log.warn("Got bad request, email and token"); + return ResponseEntity.badRequest().body(false); + } + Session session = sessionService.getByToken(token); + if(session == null) { + log.warn("Got bad request, session is invalid"); + return ResponseEntity.notFound().build(); + } + if(sessionService.isValid(session, email)) { + return ResponseEntity.ok(true); + } + return ResponseEntity.notFound().build(); + } + +} diff --git a/00-backend/src/main/java/de/htwsaar/webshop/model/SessionModel.java b/00-backend/src/main/java/de/htwsaar/webshop/model/SessionModel.java new file mode 100644 index 0000000..0f7bd4e --- /dev/null +++ b/00-backend/src/main/java/de/htwsaar/webshop/model/SessionModel.java @@ -0,0 +1,27 @@ +package de.htwsaar.webshop.model; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.util.UUID; + +import static de.htwsaar.webshop.util.TimeUtil.VALID_MIN_MILLIS_TIMESTAMP; + +@Getter +@Setter +@ToString +@AllArgsConstructor +public class SessionModel { + @NotNull + UUID uuid; + + @Min(VALID_MIN_MILLIS_TIMESTAMP) + long timeout; + +} diff --git a/00-backend/src/main/java/de/htwsaar/webshop/repository/SessionRepository.java b/00-backend/src/main/java/de/htwsaar/webshop/repository/SessionRepository.java new file mode 100644 index 0000000..d200c65 --- /dev/null +++ b/00-backend/src/main/java/de/htwsaar/webshop/repository/SessionRepository.java @@ -0,0 +1,16 @@ +package de.htwsaar.webshop.repository; + +import de.htwsaar.webshop.repository.entities.Account; +import de.htwsaar.webshop.repository.entities.Session; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface SessionRepository extends JpaRepository { + + Session findByAccount(Account account); + + Session getSessionByToken(UUID token); +} diff --git a/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/Session.java b/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/Session.java new file mode 100644 index 0000000..e69b8c1 --- /dev/null +++ b/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/Session.java @@ -0,0 +1,37 @@ +package de.htwsaar.webshop.repository.entities; + + +import de.htwsaar.webshop.model.SessionModel; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.UUID; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "Sessions") +public class Session { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @JoinColumn(name = "token", nullable = false) + private UUID token; + + @Column(name = "timeout", nullable = false) + private Long timeout; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "account_id", referencedColumnName = "id", nullable = false) + private Account account; + + public SessionModel toModel() { + return new SessionModel(token, timeout); + } +} diff --git a/00-backend/src/main/java/de/htwsaar/webshop/service/SessionService.java b/00-backend/src/main/java/de/htwsaar/webshop/service/SessionService.java new file mode 100644 index 0000000..95d3e05 --- /dev/null +++ b/00-backend/src/main/java/de/htwsaar/webshop/service/SessionService.java @@ -0,0 +1,16 @@ +package de.htwsaar.webshop.service; + +import de.htwsaar.webshop.model.SessionModel; +import de.htwsaar.webshop.repository.entities.Account; +import de.htwsaar.webshop.repository.entities.Session; + +import java.util.UUID; + +public interface SessionService { + Session create(Account account); + void delete(Session session); + Session findByAccount(Account account); + Session getByToken(UUID token); + boolean isValid(Session session, String email); + boolean isValid(UUID token, String email); +} diff --git a/00-backend/src/main/java/de/htwsaar/webshop/service/impl/SessionServiceImpl.java b/00-backend/src/main/java/de/htwsaar/webshop/service/impl/SessionServiceImpl.java new file mode 100644 index 0000000..f250ec7 --- /dev/null +++ b/00-backend/src/main/java/de/htwsaar/webshop/service/impl/SessionServiceImpl.java @@ -0,0 +1,74 @@ +package de.htwsaar.webshop.service.impl; + +import de.htwsaar.webshop.model.SessionModel; +import de.htwsaar.webshop.repository.AccountRepository; +import de.htwsaar.webshop.repository.SessionRepository; +import de.htwsaar.webshop.repository.entities.Account; +import de.htwsaar.webshop.repository.entities.Session; +import de.htwsaar.webshop.service.SessionService; +import de.htwsaar.webshop.util.TimeUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.UUID; + + +@Service +@Slf4j +public class SessionServiceImpl implements SessionService { + private final SessionRepository sessionRepository; + private final AccountRepository accountRepository; + + @Autowired + public SessionServiceImpl(SessionRepository sessionRepository, AccountRepository accountRepository) { + this.sessionRepository = sessionRepository; + this.accountRepository = accountRepository; + } + + @Override + public Session create(Account account) { + long timeout = TimeUtil.nowPlusDays(7); + UUID token = UUID.randomUUID(); + log.info("Creating session with account {} and token {} with timeout {} ", account, token, timeout); + return sessionRepository.save(new Session(0L, token, timeout, account)); + } + + @Override + public void delete(Session session) { + sessionRepository.delete(session); + } + + @Override + public Session findByAccount(Account account) { + return sessionRepository.findByAccount(account); + } + + @Override + public Session getByToken(UUID token) { + return sessionRepository.getSessionByToken(token); + } + + @Override + public boolean isValid(Session session, String email) { + if(session == null || email == null) { + return false; + } + Account accountEmail = accountRepository.getAccountByEmail(email); + if(!session.getAccount().equals(accountEmail)) { + return false; + } + if(session.getTimeout() >= System.currentTimeMillis()) { + log.info("Session with email {} is expired", email); + delete(session); + return false; + } + log.info("Session with email {} is valid", email); + return true; + } + + @Override + public boolean isValid(UUID token, String email) { + return isValid(getByToken(token), email); + } +} diff --git a/00-backend/src/main/resources/db/initdb.sql b/00-backend/src/main/resources/db/initdb.sql index 4bd877f..b3f66ba 100644 --- a/00-backend/src/main/resources/db/initdb.sql +++ b/00-backend/src/main/resources/db/initdb.sql @@ -81,4 +81,15 @@ CREATE TABLE IF NOT EXISTS OrderItems FOREIGN KEY (article_id) REFERENCES Articles (id) ON DELETE SET NULL ON UPDATE CASCADE +); + +CREATE TABLE IF NOT EXISTS Sessions +( + id INTEGER NOT NULL PRIMARY KEY, + token TEXT UNIQUE NOT NULL, + timeout INTEGER NOT NULL, + account_id INTEGER NOT NULL, + FOREIGN KEY (account_id) REFERENCES Accounts (id) + ON DELETE CASCADE + ON UPDATE CASCADE ); \ No newline at end of file