Spaces:
Runtime error
Runtime error
Upload persistent_user_manager.py
Browse files- persistent_user_manager.py +58 -52
persistent_user_manager.py
CHANGED
|
@@ -57,38 +57,45 @@ class PersistentUserManager:
|
|
| 57 |
logger.warning(f"フォールバック: ローカルディレクトリを使用 {self.user_data_dir}")
|
| 58 |
|
| 59 |
def _ensure_cookie_manager(self, force_init: bool = False):
|
| 60 |
-
"""Cookie
|
| 61 |
# 既に初期化済みで、強制初期化でない場合はスキップ
|
| 62 |
-
if self._cookie_initialized and not force_init:
|
| 63 |
return
|
| 64 |
|
| 65 |
# ブラウザセッション単位でのCookie初期化チェック
|
| 66 |
browser_session_key = f"cookie_initialized_{id(st.session_state)}"
|
| 67 |
-
if not force_init and st.session_state.get(browser_session_key, False):
|
| 68 |
self._cookie_initialized = True
|
| 69 |
return
|
| 70 |
|
| 71 |
try:
|
| 72 |
-
logger.info("永続Cookie
|
| 73 |
|
| 74 |
# セキュアなパスワードを生成(環境変数から取得、なければ生成)
|
| 75 |
cookie_password = os.getenv("MARI_COOKIE_PASSWORD", "mari_chat_secure_key_2024")
|
| 76 |
|
| 77 |
-
#
|
| 78 |
cookies = EncryptedCookieManager(
|
| 79 |
prefix="mari_",
|
| 80 |
-
password=cookie_password
|
| 81 |
-
# 永続化設定
|
| 82 |
-
expiry_days=self.cookie_expiry_days, # 30日間有効
|
| 83 |
-
# セキュリティ設定
|
| 84 |
-
key_len=32, # より強力な暗号化キー
|
| 85 |
-
# HTTPOnly設定(可能な場合)
|
| 86 |
)
|
| 87 |
|
| 88 |
-
# Cookie
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
if not cookies.ready():
|
| 90 |
-
logger.warning("
|
| 91 |
-
|
|
|
|
|
|
|
|
|
|
| 92 |
|
| 93 |
self.cookies = cookies
|
| 94 |
self._cookie_initialized = True
|
|
@@ -96,7 +103,9 @@ class PersistentUserManager:
|
|
| 96 |
# ブラウザセッション単位でフラグを設定
|
| 97 |
st.session_state[browser_session_key] = True
|
| 98 |
|
| 99 |
-
|
|
|
|
|
|
|
| 100 |
|
| 101 |
except Exception as e:
|
| 102 |
logger.error(f"Cookie管理初期化エラー: {e}")
|
|
@@ -129,14 +138,7 @@ class PersistentUserManager:
|
|
| 129 |
st.session_state.user_id_processing = True
|
| 130 |
|
| 131 |
try:
|
| 132 |
-
#
|
| 133 |
-
if not force_reset and 'user_id' in st.session_state:
|
| 134 |
-
existing_id = st.session_state.user_id
|
| 135 |
-
if existing_id and self._is_valid_uuid(existing_id):
|
| 136 |
-
logger.debug(f"セッション状態からユーザーID使用: {existing_id[:8]}...")
|
| 137 |
-
return existing_id
|
| 138 |
-
|
| 139 |
-
# Cookie管理システムを初期化(ブラウザごとに一度だけ)
|
| 140 |
self._ensure_cookie_manager(force_init=force_reset)
|
| 141 |
|
| 142 |
# 1. CookieからユーザーIDを取得(最優先)
|
|
@@ -146,9 +148,18 @@ class PersistentUserManager:
|
|
| 146 |
# 有効なCookieベースのユーザーIDが存在
|
| 147 |
self._update_user_access_time(user_id)
|
| 148 |
st.session_state.user_id = user_id # セッション状態に保存
|
| 149 |
-
logger.info(f"Cookie認証成功: {user_id[:8]}...")
|
| 150 |
return user_id
|
| 151 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
# 2. Cookieが無効または存在しない場合は新規作成
|
| 153 |
if force_reset or not user_id:
|
| 154 |
user_id = self._create_new_user_with_cookie()
|
|
@@ -183,7 +194,7 @@ class PersistentUserManager:
|
|
| 183 |
return temp_id
|
| 184 |
|
| 185 |
def _get_user_id_from_cookie(self) -> Optional[str]:
|
| 186 |
-
"""CookieからユーザーID
|
| 187 |
try:
|
| 188 |
# Cookie管理の初期化(初回のみ)
|
| 189 |
self._ensure_cookie_manager()
|
|
@@ -192,10 +203,25 @@ class PersistentUserManager:
|
|
| 192 |
logger.debug("Cookie管理システム無効 - None返却")
|
| 193 |
return None
|
| 194 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
user_id = self.cookies.get(self.cookie_name)
|
| 196 |
-
if user_id
|
| 197 |
-
logger.debug(f"Cookie
|
| 198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
|
| 200 |
logger.debug("Cookie内に有効なユーザーIDなし")
|
| 201 |
return None
|
|
@@ -205,7 +231,7 @@ class PersistentUserManager:
|
|
| 205 |
return None
|
| 206 |
|
| 207 |
def _set_user_id_cookie(self, user_id: str):
|
| 208 |
-
"""ユーザーID
|
| 209 |
try:
|
| 210 |
# Cookie管理システムが初期化されていない場合はスキップ
|
| 211 |
if not self._cookie_initialized:
|
|
@@ -216,33 +242,13 @@ class PersistentUserManager:
|
|
| 216 |
logger.debug("Cookie管理システム無効 - Cookie設定スキップ")
|
| 217 |
return
|
| 218 |
|
| 219 |
-
# 永続的なCookie
|
| 220 |
-
expiry_date = datetime.now() + timedelta(days=self.cookie_expiry_days)
|
| 221 |
-
|
| 222 |
-
# HTTPOnlyと永続化の設定
|
| 223 |
self.cookies[self.cookie_name] = user_id
|
| 224 |
|
| 225 |
-
#
|
| 226 |
-
# streamlit-cookies-managerは内部的にHTTPOnlyを設定するが、
|
| 227 |
-
# より確実にするために明示的に設定
|
| 228 |
-
try:
|
| 229 |
-
# Cookieの詳細設定(可能な場合)
|
| 230 |
-
if hasattr(self.cookies, '_set_cookie_attributes'):
|
| 231 |
-
self.cookies._set_cookie_attributes(
|
| 232 |
-
name=self.cookie_name,
|
| 233 |
-
value=user_id,
|
| 234 |
-
expires=expiry_date,
|
| 235 |
-
httponly=True,
|
| 236 |
-
secure=True, # HTTPS環境では必須
|
| 237 |
-
samesite='Lax' # CSRF攻撃を防ぐ
|
| 238 |
-
)
|
| 239 |
-
except Exception as attr_e:
|
| 240 |
-
logger.debug(f"Cookie属性設定エラー(通常動作に影響なし): {attr_e}")
|
| 241 |
-
|
| 242 |
-
# Cookieを保存
|
| 243 |
self.cookies.save()
|
| 244 |
|
| 245 |
-
logger.info(f"
|
| 246 |
|
| 247 |
except Exception as e:
|
| 248 |
logger.warning(f"永続Cookie設定エラー: {e}")
|
|
|
|
| 57 |
logger.warning(f"フォールバック: ローカルディレクトリを使用 {self.user_data_dir}")
|
| 58 |
|
| 59 |
def _ensure_cookie_manager(self, force_init: bool = False):
|
| 60 |
+
"""Cookie管理システムの初期化(永続Cookie対応)"""
|
| 61 |
# 既に初期化済みで、強制初期化でない場合はスキップ
|
| 62 |
+
if self._cookie_initialized and not force_init and self.cookies is not None:
|
| 63 |
return
|
| 64 |
|
| 65 |
# ブラウザセッション単位でのCookie初期化チェック
|
| 66 |
browser_session_key = f"cookie_initialized_{id(st.session_state)}"
|
| 67 |
+
if not force_init and st.session_state.get(browser_session_key, False) and self.cookies is not None:
|
| 68 |
self._cookie_initialized = True
|
| 69 |
return
|
| 70 |
|
| 71 |
try:
|
| 72 |
+
logger.info("永続Cookie管理システム初期化開始...")
|
| 73 |
|
| 74 |
# セキュアなパスワードを生成(環境変数から取得、なければ生成)
|
| 75 |
cookie_password = os.getenv("MARI_COOKIE_PASSWORD", "mari_chat_secure_key_2024")
|
| 76 |
|
| 77 |
+
# EncryptedCookieManagerを初期化(永続Cookie対応)
|
| 78 |
cookies = EncryptedCookieManager(
|
| 79 |
prefix="mari_",
|
| 80 |
+
password=cookie_password
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
)
|
| 82 |
|
| 83 |
+
# Cookieが準備できるまで待機(最大3回試行)
|
| 84 |
+
max_attempts = 3
|
| 85 |
+
for attempt in range(max_attempts):
|
| 86 |
+
if cookies.ready():
|
| 87 |
+
break
|
| 88 |
+
logger.debug(f"Cookie準備待機中... ({attempt + 1}/{max_attempts})")
|
| 89 |
+
if attempt < max_attempts - 1:
|
| 90 |
+
import time
|
| 91 |
+
time.sleep(0.1)
|
| 92 |
+
|
| 93 |
if not cookies.ready():
|
| 94 |
+
logger.warning("Cookie準備タイムアウト - フォールバックモード")
|
| 95 |
+
self.cookies = None
|
| 96 |
+
self._cookie_initialized = True
|
| 97 |
+
st.session_state[browser_session_key] = True
|
| 98 |
+
return
|
| 99 |
|
| 100 |
self.cookies = cookies
|
| 101 |
self._cookie_initialized = True
|
|
|
|
| 103 |
# ブラウザセッション単位でフラグを設定
|
| 104 |
st.session_state[browser_session_key] = True
|
| 105 |
|
| 106 |
+
# 既存Cookieの確認
|
| 107 |
+
existing_cookies = dict(self.cookies)
|
| 108 |
+
logger.info(f"永続Cookie管理システム初期化完了 - 既存Cookie数: {len(existing_cookies)}")
|
| 109 |
|
| 110 |
except Exception as e:
|
| 111 |
logger.error(f"Cookie管理初期化エラー: {e}")
|
|
|
|
| 138 |
st.session_state.user_id_processing = True
|
| 139 |
|
| 140 |
try:
|
| 141 |
+
# Cookie管理システムを初期化(最優先)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
self._ensure_cookie_manager(force_init=force_reset)
|
| 143 |
|
| 144 |
# 1. CookieからユーザーIDを取得(最優先)
|
|
|
|
| 148 |
# 有効なCookieベースのユーザーIDが存在
|
| 149 |
self._update_user_access_time(user_id)
|
| 150 |
st.session_state.user_id = user_id # セッション状態に保存
|
| 151 |
+
logger.info(f"永続Cookie認証成功: {user_id[:8]}...")
|
| 152 |
return user_id
|
| 153 |
|
| 154 |
+
# 2. フルリセット時以外で、セッション状態にuser_idがある場合はCookieと照合
|
| 155 |
+
if not force_reset and 'user_id' in st.session_state:
|
| 156 |
+
existing_id = st.session_state.user_id
|
| 157 |
+
if existing_id and self._is_valid_uuid(existing_id) and self._is_valid_user_id(existing_id):
|
| 158 |
+
# セッション状態のIDが有効な場合、Cookieにも保存
|
| 159 |
+
self._set_user_id_cookie(existing_id)
|
| 160 |
+
logger.info(f"セッション状態からユーザーID復元: {existing_id[:8]}...")
|
| 161 |
+
return existing_id
|
| 162 |
+
|
| 163 |
# 2. Cookieが無効または存在しない場合は新規作成
|
| 164 |
if force_reset or not user_id:
|
| 165 |
user_id = self._create_new_user_with_cookie()
|
|
|
|
| 194 |
return temp_id
|
| 195 |
|
| 196 |
def _get_user_id_from_cookie(self) -> Optional[str]:
|
| 197 |
+
"""CookieからユーザーIDを取得(永続Cookie対応)"""
|
| 198 |
try:
|
| 199 |
# Cookie管理の初期化(初回のみ)
|
| 200 |
self._ensure_cookie_manager()
|
|
|
|
| 203 |
logger.debug("Cookie管理システム無効 - None返却")
|
| 204 |
return None
|
| 205 |
|
| 206 |
+
# Cookieが準備完了するまで待機
|
| 207 |
+
if not self.cookies.ready():
|
| 208 |
+
logger.debug("Cookie準備中 - 待機")
|
| 209 |
+
return None
|
| 210 |
+
|
| 211 |
+
# 全てのCookieを確認(デバッグ用)
|
| 212 |
+
all_cookies = dict(self.cookies)
|
| 213 |
+
logger.debug(f"利用可能なCookie: {list(all_cookies.keys())}")
|
| 214 |
+
|
| 215 |
user_id = self.cookies.get(self.cookie_name)
|
| 216 |
+
if user_id:
|
| 217 |
+
logger.debug(f"Cookie値取得: {user_id[:8] if len(user_id) >= 8 else user_id}...")
|
| 218 |
+
|
| 219 |
+
if self._is_valid_uuid(user_id):
|
| 220 |
+
logger.info(f"永続CookieからユーザーID取得成功: {user_id[:8]}...")
|
| 221 |
+
return user_id
|
| 222 |
+
else:
|
| 223 |
+
logger.warning(f"Cookie内のユーザーIDが無効な形式: {user_id}")
|
| 224 |
+
return None
|
| 225 |
|
| 226 |
logger.debug("Cookie内に有効なユーザーIDなし")
|
| 227 |
return None
|
|
|
|
| 231 |
return None
|
| 232 |
|
| 233 |
def _set_user_id_cookie(self, user_id: str):
|
| 234 |
+
"""ユーザーIDを永続的なクッキーに設定"""
|
| 235 |
try:
|
| 236 |
# Cookie管理システムが初期化されていない場合はスキップ
|
| 237 |
if not self._cookie_initialized:
|
|
|
|
| 242 |
logger.debug("Cookie管理システム無効 - Cookie設定スキップ")
|
| 243 |
return
|
| 244 |
|
| 245 |
+
# 永続的なCookieを設定
|
|
|
|
|
|
|
|
|
|
| 246 |
self.cookies[self.cookie_name] = user_id
|
| 247 |
|
| 248 |
+
# Cookieを保存(streamlit-cookies-managerは自動的に永続化される)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
self.cookies.save()
|
| 250 |
|
| 251 |
+
logger.info(f"永続クッキーに保存: {user_id[:8]}... (ブラウザ閉じても保持)")
|
| 252 |
|
| 253 |
except Exception as e:
|
| 254 |
logger.warning(f"永続Cookie設定エラー: {e}")
|