11import os
22from flask import (
33 Blueprint , render_template , request , session ,
4- redirect , url_for , flash , abort
4+ redirect , url_for , flash , abort , send_from_directory
55)
6- from utils .results_loader import load_results_table
6+ from utils .results_loader import load_results_table , load_estimated_results_table
77from utils .otp_manager import send_otp , verify_otp , get_affiliations
88from utils .result_file import load_result_file , get_file_confidential_tags
99
1010results_bp = Blueprint ("results" , __name__ )
1111SAVE_DIR = "received"
12+ ESTIMATE_DIR = "estimated_results"
1213
1314
15+ # ==========================================
16+ # 共通関数: ファイルアクセス権限確認
17+ # ==========================================
18+ def check_file_permission (filename , session_key_authenticated , session_key_email , dir_path ):
19+ tags = get_file_confidential_tags (filename , dir_path )
20+ if not tags :
21+ return # 公開ファイル
22+
23+ authenticated = session .get (session_key_authenticated , False )
24+ email = session .get (session_key_email )
25+ affs = get_affiliations (email ) if email else []
26+ if not authenticated or not (set (tags ) & set (affs )):
27+ abort (403 , "このファイルにアクセスする権限がありません" )
28+
29+
30+ def serve_confidential_file (filename , dir_path , session_key_authenticated , session_key_email ):
31+ """ファイルアクセス権限確認して送信"""
32+ check_file_permission (filename , session_key_authenticated , session_key_email , dir_path )
33+ #return send_from_directory(dir_path, filename, as_attachment=True)
34+ return load_result_file (filename , dir_path )
35+
1436# ==========================================
1537# 公開用の結果一覧ページ
1638# ==========================================
17- @results_bp .route ("/results" )
39+ @results_bp .route ("/results" , strict_slashes = False )
1840def results ():
1941 rows , columns = load_results_table (public_only = True )
2042 return render_template ("results.html" , rows = rows , columns = columns )
@@ -23,79 +45,93 @@ def results():
2345# ==========================================
2446# 機密データ付きの結果ページ(OTP認証付き)
2547# ==========================================
26- @results_bp .route ("/results_confidential" , methods = ["GET" , "POST" ])
27- def results_confidential ():
28- # フォーム送信処理
29- if request .method == "POST" :
30- email = request .form .get ("email" )
31- otp = request .form .get ("otp" )
32-
33- if email and not otp :
34- # STEP1: メール送信
35- success , msg = send_otp (email )
36- if success :
37- flash ("OTPをメールに送信しました" )
38- session ["otp_email" ] = email
39- session ["otp_stage" ] = "otp"
40- else :
41- flash (msg )
42- session .pop ("otp_email" , None )
43- session ["otp_stage" ] = "email"
44- return redirect (url_for ("results.results_confidential" ))
45-
46- elif otp :
47- # STEP2: OTP検証
48- otp_email = session .get ("otp_email" )
49- if otp_email and verify_otp (otp_email , otp ):
50- session ["authenticated_confidential" ] = True
51- flash ("認証成功" )
52- session .pop ("otp_stage" , None ) # 認証済みなので削除
53- else :
54- flash ("OTP認証失敗" )
55- session .pop ("otp_email" , None )
56- session .pop ("authenticated_confidential" , None )
57- session ["otp_stage" ] = "email"
58- return redirect (url_for ("results.results_confidential" ))
59-
60- # OTPステージ判定
61- authenticated = session .get ("authenticated_confidential" , False )
62- otp_email = session .get ("otp_email" )
48+ def handle_otp_post (session_key_authenticated , session_key_email , route_name ):
49+ email = request .form .get ("email" )
50+ otp = request .form .get ("otp" )
51+
52+ if email and not otp :
53+ success , msg = send_otp (email )
54+ if success :
55+ flash ("OTPをメールに送信しました" )
56+ session [session_key_email ] = email
57+ session ["otp_stage" ] = "otp"
58+ else :
59+ flash (msg )
60+ session .pop (session_key_email , None )
61+ session ["otp_stage" ] = "email"
62+ return redirect (url_for (route_name ))
63+
64+ elif otp :
65+ otp_email = session .get (session_key_email )
66+ if otp_email and verify_otp (otp_email , otp ):
67+ session [session_key_authenticated ] = True
68+ flash ("認証成功" )
69+ session .pop ("otp_stage" , None )
70+ else :
71+ flash ("OTP認証失敗" )
72+ session .pop (session_key_email , None )
73+ session .pop (session_key_authenticated , None )
74+ session ["otp_stage" ] = "email"
75+ return redirect (url_for (route_name ))
76+
77+
78+ def render_confidential_table (template_name , public_only , session_key_authenticated , session_key_email , loader_func = None ):
79+ authenticated = session .get (session_key_authenticated , False )
80+ otp_email = session .get (session_key_email )
6381
6482 if authenticated :
65- otp_stage = None # 認証済みなのでフォームは出さない
83+ otp_stage = None
6684 elif otp_email :
67- otp_stage = "otp" # OTP入力待ち
85+ otp_stage = "otp"
6886 else :
69- otp_stage = "email" # メール入力待ち
87+ otp_stage = "email"
7088
89+ # ローダー関数を使い分ける
90+ if loader_func is None :
91+ # デフォルトは通常の results
92+ loader_func = load_results_table
7193
72- # 結果テーブル読み込み(confidential制御は utils 内で処理)
73- rows , columns = load_results_table (
74- public_only = False ,
94+ rows , columns = loader_func (
95+ public_only = public_only ,
7596 session_email = otp_email ,
7697 authenticated = authenticated
7798 )
7899
79100 return render_template (
80- "results_confidential.html" ,
101+ template_name ,
81102 rows = rows ,
82103 columns = columns ,
83104 authenticated = authenticated ,
84105 otp_stage = otp_stage
85106 )
86107
108+
109+ @results_bp .route ("/results_confidential" , methods = ["GET" , "POST" ], strict_slashes = False )
110+ def results_confidential ():
111+ if request .method == "POST" :
112+ return handle_otp_post ("authenticated_confidential" , "otp_email" , "results.results_confidential" )
113+ return render_confidential_table ("results_confidential.html" , public_only = False ,
114+ session_key_authenticated = "authenticated_confidential" ,
115+ session_key_email = "otp_email" )
116+
117+
118+ @results_bp .route ("/estimated_results" , methods = ["GET" , "POST" ], strict_slashes = False )
119+ def estimated_results ():
120+ if request .method == "POST" :
121+ return handle_otp_post ("authenticated_estimated" , "otp_email_estimated" , "results.estimated_results" )
122+ return render_confidential_table ("estimated_results.html" , public_only = False ,
123+ session_key_authenticated = "authenticated_estimated" ,
124+ session_key_email = "otp_email_estimated" , loader_func = load_estimated_results_table )
125+
126+
87127# ==========================================
88128# 個別結果ファイルの表示/ダウンロード
89129# ==========================================
90130@results_bp .route ("/results/<filename>" )
91131def show_result (filename ):
92- tags = get_file_confidential_tags (filename )
132+ return serve_confidential_file (filename , SAVE_DIR , "authenticated_confidential" , "otp_email" )
93133
94- if tags :
95- authenticated = session .get ("authenticated_confidential" , False )
96- email = session .get ("otp_email" )
97- affs = get_affiliations (email ) if email else []
98- if not authenticated or not (set (tags ) & set (affs )):
99- abort (403 , "このファイルにアクセスする権限がありません" )
100134
101- return load_result_file (filename )
135+ @results_bp .route ("/estimated_results/<filename>" )
136+ def show_estimated_result (filename ):
137+ return serve_confidential_file (filename , ESTIMATE_DIR , "authenticated_estimated" , "otp_email_estimated" )
0 commit comments