diff --git a/media/profiles/2/images.jpeg b/media/profiles/2/images.jpeg
new file mode 100644
index 0000000..78f170f
Binary files /dev/null and b/media/profiles/2/images.jpeg differ
diff --git a/resources/WEB-INF/jsp/header.jsp b/resources/WEB-INF/jsp/header.jsp
index e62fc07..cb76053 100644
--- a/resources/WEB-INF/jsp/header.jsp
+++ b/resources/WEB-INF/jsp/header.jsp
@@ -1,9 +1,37 @@
<%@ page contentType="text/html;charset=UTF-8" %>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-
Charm <3
-
-
+
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+ |
+
+
+
\ No newline at end of file
diff --git a/resources/WEB-INF/jsp/login.jsp b/resources/WEB-INF/jsp/login.jsp
new file mode 100644
index 0000000..dc1db52
--- /dev/null
+++ b/resources/WEB-INF/jsp/login.jsp
@@ -0,0 +1,43 @@
+<%@ page contentType="text/html;charset=UTF-8" %>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+
+
+ Charm Login
+ <%@ include file="style.html" %>
+
+
+ <%@ include file="header.jsp" %>
+
+
+
+
+ ${wordBundle.getWord(error)}
+
+
+
+
+ <%@ include file="footer.jsp" %>
+
+
\ No newline at end of file
diff --git a/src/ru/charm/back/controller/ContentController.java b/src/ru/charm/back/controller/ContentController.java
index 0d750ad..3756711 100644
--- a/src/ru/charm/back/controller/ContentController.java
+++ b/src/ru/charm/back/controller/ContentController.java
@@ -21,10 +21,11 @@ public class ContentController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
- String contentPath = req.getRequestURI().replace("/content/", "");
+ String contentPath = req.getRequestURI().replace("/content", "");
+ log.info("Content download request: {} {}", req.getRequestURI(), contentPath);
resp.setContentType("application/octet-stream");
service.download(contentPath, resp.getOutputStream());
- log.info("Content successfully downloaded: {}", contentPath);
+ log.info("Content successfully downloaded: {}", req);
} catch (IOException e) {
log.error("Content download failed: {}", e.getMessage());
resp.sendError(SC_NOT_FOUND);
diff --git a/src/ru/charm/back/controller/EmailController.java b/src/ru/charm/back/controller/CredentialsController.java
similarity index 88%
rename from src/ru/charm/back/controller/EmailController.java
rename to src/ru/charm/back/controller/CredentialsController.java
index 400b3df..cbe5b2d 100644
--- a/src/ru/charm/back/controller/EmailController.java
+++ b/src/ru/charm/back/controller/CredentialsController.java
@@ -7,27 +7,21 @@ import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import ru.charm.back.controller.filter.ErrorFilter;
import ru.charm.back.dto.ProfileGetDto;
import ru.charm.back.dto.ProfileUpdateDto;
import ru.charm.back.mapper.RequestToProfileUpdateDtoMapper;
-import ru.charm.back.model.Gender;
-import ru.charm.back.model.Profile;
import ru.charm.back.model.exception.DuplicateEmailException;
import ru.charm.back.service.ProfileService;
import java.io.IOException;
-import java.time.LocalDate;
import java.util.Optional;
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
-@WebServlet("/email")
+@WebServlet("/credentials")
@Slf4j
@MultipartConfig
-public class EmailController extends HttpServlet {
+public class CredentialsController extends HttpServlet {
private final ProfileService service = ProfileService.getInstance();
private final RequestToProfileUpdateDtoMapper requestToProfileUpdateDtoMapper = RequestToProfileUpdateDtoMapper.getInstance();
diff --git a/src/ru/charm/back/controller/LoginController.java b/src/ru/charm/back/controller/LoginController.java
new file mode 100644
index 0000000..a334588
--- /dev/null
+++ b/src/ru/charm/back/controller/LoginController.java
@@ -0,0 +1,46 @@
+package ru.charm.back.controller;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import ru.charm.back.dto.LoginDto;
+import ru.charm.back.dto.ProfileGetDto;
+import ru.charm.back.dto.RegistrationDto;
+import ru.charm.back.mapper.RequestToLoginDtoMapper;
+import ru.charm.back.mapper.RequestToRegistrationDtoMapper;
+import ru.charm.back.service.ProfileService;
+import ru.charm.back.validator.RegistrationValidator;
+import ru.charm.back.validator.ValidationResult;
+
+import java.io.IOException;
+import java.util.Optional;
+
+@WebServlet("/login")
+@Slf4j
+public class LoginController extends HttpServlet {
+ private final ProfileService service = ProfileService.getInstance();
+
+ private final RequestToLoginDtoMapper requestTologinDtoMapper = RequestToLoginDtoMapper.getInstance();
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ req.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(req, resp);
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
+ LoginDto dto = requestTologinDtoMapper.map(req);
+ Optional userDetailOpt = service.login(dto);
+
+ if (userDetailOpt.isPresent()) {
+ req.getSession().setAttribute("userDetails", userDetailOpt.get());
+
+ resp.sendRedirect(String.format("/profile?id=%s", userDetailOpt.get().getId()));
+ return;
+ }
+ resp.sendRedirect("/login");
+ }
+}
diff --git a/src/ru/charm/back/controller/LogoutController.java b/src/ru/charm/back/controller/LogoutController.java
new file mode 100644
index 0000000..787c7d4
--- /dev/null
+++ b/src/ru/charm/back/controller/LogoutController.java
@@ -0,0 +1,24 @@
+package ru.charm.back.controller;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.annotation.WebServlet;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import ru.charm.back.dto.LoginDto;
+import ru.charm.back.dto.ProfileGetDto;
+import ru.charm.back.mapper.RequestToLoginDtoMapper;
+import ru.charm.back.service.ProfileService;
+
+import java.io.IOException;
+import java.util.Optional;
+
+@WebServlet("/logout")
+public class LogoutController extends HttpServlet {
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ req.getSession().invalidate();
+ resp.sendRedirect("/login");
+ }
+}
diff --git a/src/ru/charm/back/controller/filter/AuthFilter.java b/src/ru/charm/back/controller/filter/AuthFilter.java
new file mode 100644
index 0000000..ece1e18
--- /dev/null
+++ b/src/ru/charm/back/controller/filter/AuthFilter.java
@@ -0,0 +1,43 @@
+package ru.charm.back.controller.filter;
+
+import jakarta.servlet.*;
+import jakarta.servlet.annotation.WebFilter;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import ru.charm.back.dto.ProfileGetDto;
+import ru.charm.back.model.Role;
+import ru.charm.back.service.WordBundle;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Set;
+
+@WebFilter("/*")
+@Slf4j
+public class AuthFilter implements Filter {
+ private final Set PRIVATE_PATHS = Set.of("/profile", "/credentials");
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest req = (HttpServletRequest) servletRequest;
+ HttpServletResponse res = (HttpServletResponse) servletResponse;
+
+ if (PRIVATE_PATHS.contains(req.getRequestURI())) {
+ ProfileGetDto userDetails = (ProfileGetDto) req.getSession().getAttribute("userDetails");
+ log.info("User {} is trying to access {}", userDetails, req.getRequestURI());
+ if (userDetails == null) {
+ res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ } else if (userDetails.getRole() == Role.ADMIN || userDetails.getId().toString().equals( req.getParameter("id"))) {
+ filterChain.doFilter(req, res);
+ } else {
+ res.sendError(HttpServletResponse.SC_FORBIDDEN);
+ }
+
+ } else {
+ filterChain.doFilter(req, res);
+
+ }
+ }
+}
diff --git a/src/ru/charm/back/dao/ProfileDao.java b/src/ru/charm/back/dao/ProfileDao.java
index c74329b..5a31d63 100644
--- a/src/ru/charm/back/dao/ProfileDao.java
+++ b/src/ru/charm/back/dao/ProfileDao.java
@@ -2,6 +2,7 @@ package ru.charm.back.dao;
import ru.charm.back.model.Gender;
import ru.charm.back.model.Profile;
+import ru.charm.back.model.Role;
import ru.charm.back.model.Status;
import java.time.LocalDate;
@@ -32,6 +33,7 @@ public class ProfileDao {
profile.setAbout("I am QA");
profile.setGender(Gender.MALE);
profile.setStatus(Status.ACTIVE);
+ profile.setRole(Role.ADMIN);
this.storage.put(1L, profile);
Profile profile1 = new Profile();
profile1.setId(2L);
@@ -43,6 +45,7 @@ public class ProfileDao {
profile1.setAbout("I am Java Dev");
profile1.setGender(Gender.FEMALE);
profile1.setStatus(Status.INACTIVE);
+ profile1.setRole(Role.USER);
this.storage.put(2L, profile1);
this.idStorage = new AtomicLong(3L);
}
@@ -80,4 +83,10 @@ public class ProfileDao {
public Set getAllEmails() {
return storage.values().stream().map(Profile::getEmail).collect(Collectors.toSet());
}
+
+ public Optional findByEmail(String email) {
+ if (email == null) return Optional.empty();
+
+ return storage.values().stream().filter(profile -> profile.getEmail().equals(email)).findFirst();
+ }
}
diff --git a/src/ru/charm/back/dto/LoginDto.java b/src/ru/charm/back/dto/LoginDto.java
new file mode 100644
index 0000000..18ad387
--- /dev/null
+++ b/src/ru/charm/back/dto/LoginDto.java
@@ -0,0 +1,9 @@
+package ru.charm.back.dto;
+
+import lombok.Data;
+
+@Data
+public class LoginDto {
+ private String email;
+ private String password;
+}
diff --git a/src/ru/charm/back/dto/ProfileGetDto.java b/src/ru/charm/back/dto/ProfileGetDto.java
index 0faf33a..2fb1c1f 100644
--- a/src/ru/charm/back/dto/ProfileGetDto.java
+++ b/src/ru/charm/back/dto/ProfileGetDto.java
@@ -1,21 +1,26 @@
package ru.charm.back.dto;
+import lombok.AccessLevel;
import lombok.Data;
+import lombok.experimental.FieldDefaults;
import ru.charm.back.model.Gender;
+import ru.charm.back.model.Role;
import ru.charm.back.model.Status;
import java.time.LocalDate;
@Data
+@FieldDefaults(level = AccessLevel.PRIVATE)
public class ProfileGetDto {
- private Long id;
- private String email;
- private String name;
- private String surname;
- private LocalDate birthDate;
- private Integer age;
- private String about;
- private Gender gender;
- private Status status;
- private String photo;
+ Long id;
+ String email;
+ String name;
+ String surname;
+ LocalDate birthDate;
+ Integer age;
+ String about;
+ Gender gender;
+ Status status;
+ String photo;
+ Role role;
}
diff --git a/src/ru/charm/back/mapper/ProfileToProfileGetDtoMapper.java b/src/ru/charm/back/mapper/ProfileToProfileGetDtoMapper.java
index dab99a5..f51f55e 100644
--- a/src/ru/charm/back/mapper/ProfileToProfileGetDtoMapper.java
+++ b/src/ru/charm/back/mapper/ProfileToProfileGetDtoMapper.java
@@ -37,6 +37,7 @@ public class ProfileToProfileGetDtoMapper implements Mapper {
@@ -25,6 +26,7 @@ public class RegistrationDtoToProfileMapper implements Mapper {
+
+ private static final RequestToLoginDtoMapper INSTANCE = new RequestToLoginDtoMapper();
+
+ private RequestToLoginDtoMapper() {
+ }
+
+ public static RequestToLoginDtoMapper getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public LoginDto map(HttpServletRequest req) {
+ return map(req, new LoginDto());
+ }
+
+ @Override
+ public LoginDto map(HttpServletRequest req, LoginDto dto) {
+ dto.setEmail(req.getParameter("email"));
+ dto.setPassword(req.getParameter("password"));
+ return dto;
+ }
+}
diff --git a/src/ru/charm/back/model/Profile.java b/src/ru/charm/back/model/Profile.java
index 8beff43..e5cc6e5 100644
--- a/src/ru/charm/back/model/Profile.java
+++ b/src/ru/charm/back/model/Profile.java
@@ -17,4 +17,5 @@ public class Profile {
private Gender gender;
private Status status;
private String photo;
+ private Role role;
}
\ No newline at end of file
diff --git a/src/ru/charm/back/model/Role.java b/src/ru/charm/back/model/Role.java
new file mode 100644
index 0000000..738cbcc
--- /dev/null
+++ b/src/ru/charm/back/model/Role.java
@@ -0,0 +1,6 @@
+package ru.charm.back.model;
+
+public enum Role {
+ ADMIN,
+ USER
+}
diff --git a/src/ru/charm/back/service/ProfileService.java b/src/ru/charm/back/service/ProfileService.java
index f8bd206..c71568e 100644
--- a/src/ru/charm/back/service/ProfileService.java
+++ b/src/ru/charm/back/service/ProfileService.java
@@ -4,6 +4,7 @@ import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import ru.charm.back.dao.ProfileDao;
+import ru.charm.back.dto.LoginDto;
import ru.charm.back.dto.ProfileGetDto;
import ru.charm.back.dto.ProfileUpdateDto;
import ru.charm.back.dto.RegistrationDto;
@@ -66,4 +67,10 @@ public class ProfileService {
public boolean delete(Long id) {
return dao.delete(id);
}
+
+ public Optional login(LoginDto dto) {
+ return dao.findByEmail(dto.getEmail())
+ .filter(profile -> profile.getPassword().equals(dto.getPassword()))
+ .map(profileToProfileGetDtoMapper::map);
+ }
}
\ No newline at end of file