Вход через Jambotron (OAuth 2.0 code + PKCE), пароли приложений, системные ящики
continuous-integration/drone/pr Build is passing

- /api/oauth/start → JAMBOTRON_URL/oauth/authorize (client mail, PKCE S256);
  /api/oauth/callback: код меняется на сервере у Jambotron /api/oauth/token
  (Bearer SERVICE_TOKEN) → рабочий ящик сотрудника → сессия, ADMIN Jambotron — ADMIN.
- Вход по паролю в веб — только встроенный администратор (когда вход через Jambotron включён).
- Пароли приложений (V9 app_passwords): User panel → APP PASSWORDS, только SMTP/IMAP.
- Системные ящики (V9 users.is_system): без входа в веб, открывают встроенные
  администраторы, не в USERS; Admin panel → MAILBOXES → кнопка SYSTEM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-07 20:57:30 +01:00
co-authored by Claude Opus 5.5
parent 910a028612
commit 99e12d33f3
26 changed files with 665 additions and 19 deletions
@@ -30,15 +30,18 @@ public class MailboxAccess {
private final UserRepository users;
private final UserService userService;
public MailboxAccess(DatabaseClient db, UserRepository users, UserService userService) {
private final AppPasswords appPasswords;
public MailboxAccess(DatabaseClient db, UserRepository users, UserService userService, AppPasswords appPasswords) {
this.appPasswords = appPasswords;
this.db = db;
this.users = users;
this.userService = userService;
}
/**
* Login for SMTP / IMAP: {@code login} or {@code mailbox*login} + the login's password → the mailbox the
* session works with, or null. A shared mailbox never logs in by itself.
* Login for SMTP / IMAP: {@code login} or {@code mailbox*login} + the login's password or one of its app
* passwords (V9) → the mailbox the session works with, or null. A shared mailbox never logs in by itself.
*/
public User login(String username, String password) {
String name = username.trim().toLowerCase(Locale.ROOT);
@@ -46,7 +49,8 @@ public class MailboxAccess {
String mailbox = star < 0 ? name : name.substring(0, star);
String login = star < 0 ? name : name.substring(star + 1);
User user = users.findByEmail(login).block();
if (user == null || !user.isActive() || user.isShared() || !userService.checkPassword(user, password)) {
if (user == null || !user.isActive() || user.isShared()
|| !(userService.checkPassword(user, password) || appPasswords.matches(user.getId(), password))) {
return null;
}
if (mailbox.equals(login)) {