Files
reactive-mail-server/backend/src/main/java/com/mailserver/service/UserService.java
T
Liosha84andClaude Opus 5.5 2149c29a32
continuous-integration/drone/pr Build is passing
Ящики из веб-почты: администратор создаёт no-reply@, monitor@, support@, задаёт новый пароль, выключает
Регистрация остаётся закрытой. Администраторы — MAIL_ADMINS (по умолчанию первый ящик);
роль не в базе, укравший ящик себя админом не сделает. API /api/admin/mailboxes,
сгенерированный пароль — один раз в ответе, в лог не пишется. Настройки → MAILBOXES
(видно только администратору). Тесты: MailboxAdminControllerTest (5), всего 73.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-07 01:23:48 +01:00

101 lines
3.9 KiB
Java

package com.mailserver.service;
import com.mailserver.domain.Folder;
import com.mailserver.domain.User;
import com.mailserver.repository.FolderRepository;
import com.mailserver.repository.UserRepository;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Instant;
@Service
public class UserService {
private final UserRepository userRepository;
private final FolderRepository folderRepository;
private final BCryptPasswordEncoder passwordEncoder;
public UserService(UserRepository userRepository, FolderRepository folderRepository) {
this.userRepository = userRepository;
this.folderRepository = folderRepository;
this.passwordEncoder = new BCryptPasswordEncoder();
}
public Mono<User> register(String email, String password, String displayName) {
return userRepository.existsByEmail(email)
.flatMap(exists -> {
if (exists) {
return Mono.error(new IllegalArgumentException("Email already registered: " + email));
}
User user = new User();
user.setEmail(email);
user.setPassword(passwordEncoder.encode(password));
user.setDisplayName(displayName == null || displayName.isBlank() ? email.substring(0, email.indexOf('@')) : displayName.trim());
user.setCreatedAt(Instant.now());
return userRepository.save(user);
})
.flatMap(user -> {
// Создаём стандартные папки
return Flux.fromIterable(MailService.SYSTEM_FOLDERS)
.concatMap(name -> {
Folder folder = new Folder();
folder.setUserId(user.getId());
folder.setName(name);
folder.setSystem(true);
folder.setCreatedAt(Instant.now());
return folderRepository.save(folder);
})
.then(Mono.just(user));
});
}
public Mono<User> findByEmail(String email) {
return userRepository.findByEmail(email)
.switchIfEmpty(Mono.error(new IllegalArgumentException("User not found: " + email)));
}
/**
* Проверяет, совпадает ли пароль с хэшем, хранящимся у пользователя.
*/
public boolean checkPassword(User user, String rawPassword) {
return passwordEncoder.matches(rawPassword, user.getPassword());
}
/** New password, only with the current one. */
public Mono<Void> changePassword(User user, String currentPassword, String newPassword) {
if (!checkPassword(user, currentPassword)) {
return Mono.error(MailService.badRequest("Current password is wrong"));
}
user.setPassword(passwordEncoder.encode(newPassword));
return userRepository.save(user).then();
}
public Flux<User> all() {
return userRepository.findAll();
}
public Mono<User> byId(Long id) {
return userRepository.findById(id);
}
/** Administrator sets a new password (Settings → Mailboxes): no current password needed. */
public Mono<User> setPassword(User user, String newPassword) {
user.setPassword(passwordEncoder.encode(newPassword));
return userRepository.save(user);
}
public Mono<User> setActive(User user, boolean active) {
user.setActive(active);
return userRepository.save(user);
}
public Mono<User> updateDisplayName(User user, String displayName) {
user.setDisplayName(displayName.trim());
return userRepository.save(user);
}
}