diff --git a/ecs/jskult-webapp/src/controller/bio.py b/ecs/jskult-webapp/src/controller/bio.py index 108fc747..ed9d1931 100644 --- a/ecs/jskult-webapp/src/controller/bio.py +++ b/ecs/jskult-webapp/src/controller/bio.py @@ -11,7 +11,6 @@ from src.model.view.bio_view_model import BioViewModel from src.router.session_router import AuthenticatedRoute from src.services.batch_status_service import BatchStatusService from src.services.bio_view_service import BioViewService -from src.services.session_service import set_session from src.system_var import constants from src.templates import templates @@ -38,14 +37,9 @@ def bio_view( logger.debug(f'UserId: {session.user_id}') # 検索項目の取得 bio = bio_service.prepare_bio_view(session) - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - session_key = set_session(session) + + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'bioSearchList.html', { 'request': request, @@ -74,14 +68,9 @@ def search_bio( bio: BioViewModel = bio_service.prepare_bio_view(session) bio.bio_data = bio_sales_view_data bio.form_data = bio_form - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - session_key = set_session(session) + + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'bioSearchList.html', { 'request': request, diff --git a/ecs/jskult-webapp/src/controller/logout.py b/ecs/jskult-webapp/src/controller/logout.py index 36ab6229..8a633f6c 100644 --- a/ecs/jskult-webapp/src/controller/logout.py +++ b/ecs/jskult-webapp/src/controller/logout.py @@ -3,7 +3,7 @@ from typing import Optional, Union from fastapi import APIRouter, Depends, Request from fastapi.responses import HTMLResponse -from src.depends.auth import verify_session +from src.depends.auth import get_current_session from src.model.internal.session import UserSession from src.model.view.logout_view_model import LogoutViewModel from src.system_var import constants @@ -20,7 +20,7 @@ router = APIRouter() def logout_view( request: Request, reason: Optional[str] = None, - session: Union[UserSession, None] = Depends(verify_session) + session: Union[UserSession, None] = Depends(get_current_session) ): # どういうルートでログインしたかを判断するため、refererを取得 referer = request.headers.get('referer', '') diff --git a/ecs/jskult-webapp/src/controller/master_mainte.py b/ecs/jskult-webapp/src/controller/master_mainte.py index f205824e..ac9fa50b 100644 --- a/ecs/jskult-webapp/src/controller/master_mainte.py +++ b/ecs/jskult-webapp/src/controller/master_mainte.py @@ -20,7 +20,6 @@ from src.model.view.table_override_view_model import TableOverrideViewModel from src.router.session_router import AuthenticatedRoute from src.services.batch_status_service import BatchStatusService from src.services.master_mainte_service import MasterMainteService -from src.services.session_service import set_session from src.system_var import constants from src.templates import templates @@ -55,21 +54,16 @@ def menu_view( # 画面表示用のモデル menu = MasterMainteMenuViewModel() - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - set_session(session) + + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'masterMainteMenu.html', { 'request': request, 'menu': menu }, - headers={'session_key': session.session_key} + headers={'session_key': session_key} ) return templates_response @@ -95,21 +89,16 @@ def inst_emp_csv_upload_view( # 画面表示用のモデル mainte_csv_up = InstEmpCsvUploadViewModel() - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - set_session(session) + + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'instEmpCsvUL.html', { 'request': request, 'mainte_csv_up': mainte_csv_up }, - headers={'session_key': session.session_key} + headers={'session_key': session_key} ) return templates_response @@ -157,21 +146,15 @@ async def inst_emp_csv_upload( select_function=csv_upload_form.select_function, select_table=csv_upload_form.select_table) - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - set_session(session) + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'instEmpCsvUL.html', { 'request': request, 'mainte_csv_up': mainte_csv_up }, - headers={'session_key': session.session_key} + headers={'session_key': session_key} ) return templates_response @@ -199,21 +182,16 @@ def new_inst_result_view( # 画面表示用のモデル mainte_csv_up = master_mainte_service.prepare_mainte_new_inst_view(session.user_id, csv_upload_form) - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - set_session(session) + + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'instEmpCsvUL.html', { 'request': request, 'mainte_csv_up': mainte_csv_up }, - headers={'session_key': session.session_key} + headers={'session_key': session_key} ) return templates_response @@ -241,21 +219,16 @@ def inst_emp_csv_download_view( mainte_csv_dl = InstEmpCsvDownloadViewModel( is_search=False ) - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - set_session(session) + + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'instEmpCsvDL.html', { 'request': request, 'mainte_csv_dl': mainte_csv_dl }, - headers={'session_key': session.session_key} + headers={'session_key': session_key} ) return templates_response @@ -310,22 +283,15 @@ def inst_emp_csv_download( result_msg=result_msg ) - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - set_session(session) - + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'instEmpCsvDL.html', { 'request': request, 'mainte_csv_dl': mainte_csv_dl }, - headers={'session_key': session.session_key} + headers={'session_key': session_key} ) return templates_response @@ -351,21 +317,16 @@ def table_override_view( # 画面表示用のモデル table_override = TableOverrideViewModel() - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - set_session(session) + + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'tableOverride.html', { 'request': request, 'table_override': table_override }, - headers={'session_key': session.session_key} + headers={'session_key': session_key} ) return templates_response @@ -393,20 +354,14 @@ def table_override_result_view( # 画面表示用のモデル table_override = master_mainte_service.copy_data_real_to_dummy() - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - set_session(session) + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'tableOverride.html', { 'request': request, 'table_override': table_override }, - headers={'session_key': session.session_key} + headers={'session_key': session_key} ) return templates_response diff --git a/ecs/jskult-webapp/src/controller/menu.py b/ecs/jskult-webapp/src/controller/menu.py index 96826fce..aaeb0cf2 100644 --- a/ecs/jskult-webapp/src/controller/menu.py +++ b/ecs/jskult-webapp/src/controller/menu.py @@ -8,7 +8,6 @@ from src.model.view.menu_view_model import MenuViewModel from src.model.view.user_view_model import UserViewModel from src.router.session_router import AuthenticatedRoute from src.services.batch_status_service import BatchStatusService -from src.services.session_service import set_session from src.templates import templates logger = get_logger('MeDaCA機能メニュー') @@ -27,7 +26,6 @@ def menu_view( batch_status_service: BatchStatusService = Depends(get_service(BatchStatusService)) ): session: UserSession = request.session - logger.info(f'UserID: {session.user_id}') # 日付マスターからバッチ情報を取得する hdke_tbl_record = batch_status_service.hdke_table_record @@ -44,20 +42,15 @@ def menu_view( dump_status=dump_status, user_model=user ) - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - set_session(session) + + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'menu.html', { 'request': request, 'menu': menu }, - headers={'session_key': session.session_key} + headers={'session_key': session_key} ) return templates_response diff --git a/ecs/jskult-webapp/src/controller/ultmarc.py b/ecs/jskult-webapp/src/controller/ultmarc.py index 825c8b50..6070042f 100644 --- a/ecs/jskult-webapp/src/controller/ultmarc.py +++ b/ecs/jskult-webapp/src/controller/ultmarc.py @@ -10,7 +10,6 @@ from src.model.request.ultmarc_inst import (UltmarcInstInfoModel, UltmarcInstSearchModel) from src.router.session_router import AuthenticatedRoute from src.services.batch_status_service import BatchStatusService -from src.services.session_service import set_session from src.services.ultmarc_view_service import UltmarcViewService from src.templates import templates @@ -40,14 +39,8 @@ def ultmarc_inst_view( ultmarc = ultmarc_service.prepare_ultmarc_inst_search_view() ultmarc.is_batch_processing = is_batch_processing - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - session_key = set_session(session) + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'instSearch.html', { 'request': request, @@ -80,14 +73,8 @@ def search_inst( # 画面表示用にエスケープを解除して返す ultmarc.form_data = ultmarc_inst_form.unescape() - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - session_key = set_session(session) + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'instSearch.html', { 'request': request, @@ -124,14 +111,8 @@ def ultmarc_inst_info_view( # ページ数(表示するページNo) ultmarc.page_num = 0 - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - session_key = set_session(session) + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'instInfo.html', { 'request': request, @@ -167,14 +148,8 @@ def ultmarc_inst_info_search( # ページ数(表示するページNo) ultmarc.page_num = ultmarc_inst_form.page_num - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - session_key = set_session(session) + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'instInfo.html', { 'request': request, @@ -203,14 +178,8 @@ def ultmarc_doctor_view( ultmarc = ultmarc_service.prepare_ultmarc_doctor_search_view() ultmarc.is_batch_processing = is_batch_processing - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - session_key = set_session(session) + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'docSearch.html', { 'request': request, @@ -243,14 +212,8 @@ def search_doc( # 画面表示用にエスケープを解除して返す ultmarc.form_data = ultmarc_doctor_form.unescape() - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - session_key = set_session(session) + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'docSearch.html', { 'request': request, @@ -287,14 +250,8 @@ def ultmarc_doctor_info_view( # ページ数(表示するページNo) ultmarc.page_num = 0 - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - session_key = set_session(session) + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'docInfo.html', { 'request': request, @@ -329,14 +286,8 @@ def ultmarc_doctor_info_search( # ページ数(表示するページNo) ultmarc.page_num = ultmarc_doctor_form.page_num - # セッション書き換え - session.update( - actions=[ - UserSession.last_access_time.set(UserSession.new_last_access_time()), - UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), - ] - ) - session_key = set_session(session) + # レスポンス + session_key = session.session_key templates_response = templates.TemplateResponse( 'docInfo.html', { 'request': request, diff --git a/ecs/jskult-webapp/src/depends/auth.py b/ecs/jskult-webapp/src/depends/auth.py index 820cdc9a..f2383d4e 100644 --- a/ecs/jskult-webapp/src/depends/auth.py +++ b/ecs/jskult-webapp/src/depends/auth.py @@ -8,7 +8,7 @@ from src.error.exceptions import JWTTokenVerifyException from src.logging.get_logger import get_logger from src.model.internal.jwt_token import JWTToken from src.model.internal.session import UserSession -from src.services.session_service import get_session +from src.services.session_service import get_session, set_session from src.system_var import environment logger = get_logger('認証チェック') @@ -16,7 +16,7 @@ cookie_security = APIKeyCookie(name='session', auto_error=False) code_security = APIKeyQuery(name='code', auto_error=False) -def get_current_session(session_key=Depends(cookie_security)): +def get_current_session(session_key=Depends(cookie_security)) -> Union[UserSession, None]: if session_key is None: return None @@ -26,27 +26,35 @@ def get_current_session(session_key=Depends(cookie_security)): return session -def check_session_expired(session: Union[UserSession, None] = Depends(get_current_session)): +def check_session_expired(session: Union[UserSession, None] = Depends(get_current_session)) -> Union[UserSession, None]: """セッションの最後にアクセスした時間が、セッション有効期限切れであるかどうかをチェックする""" if session is None: return None last_access_time = session.last_access_time - session_expired_period = datetime.datetime.fromtimestamp( - last_access_time) + datetime.timedelta(minutes=environment.SESSION_EXPIRE_MINUTE) + last_access_datetime = datetime.datetime.fromtimestamp(last_access_time) + session_expired_period = last_access_datetime + datetime.timedelta(minutes=environment.SESSION_EXPIRE_MINUTE) + logger.debug(f'last_access_time: {last_access_datetime}') + logger.debug(f'session_expired_period: {session_expired_period}') if session_expired_period < datetime.datetime.now(): return None return session -def verify_session(session: Union[UserSession, None] = Depends(check_session_expired)): +def verify_session(session: Union[UserSession, None] = Depends(check_session_expired)) -> Union[UserSession, None]: if session is None: return None jwt_token = JWTToken(session.id_token, session.refresh_token) try: - jwt_token.verify_token() + verified_token = jwt_token.verify_token() except JWTTokenVerifyException as e: logger.info(e) return None + + # IDトークンがリフレッシュされた場合、セッションに詰め直して更新 + if verified_token.is_refreshed: + session.update(actions=[UserSession.id_token.set(verified_token.id_token)]) + set_session(session) + session.id_token = verified_token.id_token return session diff --git a/ecs/jskult-webapp/src/model/internal/jwt_token.py b/ecs/jskult-webapp/src/model/internal/jwt_token.py index d7544125..69865846 100644 --- a/ecs/jskult-webapp/src/model/internal/jwt_token.py +++ b/ecs/jskult-webapp/src/model/internal/jwt_token.py @@ -1,4 +1,5 @@ import base64 +import datetime import json from typing import Optional @@ -7,18 +8,23 @@ import requests from starlette import status from src.error.exceptions import JWTTokenVerifyException +from src.logging.get_logger import get_logger from src.system_var import environment +logger = get_logger('JWTトークン検証') + class JWTToken: id_token: str refresh_token: str verified_jwt: Optional[dict] + is_refreshed: Optional[bool] - def __init__(self, id_token: str, refresh_token: str, verified_jwt: dict = None) -> None: + def __init__(self, id_token: str, refresh_token: str, verified_jwt: dict = None, is_refreshed: bool = False): self.id_token = id_token self.refresh_token = refresh_token self.verified_jwt = verified_jwt + self.is_refreshed = is_refreshed @property def verified_token(self): @@ -114,7 +120,7 @@ class JWTToken: token_response = json.loads(res.text) return cls(id_token=token_response['id_token'], refresh_token=refresh_token) - def verify_token(self): + def verify_token(self, is_refreshed=False): if self.id_token is None: raise Exception('アクセストークンがない') @@ -134,10 +140,16 @@ class JWTToken: # Cognitoのサーバー時間とのズレにより、Issued atクレームの検証に失敗するパターンに対処する options={'verify_iat': False} ) + # トークン有効期限をログに出力 + exp = verified_jwt.get('exp') + expire_datetime = datetime.datetime.fromtimestamp(exp) if exp else None + logger.info(f"トークン有効期限:{expire_datetime}") # 有効期限(exp)が切れた場合、トークンをリフレッシュする except jwt.ExpiredSignatureError: + logger.info('IDトークンの有効期限が切れたため、トークンをリフレッシュ') refreshed_jwt_token = JWTToken.refresh(self.refresh_token) - return refreshed_jwt_token.verified_token() + # リフレッシュ後のトークンを再度検証 + return refreshed_jwt_token.verify_token(is_refreshed=True) # 有効期限以外の検証に失敗した場合は例外とする except jwt.InvalidTokenError as e: raise JWTTokenVerifyException('Invalid token', e) @@ -148,5 +160,6 @@ class JWTToken: return JWTToken( id_token=self.id_token, refresh_token=self.refresh_token, - verified_jwt=verified_jwt + verified_jwt=verified_jwt, + is_refreshed=is_refreshed ) diff --git a/ecs/jskult-webapp/src/model/internal/session.py b/ecs/jskult-webapp/src/model/internal/session.py index 39d319e2..99233a1e 100644 --- a/ecs/jskult-webapp/src/model/internal/session.py +++ b/ecs/jskult-webapp/src/model/internal/session.py @@ -29,9 +29,10 @@ class UserSession(DynamoDBTableModel): return datetime.datetime.now().timestamp() @classmethod - def new_record_expiration_time(cls, expire=environment.SESSION_EXPIRE_MINUTE): + def new_record_expiration_time(cls): last_access_time = datetime.datetime.fromtimestamp(cls.new_last_access_time()) - return (last_access_time + datetime.timedelta(minutes=expire)).timestamp() + # 1時間後に有効期限切れにする + return (last_access_time + datetime.timedelta(hours=1)).timestamp() @classmethod def new( diff --git a/ecs/jskult-webapp/src/router/session_router.py b/ecs/jskult-webapp/src/router/session_router.py index 768c7f87..ccb5bc96 100644 --- a/ecs/jskult-webapp/src/router/session_router.py +++ b/ecs/jskult-webapp/src/router/session_router.py @@ -10,6 +10,8 @@ from src.depends.auth import (check_session_expired, get_current_session, verify_session) from src.error.exceptions import DBException, UnexpectedException from src.logging.get_logger import get_logger +from src.model.internal.session import UserSession +from src.services.session_service import set_session from src.system_var import constants, environment logger = get_logger('medaca_router') @@ -124,6 +126,16 @@ class AfterSetCookieSessionRoute(PrepareDatabaseRoute): return response del response.headers['session_key'] + + # セッションを更新 + session = get_current_session(session_key) + session.update( + actions=[ + UserSession.last_access_time.set(UserSession.new_last_access_time()), + UserSession.record_expiration_time.set(UserSession.new_record_expiration_time()), + ] + ) + set_session(session) # クッキーにセッションを設定 response.set_cookie( key='session',