feat(vmis): 租戶自動開通完整流程 + Admin Portal SSO + NC 行事曆訂閱

Backend:
- schedule_tenant: NC 新容器自動 pgsql 安裝 (_nc_db_check 全新容器處理)
- schedule_tenant: NC 初始化加入 Redis + APCu memcache 設定 (修正 OIDC invalid_state)
- schedule_tenant: 新租戶 KC realm 自動設定 accessCodeLifespan=600s (修正 authentication_expired)
- schedule_account: NC Mail 帳號自動設定 (nc_mail_result/nc_mail_done_at)
- schedule_account: NC 台灣國定假日行事曆自動訂閱 (CalDAV MKCALENDAR)
- nextcloud_client: 新增 subscribe_calendar() CalDAV 訂閱方法
- settings: 新增系統設定 API (site_title/version/timezone/SSO/Keycloak)
- models/result: 新增 nc_mail_result, nc_mail_done_at 欄位
- alembic: 遷移 002(system_settings) 003(keycloak_admin) 004(nc_mail_result)

Frontend (Admin Portal):
- 新增完整管理後台 (index/tenants/accounts/servers/schedules/logs/settings/system-status)
- api.js: Keycloak JS Adapter SSO 整合 (PKCE/S256, fallback KC JS 來源, 自動 token 更新)
- index.html: Promise.allSettled 取代 Promise.all,防止單一 API 失敗影響整頁
- 所有頁面加入 try/catch + toast 錯誤處理
- 新增品牌 LOGO 與 favicon

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
VMIS Developer
2026-03-15 15:31:37 +08:00
parent 42d1420f9c
commit 62baadb06f
53 changed files with 5638 additions and 195 deletions

View File

@@ -0,0 +1,254 @@
# WebMail Keycloak SSO 整合指南
本指南說明如何將 Roundcube WebMail 與 Keycloak SSO 整合。
## 前置條件
已完成:
- ✅ Keycloak Client 已建立
- ✅ Client ID: `webmail`
- ✅ Client Secret: `CDUcB68SZUEZ2zmiQV4cz22czjw6Sn1q`
- ✅ Realm: `vmis-admin`
## 步驟 1: 連接到 WebMail 主機
```bash
ssh 10.1.0.254
```
## 步驟 2: 查看 Roundcube 容器
```bash
docker ps | grep -i roundcube
```
記錄容器名稱 (例如: `roundcube``webmail`)
## 步驟 3: 查看 Roundcube 配置掛載路徑
```bash
docker inspect <容器名稱> | grep -A 5 "Mounts"
```
找出配置檔案的掛載路徑 (通常是 `/var/www/html/config` 或類似路徑)
## 步驟 4: 安裝 OAuth2 插件
### 方法 A: 如果 Roundcube 使用官方 Docker 映像
1. 進入容器:
```bash
docker exec -it <容器名稱> bash
```
2. 使用 Composer 安裝 OAuth2 插件:
```bash
cd /var/www/html
composer require roundcube/oauth2
```
3. 啟用插件 (編輯 config/config.inc.php):
```bash
vi config/config.inc.php
```
找到 `$config['plugins']` 行,加入 `'oauth2'`:
```php
$config['plugins'] = array(
'oauth2',
// ... 其他插件
);
```
### 方法 B: 如果無法使用 Composer
手動下載並安裝插件:
```bash
cd /var/www/html/plugins
git clone https://github.com/roundcube/roundcubemail-oauth2.git oauth2
```
## 步驟 5: 配置 OAuth2 插件
建立或編輯 `config/oauth2.inc.php`:
```bash
vi /var/www/html/config/oauth2.inc.php
```
加入以下內容:
```php
<?php
/**
* Roundcube OAuth2 Configuration
* Keycloak SSO Integration for WebMail
*/
$config['oauth2'] = array(
'provider' => 'generic',
'client_id' => 'webmail',
'client_secret' => 'CDUcB68SZUEZ2zmiQV4cz22czjw6Sn1q',
'auth_uri' => 'https://auth.lab.taipei/realms/vmis-admin/protocol/openid-connect/auth',
'token_uri' => 'https://auth.lab.taipei/realms/vmis-admin/protocol/openid-connect/token',
'identity_uri' => 'https://auth.lab.taipei/realms/vmis-admin/protocol/openid-connect/userinfo',
'redirect_uri' => 'https://webmail.porscheworld.tw/index.php/login/oauth',
'scope' => 'openid email profile',
'login_redirect' => true,
'username_field' => 'email',
'identity_fields' => array(
'email' => 'email',
'name' => 'name',
'username' => 'preferred_username'
),
);
// 自動登入 (可選)
$config['oauth2_auto_login'] = true;
// 允許傳統登入 (開發階段建議保留)
$config['oauth2_allow_traditional_login'] = true;
```
## 步驟 6: 修改 Roundcube 主配置
編輯 `config/config.inc.php`:
```bash
vi /var/www/html/config/config.inc.php
```
確保以下設定:
```php
// 啟用插件
$config['plugins'] = array(
'oauth2',
// ... 其他插件
);
// IMAP 設定 (根據實際郵件伺服器調整)
$config['default_host'] = 'ssl://10.1.0.254';
$config['default_port'] = 993;
// SMTP 設定
$config['smtp_server'] = 'tls://10.1.0.254';
$config['smtp_port'] = 587;
// 使用者名稱網域 (根據 Keycloak 使用者的 email 設定)
$config['username_domain'] = array(
'porscheworld.tw' => 'porscheworld.tw',
'lab.taipei' => 'lab.taipei',
'ease.taipei' => 'ease.taipei',
);
// 自動完成郵件地址
$config['mail_domain'] = 'lab.taipei';
```
## 步驟 7: 設定郵件地址對應規則
Keycloak 使用者的 `email` 屬性應該對應到郵件伺服器的帳號。
### 確保 Keycloak 使用者有正確的 email
1. 登入 Keycloak Admin Console:
```
https://auth.lab.taipei/admin
```
2. 進入 `vmis-admin` Realm
3. 檢查使用者的 Email 欄位,例如:
- Username: `sysadmin`
- Email: `admin@lab.taipei`
這個 Email 必須是郵件伺服器上實際存在的帳號。
## 步驟 8: 重啟 Roundcube 容器
```bash
docker restart <容器名稱>
```
## 步驟 9: 測試 SSO 登入
1. 清除瀏覽器 Cookie
2. 訪問 WebMail:
```
https://webmail.porscheworld.tw
```
3. 應該會自動重導向到 Keycloak 登入頁面:
```
https://auth.lab.taipei/realms/vmis-admin/protocol/openid-connect/auth?...
```
4. 使用 Keycloak 帳號登入 (例如: `sysadmin`)
5. 登入成功後,應該會重導向回 WebMail 並自動登入郵件帳號
## 故障排除
### 問題 1: 無法重導向到 Keycloak
**檢查:**
- OAuth2 插件是否正確安裝
- `config/oauth2.inc.php` 是否存在且設定正確
- Roundcube 容器日誌: `docker logs <容器名稱>`
### 問題 2: 登入後顯示「Invalid credentials」
**可能原因:**
- Keycloak 使用者的 email 與郵件伺服器帳號不符
- 郵件伺服器密碼與 Keycloak 密碼不同
**解決方案:**
需要實作「密碼同步」或使用「IMAP OAuth2」:
1. **方案 A: 密碼同步** - Keycloak 密碼變更時同步到郵件伺服器
2. **方案 B: IMAP OAuth2** - 郵件伺服器支援 OAuth2 認證 (需要 Docker Mailserver 配置)
### 問題 3: 重導向 URI 不符
**錯誤訊息:**
```
Invalid redirect_uri
```
**解決方案:**
檢查 Keycloak Client 的 Valid Redirect URIs 設定:
```
https://webmail.porscheworld.tw/*
```
## 進階設定
### 整合 HR Portal 多帳號切換
如果要實現「員工可以使用 SSO 登入後,切換不同的郵件帳號」:
1. 安裝 `multi_accounts` 插件
2. 配置 API 端點連接 HR Portal
3. 從 HR Portal 取得員工授權的郵件帳號列表
詳見: [郵件系統設計文件](P:\porscheworld\2.專案設計區\3.MailSystem\郵件系統設計文件.md)
## 相關連結
- Keycloak Admin: https://auth.lab.taipei/admin
- WebMail: https://webmail.porscheworld.tw
- Roundcube OAuth2 Plugin: https://github.com/roundcube/roundcubemail-oauth2
## 完成檢查清單
- [ ] OAuth2 插件已安裝
- [ ] `config/oauth2.inc.php` 設定完成
- [ ] `config/config.inc.php` 啟用插件
- [ ] Keycloak 使用者 email 正確設定
- [ ] Roundcube 容器已重啟
- [ ] SSO 登入測試成功
- [ ] 郵件收發測試成功