
massive modification, this is the part where DB gets cryptography, extended functionality support for the application
@9d4865e527f227acde6c37eec539578aba157b75
+++ .gitignore
... | ... | @@ -0,0 +1,3 @@ |
1 | +/database/keys/ | |
2 | +/database/db_config.json | |
3 | +/database/db_config1.json |
--- action.py
+++ action.py
... | ... | @@ -18,59 +18,6 @@ |
18 | 18 |
) |
19 | 19 |
|
20 | 20 |
|
21 |
- |
|
22 |
-def find_node(gps_address_y,gps_address_x): |
|
23 |
- db=DB() |
|
24 |
- nn_end = None |
|
25 |
- end_delta = float("inf") |
|
26 |
- value=0.0001 |
|
27 |
- near_nodes=[] |
|
28 |
- while near_nodes==[]: |
|
29 |
- value=value*10 |
|
30 |
- near_nodes=db.db_get_near_node(gps_address_y,gps_address_x,value) |
|
31 |
- |
|
32 |
- for n in near_nodes: |
|
33 |
- e_dist = haversine((gps_address_x,gps_address_y), n) |
|
34 |
- if e_dist < end_delta : |
|
35 |
- nn_end = n |
|
36 |
- end_delta = e_dist |
|
37 |
- return nn_end |
|
38 |
- |
|
39 |
- |
|
40 |
-@Action.route('/image_summit') |
|
41 |
-class fileUpload(Resource): |
|
42 |
- @Action.doc(responses={200: 'Success'}) |
|
43 |
- @Action.doc(responses={500: 'Register Failed'}) |
|
44 |
- def post(self): |
|
45 |
- if request.method == 'POST': |
|
46 |
- current_time = datetime.now() |
|
47 |
- timestamp = current_time.strftime("%Y%m%d%H%M%S") |
|
48 |
- # 시간을 원하는 형식으로 포맷팅하기 (예: 년-월-일 시:분:초) |
|
49 |
- formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") |
|
50 |
- # 포맷팅된 현재 시간 출력 |
|
51 |
- token = request.headers.get('Authorization') |
|
52 |
- print(token) |
|
53 |
- if not token: |
|
54 |
- return jsonify({'result': 'fail', 'msg': '토큰이 없습니다.'}) |
|
55 |
- else: |
|
56 |
- # Decode the token to verify it |
|
57 |
- decoded_token = jwt.decode(token, "secret", algorithms=['HS256']) |
|
58 |
- print(decoded_token) |
|
59 |
- user_id = decoded_token['id'] |
|
60 |
- print("현재 시간,저장요청:", formatted_time) |
|
61 |
- f = request.files['file'] |
|
62 |
- save_path = f"images/{timestamp}/{user_id}" |
|
63 |
- os.makedirs(save_path, exist_ok=True) |
|
64 |
- new_filename = f"{save_path}/{user_id}_{timestamp}.jpg" |
|
65 |
- print(f) |
|
66 |
- f.save(new_filename) |
|
67 |
- print("저장완료", formatted_time) |
|
68 |
- |
|
69 |
- return { |
|
70 |
- 'save': 'done' # str으로 반환하여 return |
|
71 |
- }, 200 |
|
72 |
- |
|
73 |
- |
|
74 | 21 |
@Action.route('/gps_update') |
75 | 22 |
class fileUpload(Resource): |
76 | 23 |
def post(self): |
--- app.py
+++ app.py
... | ... | @@ -2,7 +2,6 @@ |
2 | 2 |
from flask_restx import Api |
3 | 3 |
from flask_cors import CORS |
4 | 4 |
from auth import Auth |
5 |
-from trip import Trip |
|
6 | 5 |
from action import Action |
7 | 6 |
from flask_caching import Cache |
8 | 7 |
import json |
... | ... | @@ -21,8 +20,6 @@ |
21 | 20 |
license="") |
22 | 21 |
|
23 | 22 |
print("Api Started") |
24 |
-api.add_namespace(Trip, '/trip') |
|
25 |
-print("Api Add Trip") |
|
26 | 23 |
|
27 | 24 |
api.add_namespace(Auth, '/auth') |
28 | 25 |
print("Api Add Auth") |
... | ... | @@ -30,5 +27,5 @@ |
30 | 27 |
api.add_namespace(Action, '/action') |
31 | 28 |
|
32 | 29 |
if __name__ == "__main__": |
33 |
- app.run(debug=True, host='192.168.0.195', port=15857) |
|
30 |
+ app.run(debug=True, host='0.0.0.0', port=15857) |
|
34 | 31 |
print("Flask Start") |
--- auth.py
+++ auth.py
... | ... | @@ -27,6 +27,16 @@ |
27 | 27 |
|
28 | 28 |
}) |
29 | 29 |
|
30 |
+ |
|
31 |
+get_phone_number = Auth.inherit('get a phone number of an user', { |
|
32 |
+ 'id' : fields.String(description="user id", required=True) |
|
33 |
+}) |
|
34 |
+ |
|
35 |
+ |
|
36 |
+get_email = Auth.inherit('get an email of an user', { |
|
37 |
+ 'id' : fields.String(description="user id", required=True) |
|
38 |
+}) |
|
39 |
+ |
|
30 | 40 |
user_fields_register = Auth.inherit('User reigster', user_fields, { |
31 | 41 |
'password': fields.String(description='Password', required=True),'email': fields.String(description='email', required=True),'user_sex': fields.String(description='sex', required=True),'phone': fields.String(description='phone', required=True) |
32 | 42 |
|
... | ... | @@ -54,58 +64,105 @@ |
54 | 64 |
|
55 | 65 |
|
56 | 66 |
|
67 |
+ |
|
57 | 68 |
@Auth.route('/register') |
58 | 69 |
class AuthRegister(Resource): |
59 | 70 |
@Auth.expect(user_fields_register) |
60 | 71 |
@Auth.doc(responses={200: 'Success'}) |
61 | 72 |
@Auth.doc(responses={500: 'Register Failed'}) |
62 | 73 |
def post(self): |
63 |
- db=DB() |
|
64 |
- id_ = request.json['id'] |
|
65 |
- password = request.json['password'] |
|
66 |
- user_email = request.json['email'] |
|
67 |
- sex = request.json['sex'] |
|
68 |
- phone = request.json['phone'] |
|
69 |
- pw_has = hashlib.sha256(password.encode('utf-8')).hexdigest() |
|
70 |
- db_check_duplicate = db.db_check_id(id_) |
|
71 |
- if db_check_duplicate != None: |
|
72 |
- return { |
|
73 |
- "message" : "Register Failed : duplicate ID" |
|
74 |
- }, 500 |
|
75 |
- db_login_success_flag=db.db_login(id_,password) |
|
76 |
- if db_login_success_flag != None: |
|
77 |
- return { |
|
78 |
- "message": "Register Failed" |
|
79 |
- }, 500 |
|
74 |
+ user_manager = DB() |
|
75 |
+ # Extract data from the request |
|
76 |
+ data = request.json |
|
77 |
+ id_ = data['id'] |
|
78 |
+ password = data['password'] |
|
79 |
+ user_email = data['email'] |
|
80 |
+ sex = data['user_sex'] |
|
81 |
+ phone = data['phone'] |
|
82 |
+ |
|
83 |
+ # Prepare data for registration |
|
84 |
+ user_data = { |
|
85 |
+ 'username': id_, |
|
86 |
+ 'password': password, |
|
87 |
+ 'email': user_email, |
|
88 |
+ 'sex': sex, |
|
89 |
+ 'phone': phone |
|
90 |
+ } |
|
91 |
+ |
|
92 |
+ # Call the register_user method from the UserManager instance |
|
93 |
+ result, status_code = user_manager.register_user(user_data) |
|
94 |
+ |
|
95 |
+ # Return the appropriate response based on the result from UserManager |
|
96 |
+ if status_code == 200: |
|
97 |
+ return result, 200 |
|
80 | 98 |
else: |
81 |
- db.db_add_id(id_,pw_has,user_email,sex,phone) |
|
82 |
- return { |
|
83 |
- 'Authorization': id_ # str으로 반환하여 return |
|
84 |
- }, 200 |
|
99 |
+ return result, 500 |
|
100 |
+ |
|
101 |
+@Auth.route('/retrive_phone_number') |
|
102 |
+class AuthRegister(Resource): |
|
103 |
+ @Auth.expect(get_phone_number) |
|
104 |
+ @Auth.doc(responses={200: 'Success'}) |
|
105 |
+ @Auth.doc(responses={500: 'Register Failed'}) |
|
106 |
+ def post(self): |
|
107 |
+ user_manager = DB() |
|
108 |
+ data = request.json |
|
109 |
+ id_ = data['id'] |
|
110 |
+ query_input = { |
|
111 |
+ "username" : id_ |
|
112 |
+ } |
|
113 |
+ result, status_code = user_manager.get_phone_number(query_input) |
|
114 |
+ |
|
115 |
+ if status_code == 200: |
|
116 |
+ return result, 200 |
|
117 |
+ else: |
|
118 |
+ return result, 500 |
|
119 |
+ |
|
120 |
+ |
|
121 |
+@Auth.route('/retrive_email') |
|
122 |
+class AuthRegister(Resource): |
|
123 |
+ @Auth.expect(get_email) |
|
124 |
+ @Auth.doc(responses={200: 'Success'}) |
|
125 |
+ @Auth.doc(responses={500: 'Register Failed'}) |
|
126 |
+ def post(self): |
|
127 |
+ user_manager = DB() |
|
128 |
+ data = request.json |
|
129 |
+ id_ = data['id'] |
|
130 |
+ query_input = { |
|
131 |
+ "username" : id_ |
|
132 |
+ } |
|
133 |
+ result, status_code = user_manager.get_email(query_input) |
|
134 |
+ |
|
135 |
+ if status_code == 200: |
|
136 |
+ return result, 200 |
|
137 |
+ else: |
|
138 |
+ return result, 500 |
|
139 |
+ |
|
140 |
+ |
|
85 | 141 |
|
86 | 142 |
@Auth.route('/login') |
87 | 143 |
class AuthLogin(Resource): |
88 | 144 |
@Auth.expect(user_fields_auth) |
89 |
- @Auth.doc(responses={200: 'Success'}) |
|
90 |
- @Auth.doc(responses={404: 'User Not Found'}) |
|
91 |
- @Auth.doc(responses={500: 'Auth Failed'}) |
|
145 |
+ @Auth.doc(responses={200: 'Login Successful'}) |
|
146 |
+ @Auth.doc(responses={401: 'Unauthorized'}) |
|
147 |
+ @Auth.doc(responses={500: 'Login Failed'}) |
|
92 | 148 |
def post(self): |
93 |
- db=DB() |
|
94 |
- id = request.json['id'] |
|
95 |
- password = request.json['password'] |
|
96 |
- #CRITICAL ... WHY? WHY? |
|
97 |
- # ... hashing should be handled at client, not server... |
|
98 |
- pw_hash = hashlib.sha256(password.encode('utf-8')).hexdigest() |
|
99 |
- result = db.db_login(id,pw_hash) |
|
100 |
- if result is not None: |
|
101 |
- payload = { |
|
102 |
- 'id' : id, |
|
103 |
- 'exp' : datetime.datetime.utcnow() + datetime.timedelta(days=14) |
|
104 |
- } |
|
105 |
- token = jwt.encode(payload, "secret", algorithm='HS256') |
|
106 |
- return jsonify({'result': 'success', 'token': token}) |
|
107 |
- else: |
|
108 |
- return jsonify({'result': 'fail', 'msg': '아이디/비밀번호가 일치하지 않습니다.'}) |
|
149 |
+ user_manager = DB() |
|
150 |
+ # Extract data from the request |
|
151 |
+ data = request.json |
|
152 |
+ id_ = data['id'] |
|
153 |
+ password = data['password'] |
|
154 |
+ |
|
155 |
+ # Prepare data for authentication |
|
156 |
+ user_data = { |
|
157 |
+ 'username': id_, |
|
158 |
+ 'password': password |
|
159 |
+ } |
|
160 |
+ |
|
161 |
+ # Call the login_user method from the UserManager instance |
|
162 |
+ result, status_code = user_manager.login_user(user_data) |
|
163 |
+ |
|
164 |
+ # Return the appropriate response based on the result from UserManager |
|
165 |
+ return result, status_code |
|
109 | 166 |
|
110 | 167 |
|
111 | 168 |
@Auth.route('/secession') |
--- database/database.py
+++ database/database.py
... | ... | @@ -1,216 +1,221 @@ |
1 | 1 |
import psycopg2 # driver 임포트 |
2 |
-import time |
|
2 |
+import json |
|
3 |
+import bcrypt |
|
4 |
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes |
|
5 |
+from cryptography.hazmat.primitives import padding |
|
6 |
+from cryptography.hazmat.backends import default_backend |
|
7 |
+import re |
|
8 |
+import os |
|
3 | 9 |
from io import StringIO |
4 | 10 |
from datetime import datetime, timedelta |
5 | 11 |
|
12 |
+config_file_path = "database/db_config.json" |
|
6 | 13 |
|
7 | 14 |
class DB(): |
8 | 15 |
def __init__(self): |
9 |
- self.conn=psycopg2.connect( |
|
10 |
- host='210.180.118.83', |
|
11 |
- dbname='TRAFFICAGNET', |
|
12 |
- user='takensoft', |
|
13 |
- password='tts96314728!@', |
|
14 |
- port='5432', |
|
15 |
- options=f'-c search_path=trafficagent' |
|
16 |
- ) # db에 접속 |
|
16 |
+ # Load the database configuration from the JSON file |
|
17 |
+ self.db_config = self.load_db_config(config_file_path) |
|
18 |
+ |
|
19 |
+ # Initialize database connection |
|
20 |
+ self.conn = psycopg2.connect( |
|
21 |
+ host=self.db_config['host'], |
|
22 |
+ dbname=self.db_config['dbname'], |
|
23 |
+ user=self.db_config['user'], |
|
24 |
+ password=self.db_config['password'], |
|
25 |
+ port=self.db_config['port'], |
|
26 |
+ # options=self.db_config['options'] |
|
27 |
+ ) |
|
28 |
+ self.schema = self.db_config["schema"] |
|
17 | 29 |
self.conn.autocommit=True |
18 |
- self.schema = 'trafficagent' |
|
19 |
- ''' |
|
20 |
- def __init__(self): |
|
21 |
- self.conn=psycopg2.connect( |
|
22 |
- host='165.229.169.113', |
|
23 |
- dbname='traffic_agent', |
|
24 |
- user='takensoft', |
|
25 |
- password='ts44301236!@', |
|
26 |
- port='5432', |
|
27 |
- options="-c search_path=traffic_agent_v1") # db에 접속 |
|
28 |
- self.conn.autocommit=True |
|
29 |
- ''' |
|
30 |
- |
|
31 |
- |
|
32 |
- |
|
33 |
- def db_check_id(self,id): |
|
34 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
30 |
+ self.cur = self.conn.cursor() |
|
31 |
+ # yeah, that double quotation is absolutely needed (to distinguish capital letters) |
|
32 |
+ self.cur.execute("SET search_path TO " + f'"{self.schema}"') |
|
33 |
+ with open("database/keys/encryption_key2024-09-05_14:27:02", "rb") as f: |
|
34 |
+ self.encryption_key = f.read() |
|
35 | 35 |
|
36 |
- cur.execute(f''' |
|
37 |
- SELECT user_id |
|
38 |
- FROM "{self.schema}".user_id |
|
39 |
- Where user_id = '{id}'; |
|
40 |
- ''') |
|
41 |
- result=cur.fetchone() |
|
42 |
- cur.close() |
|
36 |
+ def load_db_config(self, config_file_path): |
|
37 |
+ """ |
|
38 |
+ Loads database configuration from a JSON file. |
|
39 |
+ """ |
|
40 |
+ with open(config_file_path, 'r') as config_file: |
|
41 |
+ return json.load(config_file) |
|
43 | 42 |
|
44 |
- return result |
|
43 |
+ def encrypt_aes(self, plain_text): |
|
44 |
+ iv = os.urandom(16) # AES block size is 16 bytes |
|
45 |
+ cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=default_backend()) |
|
46 |
+ encryptor = cipher.encryptor() |
|
45 | 47 |
|
46 |
- def db_login(self,id,pw): |
|
47 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
48 |
+ # Pad the plaintext to be a multiple of 16 bytes |
|
49 |
+ padder = padding.PKCS7(algorithms.AES.block_size).padder() |
|
50 |
+ padded_data = padder.update(plain_text.encode('utf-8')) + padder.finalize() |
|
48 | 51 |
|
49 |
- cur.execute(f''' |
|
50 |
- SELECT user_id, user_pw, user_email, user_sex, user_phone, user_time_stamp |
|
51 |
- FROM "{self.schema}".user_id |
|
52 |
- Where user_id = '{id}' and user_pw='{pw}'; |
|
53 |
- ''') |
|
54 |
- result=cur.fetchone() |
|
52 |
+ encrypted_data = encryptor.update(padded_data) + encryptor.finalize() |
|
53 |
+ return encrypted_data, iv |
|
54 |
+ |
|
55 |
+ def decrypt_aes(self, encrypted_data, iv): |
|
56 |
+ cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=default_backend()) |
|
57 |
+ decryptor = cipher.decryptor() |
|
58 |
+ |
|
59 |
+ decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize() |
|
60 |
+ |
|
61 |
+ # Remove padding after decryption |
|
62 |
+ unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() |
|
63 |
+ unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize() |
|
64 |
+ |
|
65 |
+ return unpadded_data.decode('utf-8') |
|
66 |
+ |
|
67 |
+ def cleanse_and_validate_input(self, data): |
|
68 |
+ """ |
|
69 |
+ Cleanses input by removing leading/trailing spaces and validates the data. |
|
70 |
+ Returns cleansed data and an error message if validation fails. |
|
71 |
+ """ |
|
72 |
+ username = data.get('username', '').strip() |
|
73 |
+ password = data.get('password', '').strip() |
|
74 |
+ email = data.get('email', '').strip() |
|
75 |
+ phone = data.get('phone', '').strip() |
|
76 |
+ sex = data.get('sex', '').strip() |
|
77 |
+ |
|
78 |
+ # Validate username |
|
79 |
+ if not username: |
|
80 |
+ return None, "Username is required." |
|
81 |
+ if len(username) > 255: |
|
82 |
+ return None, "Username must not exceed 255 characters." |
|
83 |
+ |
|
84 |
+ # Validate password |
|
85 |
+ if not password: |
|
86 |
+ return None, "Password is required." |
|
87 |
+ if len(password) < 8: |
|
88 |
+ return None, "Password must be at least 8 characters long." |
|
89 |
+ |
|
90 |
+ # Validate email format |
|
91 |
+ if not email or not re.fullmatch(r"[^@]+@[^@]+\.[^@]+", email): |
|
92 |
+ return None, "Invalid email address." |
|
93 |
+ |
|
94 |
+ # Validate phone number format |
|
95 |
+ if not re.fullmatch(r'010\d{8}', phone): |
|
96 |
+ return None, "Phone number must be in the format 010XXXXXXXX where X are digits." |
|
97 |
+ |
|
98 |
+ # Validate sex input |
|
99 |
+ if not sex: |
|
100 |
+ return None, "Sex is required." |
|
101 |
+ if sex not in ['Male', 'Female', 'Non-binary', 'Other']: |
|
102 |
+ return None, "Invalid value for sex." |
|
103 |
+ |
|
104 |
+ return { |
|
105 |
+ 'username': username, |
|
106 |
+ 'password': password, |
|
107 |
+ 'email': email, |
|
108 |
+ 'phone': phone, |
|
109 |
+ 'sex': sex |
|
110 |
+ }, None |
|
111 |
+ |
|
112 |
+ def register_user(self, data): |
|
113 |
+ data, error = self.cleanse_and_validate_input(data) |
|
114 |
+ if error: |
|
115 |
+ return {'status': 'error', 'message': error}, 400 |
|
116 |
+ |
|
117 |
+ username = data['username'] |
|
118 |
+ password = data['password'] |
|
119 |
+ email = data['email'] |
|
120 |
+ phone = data['phone'] |
|
121 |
+ sex = data['sex'] |
|
122 |
+ |
|
123 |
+ # Hash the password with bcrypt, which automatically handles the salt |
|
124 |
+ hashed_pw = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()) |
|
125 |
+ |
|
126 |
+ # Encrypt email, phone, and sex with AES |
|
127 |
+ encrypted_email, email_iv = self.encrypt_aes(email) |
|
128 |
+ encrypted_phone, phone_iv = self.encrypt_aes(phone) |
|
129 |
+ encrypted_sex, sex_iv = self.encrypt_aes(sex) |
|
130 |
+ |
|
131 |
+ # Insert the user into the database |
|
132 |
+ try: |
|
133 |
+ self.cur.execute(f""" |
|
134 |
+ INSERT INTO users (username, user_pw, user_email, email_iv, user_phone, phone_iv, user_sex, user_time_stamp) |
|
135 |
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s) |
|
136 |
+ """, ( |
|
137 |
+ username, |
|
138 |
+ psycopg2.Binary(hashed_pw), |
|
139 |
+ psycopg2.Binary(encrypted_email), |
|
140 |
+ psycopg2.Binary(email_iv), |
|
141 |
+ psycopg2.Binary(encrypted_phone), |
|
142 |
+ psycopg2.Binary(phone_iv), |
|
143 |
+ psycopg2.Binary(encrypted_sex), |
|
144 |
+ datetime.now() # Correct way to insert current timestamp with timezone |
|
145 |
+ ) |
|
146 |
+ ) |
|
147 |
+ self.conn.commit() |
|
148 |
+ return {'status': 'success', 'message': f'user {username} registered successfully'}, 200 |
|
149 |
+ except psycopg2.Error as e: |
|
150 |
+ self.conn.rollback() |
|
151 |
+ return {'status': 'error', 'message': str(e)}, 400 |
|
152 |
+ |
|
153 |
+ def login_user(self, data): |
|
154 |
+ username = data.get('username', '').strip() |
|
155 |
+ password = data.get('password', '').strip() |
|
156 |
+ |
|
157 |
+ # Validate input |
|
158 |
+ if not username or not password: |
|
159 |
+ return {'status': 'error', 'message': 'Username and password are required.'}, 400 |
|
160 |
+ |
|
161 |
+ # Retrieve the user's hashed password from the database |
|
162 |
+ self.cur.execute("SELECT user_pw FROM users WHERE username = %s", (username,)) |
|
163 |
+ user = self.cur.fetchone() |
|
164 |
+ |
|
165 |
+ if user is None: |
|
166 |
+ return {'status': 'error', 'message': 'Invalid username or password'}, 401 |
|
167 |
+ |
|
168 |
+ hashed_pw = bytes(user[0]) # Convert the retrieved hashed password to bytes |
|
169 |
+ |
|
170 |
+ # Check if the provided password matches the stored hashed password |
|
171 |
+ if bcrypt.checkpw(password.encode('utf-8'), hashed_pw): |
|
172 |
+ return {'status': 'success', 'message': 'Logged in successfully'}, 200 |
|
173 |
+ else: |
|
174 |
+ return {'status': 'error', 'message': 'Invalid username or password'}, 401 |
|
175 |
+ |
|
176 |
+ def get_phone_number(self, data): |
|
177 |
+ username = data.get('username', '').strip() |
|
178 |
+ |
|
179 |
+ if not username: |
|
180 |
+ return {'status': 'error', 'message': 'Username is required.'}, 400 |
|
181 |
+ |
|
182 |
+ # Retrieve the encrypted phone number and IV from the database |
|
183 |
+ self.cur.execute("SELECT user_phone, phone_iv FROM users WHERE username = %s", (username,)) |
|
184 |
+ user = self.cur.fetchone() |
|
185 |
+ |
|
186 |
+ if user is None: |
|
187 |
+ return {'status': 'error', 'message': 'User not found'}, 404 |
|
188 |
+ |
|
189 |
+ encrypted_phone, phone_iv = user |
|
190 |
+ |
|
191 |
+ # Decrypt the phone number |
|
192 |
+ decrypted_phone = self.decrypt_aes(encrypted_phone, phone_iv) |
|
193 |
+ |
|
194 |
+ return {'status': 'success', 'phone_number': decrypted_phone}, 200 |
|
55 | 195 |
|
56 | 196 |
|
57 |
- cur.close() |
|
197 |
+ def get_email(self, data): |
|
198 |
+ username = data.get('username', '').strip() |
|
58 | 199 |
|
59 |
- return result |
|
200 |
+ if not username: |
|
201 |
+ return {'status': 'error', 'message': 'Username is required.'}, 400 |
|
60 | 202 |
|
61 |
- def db_add_id(self,user_id,user_pw,user_email,user_sex,user_phone) : |
|
62 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
63 |
- recievingtime=datetime.now() |
|
64 |
- d = recievingtime.isoformat(sep=' ', timespec='milliseconds') |
|
65 |
- # d=recievingtime.strftime("%Y-%m-%d %H:%M:%S.%f") |
|
66 |
- cur.execute(f''' |
|
67 |
- insert into "{self.schema}".user_id (user_id,user_pw,user_email,user_sex,user_phone,user_time_stamp) |
|
68 |
- values ('{user_id}','{user_pw}','{user_email}','{user_sex}','{user_phone}','{d}') |
|
69 |
- ''') |
|
70 |
- |
|
71 |
- def db_add_action(self,user_id,user_pw,user_email,user_sex,user_phone) : |
|
72 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
73 |
- recievingtime=datetime.now() |
|
74 |
- d = recievingtime.isoformat(sep=' ', timespec='milliseconds') |
|
75 |
- # d=recievingtime.strftime("%Y-%m-%d %H:%M:%S.%f") |
|
76 |
- cur.execute(f''' |
|
77 |
- insert into "{self.schema}".user_id (user_id,user_pw,user_email,user_sex,user_phone,user_time_stamp) |
|
78 |
- values ('{user_id}','{user_pw}','{user_email}','{user_sex}','{user_phone}','{d}') |
|
79 |
- ''') |
|
203 |
+ # Retrieve the encrypted phone number and IV from the database |
|
204 |
+ self.cur.execute("SELECT user_email, email_iv FROM users WHERE username = %s", (username,)) |
|
205 |
+ user = self.cur.fetchone() |
|
80 | 206 |
|
207 |
+ if user is None: |
|
208 |
+ return {'status': 'error', 'message': 'User not found'}, 404 |
|
81 | 209 |
|
82 |
- cur.close() |
|
83 |
- def db_delete_id(self,user_id) : |
|
84 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
85 |
- cur.execute(f''' |
|
86 |
- delete |
|
87 |
- from "{self.schema}".user_id ui |
|
88 |
- where user_id = '{user_id}' |
|
89 |
- ''') |
|
90 |
- cur.close() |
|
210 |
+ encrypted_phone, phone_iv = user |
|
91 | 211 |
|
92 |
- def db_get_node(self): |
|
93 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
212 |
+ # Decrypt the phone number |
|
213 |
+ decrypted_phone = self.decrypt_aes(encrypted_phone, phone_iv) |
|
94 | 214 |
|
95 |
- cur.execute(''' |
|
96 |
- select "index",source_x ,source_y,target_x,target_y," dist " |
|
97 |
- from "{self.schema}".node n |
|
98 |
- where flcass != 'pedstrian' |
|
99 |
- ''') |
|
100 |
- result=cur.fetchall() |
|
101 |
- |
|
102 |
- return result |
|
103 |
- |
|
104 |
- def db_get_dest(self,dest1): |
|
105 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
106 |
- |
|
107 |
- cur.execute(f''' |
|
108 |
- select j.q,li.q ,li.location_cen_x_4623,li.location_cen_y_4623,li.location_exit_x_4623 ,li.location_exit_y_4623 |
|
109 |
- from "{self.schema}".jibun j, "{self.schema}".location_info li |
|
110 |
- where j.build_num =li.build_code and (j.q='{dest1}' or li.q='{dest1}' or li.build_name_city like '{dest1}') |
|
111 |
- ''') |
|
112 |
- result=cur.fetchone() |
|
113 |
- |
|
114 |
- return (float(result[4]),float(result[5])) |
|
115 |
- |
|
116 |
- def db_get_near_node(self,dest_x,dest_y,value): |
|
117 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
118 |
- |
|
119 |
- cur.execute(f''' |
|
120 |
- select source_x, source_y |
|
121 |
- from "{self.schema}".node n |
|
122 |
- where {dest_x} > source_y - {value} and {dest_x} <= source_y + {value} |
|
123 |
- and {dest_y} > source_x - {value} and {dest_y} <= source_x + {value} |
|
124 |
- |
|
125 |
- ''') |
|
126 |
- result=cur.fetchall() |
|
127 |
- return result |
|
128 |
- |
|
129 |
- def db_get_address(self,dest1): |
|
130 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
131 |
- |
|
132 |
- cur.execute(f''' |
|
133 |
- select j.q,li.q ,li.location_cen_x_4623,li.location_cen_y_4623,li.location_exit_x_4623 ,li.location_exit_y_4623 |
|
134 |
- from "{self.schema}".jibun j, "{self.schema}".location_info li |
|
135 |
- where j.build_num =li.build_code and (j.q='{dest1}' or li.q='{dest1}' or li.build_name_city like '{dest1}') |
|
136 |
- ''') |
|
137 |
- result=cur.fetchone() |
|
138 |
- print( (float(result[2]),float(result[3]))) |
|
139 |
- |
|
140 |
- return (float(result[2]),float(result[3])) |
|
141 |
- |
|
142 |
- def db_add_report(self,report_id,report_x,report_y) : |
|
143 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
144 |
- now=time.localtime() |
|
145 |
- d=time.strftime('%Y-%m-%d %X', now) |
|
146 |
- cur.execute(f''' |
|
147 |
- insert into "{self.schema}".report (report_id,report_x,report_y,timestamp) |
|
148 |
- values ('{report_id}','{report_x}','{report_y}','{d}') |
|
149 |
- ''') |
|
150 |
- |
|
151 |
- def db_get_near_point(self,dest_x,dest_y): |
|
152 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
153 |
- now=datetime.now() |
|
154 |
- d_plus=now +timedelta(hours=1) |
|
155 |
- d_plus=str("'"+d_plus.strftime('%Y-%m-%d %X')+"'") |
|
156 |
- d_minus=now -timedelta(hours=1) |
|
157 |
- d_minus=str("'"+d_minus.strftime('%Y-%m-%d %X')+"'") |
|
158 |
- cur.execute(f''' |
|
159 |
- select report_x, report_y |
|
160 |
- from "{self.schema}".report |
|
161 |
- where {dest_y} > report_y - 0.000498 and {dest_y} <= report_y + 0.000498 |
|
162 |
- and {dest_x} > report_x - 0.000498 and {dest_x} <= report_x + 0.000498 |
|
163 |
- and timestamp between {d_minus} and {d_plus}; |
|
164 |
- |
|
165 |
- |
|
166 |
- ''') |
|
167 |
- result=cur.fetchall() |
|
168 |
- return result |
|
169 |
- |
|
170 |
- |
|
171 |
- def db_add_pothole(self,pothole_id,pothole_location_x,pothole_location_y) : |
|
172 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
173 |
- now=datetime.now() |
|
174 |
- d=now.strftime('%Y-%m-%d %X') |
|
175 |
- cur.execute(f''' |
|
176 |
- insert into "{self.schema}".pothole (pothole_id,pothole_location_x,pothole_location_y,timestamp) |
|
177 |
- values ('{pothole_id}','{pothole_location_x}','{pothole_location_y}','{d}') |
|
178 |
- ''') |
|
179 |
- |
|
180 |
- def db_delete_pothole(self,dest_x,dest_y) : |
|
181 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
182 |
- now=datetime.now() |
|
183 |
- d_plus=now +timedelta(hours=1) |
|
184 |
- d_plus=str("'"+d_plus.strftime('%Y-%m-%d %X')+"'") |
|
185 |
- d_minus=now -timedelta(hours=1) |
|
186 |
- d_minus=str("'"+d_minus.strftime('%Y-%m-%d %X')+"'") |
|
187 |
- cur.execute(f''' |
|
188 |
- delete from "{self.schema}".pothole |
|
189 |
- where {dest_y} > pothole_location_y - 0.000498 and {dest_y} <= pothole_location_y + 0.000498 |
|
190 |
- and {dest_x} > pothole_location_x - 0.000498 and {dest_x} <= pothole_location_x + 0.000498 |
|
191 |
- and timestamp between {d_minus} and {d_plus}; |
|
192 |
- ''') |
|
193 |
- |
|
194 |
- def db_display_pothole(self) : |
|
195 |
- cur = self.conn.cursor() # 커서를 생성한다 |
|
196 |
- cur.execute(f''' |
|
197 |
- select report_x,report_y from "{self.schema}".report |
|
198 |
- ''') |
|
199 |
- result=cur.fetchall() |
|
200 |
- return result |
|
201 |
- |
|
202 |
- def insert_gps_data(self, csv_block, columns): |
|
203 |
- cur = self.conn.cursor() |
|
204 |
- data = StringIO(csv_block) |
|
205 |
- |
|
206 |
- # using COPY instead of INSERT to do even less operation per data. |
|
207 |
- cur.copy_from(data, 'gps_data', sep=',', columns = columns) |
|
208 |
- self.conn.commit() |
|
209 |
- cur.close() |
|
210 |
- return True |
|
215 |
+ return {'status': 'success', 'phone_number': decrypted_phone}, 200 |
|
211 | 216 |
|
212 | 217 |
def close_connection(self): |
213 |
- cur = self.conn.cursor() |
|
218 |
+ cur = self.cur |
|
214 | 219 |
cur.close() |
215 | 220 |
return True |
216 | 221 |
|
+++ database/key_gen.py
... | ... | @@ -0,0 +1,18 @@ |
1 | +import os | |
2 | + | |
3 | +# NEVER be the part of server script, THIS SHOULD NEVER run with server. | |
4 | +# ALSO, remember to BACKUP the key | |
5 | + | |
6 | +def create_and_save_key(key_file_path): | |
7 | + """ | |
8 | + Generates a new AES encryption key and saves it to a file. | |
9 | + """ | |
10 | + key = os.urandom(32) # AES-256 requires a 32-byte key | |
11 | + with open(key_file_path, 'wb') as key_file: | |
12 | + key_file.write(key) | |
13 | + print(f"Encryption key created and saved to {key_file_path}") | |
14 | + return key | |
15 | + | |
16 | +if __name__ == "__main__": | |
17 | + from datetime import datetime | |
18 | + create_and_save_key(f"keys/encryption_key{datetime.now().strftime('%Y-%m-%d_%H:%M:%S')}")(파일 끝에 줄바꿈 문자 없음) |
--- requirements.txt
... | ... | @@ -1,41 +0,0 @@ |
1 | -aniso8601==9.0.1 | |
2 | -attrs==23.2.0 | |
3 | -blinker==1.8.2 | |
4 | -cachelib==0.9.0 | |
5 | -click==8.1.7 | |
6 | -Flask==3.0.3 | |
7 | -Flask-Caching==2.3.0 | |
8 | -flask-restx==1.3.0 | |
9 | -haversine==2.8.1 | |
10 | -imageio==2.34.1 | |
11 | -importlib_resources==6.4.0 | |
12 | -itsdangerous==2.2.0 | |
13 | -Jinja2==3.1.4 | |
14 | -joblib==1.4.2 | |
15 | -jsonschema==4.22.0 | |
16 | -jsonschema-specifications==2023.12.1 | |
17 | -lazy_loader==0.4 | |
18 | -MarkupSafe==2.1.5 | |
19 | -networkx==3.3 | |
20 | -numpy==1.23.3 | |
21 | -opencv-python==4.10.0.82 | |
22 | -packaging==24.1 | |
23 | -pandas==2.2.2 | |
24 | -pillow==10.3.0 | |
25 | -psycopg2-binary==2.9.9 | |
26 | -PyJWT==2.8.0 | |
27 | -python-dateutil==2.9.0.post0 | |
28 | -pytz==2024.1 | |
29 | -referencing==0.35.1 | |
30 | -rpds-py==0.18.1 | |
31 | -scikit-image==0.23.2 | |
32 | -scikit-learn==1.5.0 | |
33 | -scipy==1.13.1 | |
34 | -six==1.16.0 | |
35 | -threadpoolctl==3.5.0 | |
36 | -tifffile==2024.5.22 | |
37 | -torch==1.12.1 | |
38 | -typing_extensions==4.12.2 | |
39 | -tzdata==2024.1 | |
40 | -Werkzeug==3.0.3 | |
41 | - |
--- trip.py
... | ... | @@ -1,78 +0,0 @@ |
1 | -from flask import request | |
2 | -from flask_restx import Resource, Api, Namespace, fields | |
3 | -from navigation_model.model_trip import path_finder | |
4 | -import time | |
5 | -import numpy as np | |
6 | -import networkx | |
7 | - | |
8 | -todos = {} | |
9 | -count = 1 | |
10 | - | |
11 | -not_in_list=[] | |
12 | - | |
13 | - | |
14 | -#trip = path_finder() | |
15 | - | |
16 | - | |
17 | - | |
18 | -Trip = Namespace( | |
19 | - name="trip", | |
20 | - description="경로 노드를 받기위한 사용하는 API.", | |
21 | -) | |
22 | - | |
23 | -trip_fields = Trip.model('Trip', { # Model 객체 생성 | |
24 | - 'path_start': fields.String(description='a Todo', required=True),'path_end' : fields.String(description='a Todo', required=True) | |
25 | -}) | |
26 | - | |
27 | - | |
28 | - | |
29 | -@Trip.route('/trip') | |
30 | -class TripPost(Resource): | |
31 | - @Trip.expect(trip_fields) | |
32 | - @Trip.response(201, 'Success', trip_fields) | |
33 | - def post(self): | |
34 | - """경로를 받습니다""" | |
35 | - start_time=time.time() | |
36 | - start = request.json['path_start'] | |
37 | - end = request.json['path_end'] | |
38 | - | |
39 | - return { | |
40 | - 'nodes' : trip.get_trip(start,end), | |
41 | - 'start_point' : trip.get_dest(start), | |
42 | - 'end_point' : trip.get_dest(end) | |
43 | - | |
44 | - | |
45 | - }, 201 | |
46 | -@Trip.route('/trip2') | |
47 | -class TripPost(Resource): | |
48 | - @Trip.expect(trip_fields) | |
49 | - @Trip.response(201, 'Success', trip_fields) | |
50 | - def post(self): | |
51 | - """경로를 받습니다""" | |
52 | - start_time=time.time() | |
53 | - start_x = request.json['dest1_x'] | |
54 | - start_y = request.json['dest1_y'] | |
55 | - end = request.json['path_end'] | |
56 | - return { | |
57 | - 'nodes' : trip.get_trip_2(start_x,start_y,end,not_in_list), | |
58 | - 'start_point' : (start_x,start_y), | |
59 | - 'end_point' : trip.get_dest(end) | |
60 | - | |
61 | - | |
62 | - }, 201 | |
63 | - | |
64 | - | |
65 | -@Trip.route('/remove') | |
66 | -class removenodePost(Resource): | |
67 | - def post(self): | |
68 | - """경로를 받습니다""" | |
69 | - gps_x = request.json['gps_x'] | |
70 | - gps_y = request.json['gps_y'] | |
71 | - trip.G.remove_node((float(gps_x),float(gps_y))) | |
72 | - not_in_list.append((float(gps_x),float(gps_y))) | |
73 | - | |
74 | - return { | |
75 | - "done" : "done" | |
76 | - }, 201 | |
77 | - | |
78 | - |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?