diff --git a/src/main/java/mate/academy/Main.java b/src/main/java/mate/academy/Main.java index df492aecc..7f5f81997 100644 --- a/src/main/java/mate/academy/Main.java +++ b/src/main/java/mate/academy/Main.java @@ -2,16 +2,22 @@ import java.time.LocalDate; import java.time.LocalDateTime; +import mate.academy.exception.AuthenticationException; +import mate.academy.exception.RegistrationException; +import mate.academy.lib.Injector; import mate.academy.model.CinemaHall; import mate.academy.model.Movie; import mate.academy.model.MovieSession; +import mate.academy.model.User; +import mate.academy.service.AuthenticationService; import mate.academy.service.CinemaHallService; import mate.academy.service.MovieService; import mate.academy.service.MovieSessionService; public class Main { - public static void main(String[] args) { - MovieService movieService = null; + public static void main(String[] args) throws RegistrationException, AuthenticationException { + Injector injector = Injector.getInstance("mate.academy"); + MovieService movieService = (MovieService) injector.getInstance(MovieService.class); Movie fastAndFurious = new Movie("Fast and Furious"); fastAndFurious.setDescription("An action film about street racing, heists, and spies."); @@ -27,7 +33,8 @@ public static void main(String[] args) { secondCinemaHall.setCapacity(200); secondCinemaHall.setDescription("second hall with capacity 200"); - CinemaHallService cinemaHallService = null; + CinemaHallService cinemaHallService = (CinemaHallService) injector + .getInstance(CinemaHallService.class); cinemaHallService.add(firstCinemaHall); cinemaHallService.add(secondCinemaHall); @@ -44,12 +51,24 @@ public static void main(String[] args) { yesterdayMovieSession.setMovie(fastAndFurious); yesterdayMovieSession.setShowTime(LocalDateTime.now().minusDays(1L)); - MovieSessionService movieSessionService = null; + MovieSessionService movieSessionService = (MovieSessionService) injector + .getInstance(MovieSessionService.class); movieSessionService.add(tomorrowMovieSession); movieSessionService.add(yesterdayMovieSession); System.out.println(movieSessionService.get(yesterdayMovieSession.getId())); System.out.println(movieSessionService.findAvailableSessions( fastAndFurious.getId(), LocalDate.now())); + + AuthenticationService authenticationService = (AuthenticationService) injector + .getInstance(AuthenticationService.class); + User david = new User(); + david.setEmail("david@gmail.com"); + david.setPassword("123456789"); + User registeredUser = authenticationService.register(david.getEmail(), david.getPassword()); + System.out.println("Successful registration: " + registeredUser); + User loggedUser = authenticationService.login(david.getEmail(), + david.getPassword()); + System.out.println("Successful login : " + loggedUser); } } diff --git a/src/main/java/mate/academy/dao/UserDao.java b/src/main/java/mate/academy/dao/UserDao.java new file mode 100644 index 000000000..793bcf191 --- /dev/null +++ b/src/main/java/mate/academy/dao/UserDao.java @@ -0,0 +1,12 @@ +package mate.academy.dao; + +import java.util.Optional; +import mate.academy.model.User; + +public interface UserDao { + User add(User user); + + Optional get(Long id); + + public Optional findByEmail(String email); +} diff --git a/src/main/java/mate/academy/dao/impl/UserDaoImpl.java b/src/main/java/mate/academy/dao/impl/UserDaoImpl.java new file mode 100644 index 000000000..6c6320148 --- /dev/null +++ b/src/main/java/mate/academy/dao/impl/UserDaoImpl.java @@ -0,0 +1,55 @@ +package mate.academy.dao.impl; + +import java.util.Optional; +import mate.academy.dao.UserDao; +import mate.academy.exception.DataProcessingException; +import mate.academy.lib.Dao; +import mate.academy.model.User; +import mate.academy.util.HibernateUtil; +import org.hibernate.Session; +import org.hibernate.Transaction; + +@Dao +public class UserDaoImpl implements UserDao { + @Override + public User add(User user) { + Transaction transaction = null; + Session session = null; + try { + session = HibernateUtil.getSessionFactory().openSession(); + transaction = session.beginTransaction(); + session.persist(user); + transaction.commit(); + return user; + } catch (Exception e) { + if (transaction != null) { + transaction.rollback(); + } + throw new DataProcessingException("Can't insert a user " + user, e); + } finally { + if (session != null) { + session.close(); + } + } + } + + @Override + public Optional get(Long id) { + try (Session session = HibernateUtil.getSessionFactory().openSession()) { + return Optional.ofNullable(session.get(User.class, id)); + } catch (Exception e) { + throw new DataProcessingException("Can't get a user by id: " + id, e); + } + } + + @Override + public Optional findByEmail(String email) { + try (Session session = HibernateUtil.getSessionFactory().openSession()) { + return session.createQuery("FROM User u where u.email = :email", User.class) + .setParameter("email", email) + .uniqueResultOptional(); + } catch (Exception e) { + throw new DataProcessingException("Can't find user by email " + email, e); + } + } +} diff --git a/src/main/java/mate/academy/exception/AuthenticationException.java b/src/main/java/mate/academy/exception/AuthenticationException.java new file mode 100644 index 000000000..b92eb133b --- /dev/null +++ b/src/main/java/mate/academy/exception/AuthenticationException.java @@ -0,0 +1,7 @@ +package mate.academy.exception; + +public class AuthenticationException extends Exception { + public AuthenticationException(String message) { + super(message); + } +} diff --git a/src/main/java/mate/academy/exception/RegistrationException.java b/src/main/java/mate/academy/exception/RegistrationException.java new file mode 100644 index 000000000..524a167cc --- /dev/null +++ b/src/main/java/mate/academy/exception/RegistrationException.java @@ -0,0 +1,7 @@ +package mate.academy.exception; + +public class RegistrationException extends Exception { + public RegistrationException(String message) { + super(message); + } +} diff --git a/src/main/java/mate/academy/model/User.java b/src/main/java/mate/academy/model/User.java new file mode 100644 index 000000000..31a7d637d --- /dev/null +++ b/src/main/java/mate/academy/model/User.java @@ -0,0 +1,58 @@ +package mate.academy.model; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +@Entity +@Table(name = "users") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String email; + private String password; + private byte[] salt; + + public Long getId() { + return id; + } + + public String getEmail() { + return email; + } + + public String getPassword() { + return password; + } + + public byte[] getSalt() { + return salt; + } + + public void setId(Long id) { + this.id = id; + } + + public void setEmail(String login) { + this.email = login; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setSalt(byte[] salt) { + this.salt = salt; + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", login='" + email + '\'' + + '}'; + } +} diff --git a/src/main/java/mate/academy/service/AuthenticationService.java b/src/main/java/mate/academy/service/AuthenticationService.java new file mode 100644 index 000000000..93dc54926 --- /dev/null +++ b/src/main/java/mate/academy/service/AuthenticationService.java @@ -0,0 +1,11 @@ +package mate.academy.service; + +import mate.academy.exception.AuthenticationException; +import mate.academy.exception.RegistrationException; +import mate.academy.model.User; + +public interface AuthenticationService { + User login(String email, String password) throws AuthenticationException; + + User register(String email, String password) throws RegistrationException; +} diff --git a/src/main/java/mate/academy/service/UserService.java b/src/main/java/mate/academy/service/UserService.java new file mode 100644 index 000000000..42a6361a7 --- /dev/null +++ b/src/main/java/mate/academy/service/UserService.java @@ -0,0 +1,10 @@ +package mate.academy.service; + +import java.util.Optional; +import mate.academy.model.User; + +public interface UserService { + User add(User user); + + Optional findByEmail(String email); +} diff --git a/src/main/java/mate/academy/service/impl/AuthenticationServiceImpl.java b/src/main/java/mate/academy/service/impl/AuthenticationServiceImpl.java new file mode 100644 index 000000000..220b79489 --- /dev/null +++ b/src/main/java/mate/academy/service/impl/AuthenticationServiceImpl.java @@ -0,0 +1,42 @@ +package mate.academy.service.impl; + +import java.util.Optional; +import mate.academy.exception.AuthenticationException; +import mate.academy.exception.RegistrationException; +import mate.academy.lib.Inject; +import mate.academy.lib.Service; +import mate.academy.model.User; +import mate.academy.service.AuthenticationService; +import mate.academy.service.UserService; +import mate.academy.util.HashUtil; + +@Service +public class AuthenticationServiceImpl implements AuthenticationService { + @Inject + private UserService userService; + + @Override + public User login(String email, String password) throws AuthenticationException { + Optional userFromDbOptional = userService.findByEmail(email); + if (userFromDbOptional.isEmpty() + || (!userFromDbOptional.get().getPassword().equals( + HashUtil.hashPassword(password, userFromDbOptional.get().getSalt())))) { + throw new AuthenticationException("Can't authenticate user"); + } + return userFromDbOptional.get(); + } + + @Override + public User register(String email, String password) throws RegistrationException { + if (userService.findByEmail(email).isPresent()) { + throw new RegistrationException("User with email " + email + " already exists"); + } + if (email.isEmpty() || password.isEmpty()) { + throw new RegistrationException("Email and password can't be empty"); + } + User user = new User(); + user.setEmail(email); + user.setPassword(password); + return userService.add(user); + } +} diff --git a/src/main/java/mate/academy/service/impl/UserServiceImpl.java b/src/main/java/mate/academy/service/impl/UserServiceImpl.java new file mode 100644 index 000000000..054f65e42 --- /dev/null +++ b/src/main/java/mate/academy/service/impl/UserServiceImpl.java @@ -0,0 +1,27 @@ +package mate.academy.service.impl; + +import java.util.Optional; +import mate.academy.dao.UserDao; +import mate.academy.lib.Inject; +import mate.academy.lib.Service; +import mate.academy.model.User; +import mate.academy.service.UserService; +import mate.academy.util.HashUtil; + +@Service +public class UserServiceImpl implements UserService { + @Inject + private UserDao userDao; + + @Override + public User add(User user) { + user.setSalt(HashUtil.getSalt()); + user.setPassword(HashUtil.hashPassword(user.getPassword(), user.getSalt())); + return userDao.add(user); + } + + @Override + public Optional findByEmail(String email) { + return userDao.findByEmail(email); + } +} diff --git a/src/main/java/mate/academy/util/HashUtil.java b/src/main/java/mate/academy/util/HashUtil.java new file mode 100644 index 000000000..bac7d4832 --- /dev/null +++ b/src/main/java/mate/academy/util/HashUtil.java @@ -0,0 +1,34 @@ +package mate.academy.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +public class HashUtil { + private static final String CRYPTO_ALGORITHM = "SHA-512"; + + private HashUtil() { + } + + public static byte[] getSalt() { + SecureRandom secureRandom = new SecureRandom(); + byte[] salt = new byte[16]; + secureRandom.nextBytes(salt); + return salt; + } + + public static String hashPassword(String password, byte[] salt) { + StringBuilder hashPassword = new StringBuilder(); + try { + MessageDigest messageDigest = MessageDigest.getInstance(CRYPTO_ALGORITHM); + messageDigest.update(salt); + byte[] digest = messageDigest.digest(password.getBytes()); + for (byte b : digest) { + hashPassword.append(String.format("%02x", b)); + } + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("Could not create hash using SHA-512 algorithm", e); + } + return hashPassword.toString(); + } +} diff --git a/src/main/resources/hibernate.cfg.xml b/src/main/resources/hibernate.cfg.xml new file mode 100644 index 000000000..11aa907df --- /dev/null +++ b/src/main/resources/hibernate.cfg.xml @@ -0,0 +1,21 @@ + + + + + + org.hibernate.dialect.MySQL8Dialect + jdbc:mysql://localhost/hib_movies?serverTimezone=UTC + com.mysql.cj.jdbc.Driver + root + Olga8267davyd)) + true + update + + + + + + + \ No newline at end of file