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("email")}

${wordBundle.getWord("password")}

+ + + + + +
+ + + +
+
+
+ + ${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