Linh Vuu commited on
Commit
c44d66d
1 Parent(s): e1447d1

added files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .DS_Store +0 -0
  2. .gitignore +3 -0
  3. Flow/test_flow.html +12 -0
  4. Flow/testing tool_flow.drawio +1 -0
  5. Flow/testing tool_flow.jpeg +0 -0
  6. Flow/user_flow.drawio +1 -0
  7. Flow/user_flow.html +12 -0
  8. Flow/user_flow.jpeg +0 -0
  9. Mockup/.DS_Store +0 -0
  10. Mockup/Login.png +0 -0
  11. Mockup/Testing tool/1. Select Use Case.png +0 -0
  12. Mockup/Testing tool/2. Use Case Selected.png +0 -0
  13. Mockup/Testing tool/3. Upload Photo.png +0 -0
  14. Mockup/Testing tool/4. Passed.png +0 -0
  15. Mockup/Testing tool/5. Failed.png +0 -0
  16. Mockup/User/.DS_Store +0 -0
  17. Mockup/User/1. TransferMoney.png +0 -0
  18. Mockup/User/2. OTP.png +0 -0
  19. Mockup/User/3. UploadPhoto.png +0 -0
  20. Mockup/User/4. Passed.png +0 -0
  21. Mockup/User/5. Failed.png +0 -0
  22. README.md +2 -2
  23. app.py +158 -0
  24. baam_functions.py +766 -0
  25. backup-data.db +0 -0
  26. data.db +0 -0
  27. demo_user_flow.jpg +0 -0
  28. demo_user_profile.jpg +0 -0
  29. dummy_db.csv +0 -0
  30. face_verification/.DS_Store +0 -0
  31. face_verification/image/.DS_Store +0 -0
  32. face_verification/image/charlie-puth.jpg +0 -0
  33. face_verification/image/iu.jpeg +0 -0
  34. face_verification/image/lilitran2.JPG +0 -0
  35. face_verification/image/linhvuu.jpg +0 -0
  36. face_verification/image/linhvuu2.png +0 -0
  37. face_verification/image/linhvuu3.JPG +0 -0
  38. face_verification/test_deepface.ipynb +0 -0
  39. face_verification_helper.py +25 -0
  40. img/.DS_Store +0 -0
  41. img/Standard_Chartered.png +0 -0
  42. img/TestFail.png +0 -0
  43. img/TestPass.png +0 -0
  44. img/bank_sidebar.png +0 -0
  45. img/failed.png +0 -0
  46. img/failed_da.png +0 -0
  47. img/home.png +0 -0
  48. img/logo.png +0 -0
  49. img/menu.png +0 -0
  50. img/otp.jpg +0 -0
.DS_Store ADDED
Binary file (10.2 kB). View file
 
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ .streamlit
2
+ __pycache__
3
+ myenv
Flow/test_flow.html ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=5,IE=9" ><![endif]-->
2
+ <!DOCTYPE html>
3
+ <html>
4
+ <head>
5
+ <title>test_flow.html</title>
6
+ <meta charset="utf-8"/>
7
+ </head>
8
+ <body>
9
+ <div class="mxgraph" style="max-width:100%;border:1px solid transparent;" data-mxgraph="{&quot;highlight&quot;:&quot;#0000ff&quot;,&quot;nav&quot;:true,&quot;resize&quot;:true,&quot;xml&quot;:&quot;&lt;mxfile host=\&quot;app.diagrams.net\&quot; modified=\&quot;2022-09-30T10:28:53.723Z\&quot; agent=\&quot;5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36\&quot; etag=\&quot;iMfvbQ_fRjlVJwybjWiR\&quot; version=\&quot;20.3.6\&quot; type=\&quot;device\&quot;&gt;&lt;diagram id=\&quot;EapV6e3vjfq9qviyezT-\&quot; name=\&quot;Page-1\&quot;&gt;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&lt;/diagram&gt;&lt;/mxfile&gt;&quot;,&quot;toolbar&quot;:&quot;pages zoom layers lightbox&quot;,&quot;page&quot;:0}"></div>
10
+ <script type="text/javascript" src="https://app.diagrams.net/js/viewer-static.min.js"></script>
11
+ </body>
12
+ </html>
Flow/testing tool_flow.drawio ADDED
@@ -0,0 +1 @@
 
 
1
+ <mxfile host="app.diagrams.net" modified="2022-09-25T07:34:59.322Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" etag="UO32HiYLzA1FkrK7jH-4" version="20.3.6" type="device"><diagram id="EapV6e3vjfq9qviyezT-" name="Page-1">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</diagram></mxfile>
Flow/testing tool_flow.jpeg ADDED
Flow/user_flow.drawio ADDED
@@ -0,0 +1 @@
 
 
1
+ <mxfile host="app.diagrams.net" modified="2022-09-25T07:37:24.734Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" etag="4rpihqY8uK4PeHr6fhQx" version="20.3.6" type="device"><diagram id="EapV6e3vjfq9qviyezT-" name="Page-1">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</diagram></mxfile>
Flow/user_flow.html ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=5,IE=9" ><![endif]-->
2
+ <!DOCTYPE html>
3
+ <html>
4
+ <head>
5
+ <title>user_flow.html</title>
6
+ <meta charset="utf-8"/>
7
+ </head>
8
+ <body>
9
+ <div class="mxgraph" style="max-width:100%;border:1px solid transparent;" data-mxgraph="{&quot;highlight&quot;:&quot;#0000ff&quot;,&quot;nav&quot;:true,&quot;resize&quot;:true,&quot;xml&quot;:&quot;&lt;mxfile host=\&quot;app.diagrams.net\&quot; modified=\&quot;2022-09-30T10:29:58.007Z\&quot; agent=\&quot;5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36\&quot; etag=\&quot;7vlyL5OUrrYoEm0dAe5a\&quot; version=\&quot;20.3.6\&quot; type=\&quot;device\&quot;&gt;&lt;diagram id=\&quot;EapV6e3vjfq9qviyezT-\&quot; name=\&quot;Page-1\&quot;&gt;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&lt;/diagram&gt;&lt;/mxfile&gt;&quot;,&quot;toolbar&quot;:&quot;pages zoom layers lightbox&quot;,&quot;page&quot;:0}"></div>
10
+ <script type="text/javascript" src="https://app.diagrams.net/js/viewer-static.min.js"></script>
11
+ </body>
12
+ </html>
Flow/user_flow.jpeg ADDED
Mockup/.DS_Store ADDED
Binary file (6.15 kB). View file
 
Mockup/Login.png ADDED
Mockup/Testing tool/1. Select Use Case.png ADDED
Mockup/Testing tool/2. Use Case Selected.png ADDED
Mockup/Testing tool/3. Upload Photo.png ADDED
Mockup/Testing tool/4. Passed.png ADDED
Mockup/Testing tool/5. Failed.png ADDED
Mockup/User/.DS_Store ADDED
Binary file (6.15 kB). View file
 
Mockup/User/1. TransferMoney.png ADDED
Mockup/User/2. OTP.png ADDED
Mockup/User/3. UploadPhoto.png ADDED
Mockup/User/4. Passed.png ADDED
Mockup/User/5. Failed.png ADDED
README.md CHANGED
@@ -1,7 +1,7 @@
1
  ---
2
  title: Banking Advanced Authentication System
3
- emoji: 🌍
4
- colorFrom: purple
5
  colorTo: yellow
6
  sdk: streamlit
7
  sdk_version: 1.31.0
 
1
  ---
2
  title: Banking Advanced Authentication System
3
+ emoji: 🏦
4
+ colorFrom: pink
5
  colorTo: yellow
6
  sdk: streamlit
7
  sdk_version: 1.31.0
app.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Import libraries
2
+ import os
3
+ import streamlit as st
4
+ from PIL import Image
5
+ from streamlit_extras.switch_page_button import switch_page
6
+ from baam_functions import *
7
+ from datetime import datetime
8
+ from bokeh.models.widgets import Button
9
+ from bokeh.models import CustomJS
10
+ from streamlit_bokeh_events import streamlit_bokeh_events
11
+
12
+ # Set direction as current folder
13
+ sourceFileDir = os.path.dirname(os.path.abspath(__file__))
14
+ os.chdir(sourceFileDir)
15
+
16
+ logo = Image.open('img/logo.png')
17
+ st.set_page_config(page_title = "BAAM", page_icon = logo)
18
+
19
+ # Initialise variables to pass between pages
20
+ st.session_state['user_dict'] = {}
21
+ st.session_state['verification'] = False
22
+ st.session_state['location'] = ""
23
+ st.session_state['raw_location'] = ""
24
+ st.session_state['username'] = ""
25
+ # st.session_state['typing_speed'] = 0
26
+
27
+ def main():
28
+ """Banking Advanced Authentication System"""
29
+
30
+ menu = ["Home", "SignUp", "Login"]
31
+ choice = st.sidebar.selectbox("Menu", menu)
32
+
33
+ if choice == "Home":
34
+ st.subheader("Banking Advanced Authentication Module")
35
+ st.image("img/home.png")
36
+ st.write("Please select one action from the Menu on the side bar. Thank you.")
37
+ st.image("img/menu.png")
38
+
39
+ elif choice == "SignUp":
40
+ st.subheader("Create New Account")
41
+ new_user = st.text_input("Username")
42
+ new_password = st.text_input("Password",type='password')
43
+
44
+ if st.button("Signup"):
45
+ create_user_table()
46
+ add_user_data(new_user,make_hashes(new_password))
47
+ st.success("You have successfully created a valid Account")
48
+ st.info("Go to Login Menu to login")
49
+
50
+ elif choice == "Login":
51
+
52
+ # Header of the page
53
+ col1, col2, col3 = st.columns(3)
54
+ with col1:
55
+ st.write(' ')
56
+
57
+ # Logo
58
+ with col2:
59
+ st.image("img/Standard_Chartered.png", width=175)
60
+ with col3:
61
+ st.write(' ')
62
+
63
+ # Login form
64
+ st.subheader("Login")
65
+ username = st.text_input("Username")
66
+ password = st.text_input("Password",type='password')
67
+
68
+ is_tester = st.checkbox('Login to test')
69
+ login_button = Button(label="Login")
70
+
71
+ # Collect location when login button is clicked
72
+ login_button.js_on_event("button_click", CustomJS(code="""
73
+ navigator.geolocation.getCurrentPosition(
74
+ (loc) => {
75
+ document.dispatchEvent(new CustomEvent("GET_LOCATION", {detail: {lat: loc.coords.latitude, lon: loc.coords.longitude}}))
76
+ }
77
+ )
78
+ """))
79
+
80
+ # Get location
81
+ location = streamlit_bokeh_events(
82
+ login_button,
83
+ events="GET_LOCATION",
84
+ key="get_location",
85
+ refresh_on_update=False,
86
+ override_height=75,
87
+ debounce_time=0)
88
+
89
+ # If can get location
90
+ if location:
91
+
92
+ # Hash password & verify password
93
+ hashed_pswd = make_hashes(password)
94
+ result = login_user(username,check_hashes(password, hashed_pswd))
95
+
96
+ # When the username and password are correct
97
+ if result:
98
+
99
+ # time_start = st.session_state['time_start']
100
+
101
+ # Stop counting time after the user finished entering username, password and clicked login button
102
+ login_time = datetime.now()
103
+
104
+ # # *** Calculate the login duration in seconds (the time it takes user to enter username, password and click login button)
105
+ # login_duration = login_time - time_start
106
+ # login_duration_seconds = login_duration.total_seconds()
107
+
108
+ # # Calculate typing speed (characters per minute)
109
+ # typing_speed = (len(username) + len(password)) * 60 / login_duration_seconds
110
+
111
+ # Launch app for end user
112
+ if not(is_tester):
113
+
114
+ # Collect user's information when logging in
115
+ #user_dict, st.session_state['location'] = collect_data(username, location, login_time, typing_speed)
116
+ user_dict, st.session_state['location'] = collect_data(username, location, login_time)
117
+
118
+ # Store user information to pass to other pages
119
+ st.session_state['user_dict'] = user_dict
120
+ verification = verify_user(user_dict)
121
+
122
+ # THAO LE CODE:
123
+ # weight edit in logic_tl
124
+ weight = {'device_uuid': 40, 'mac_address': 40, 'device_name': 30.0, 'device_model': 20.0, \
125
+ 'device_vendor': 4.0, 'ip_v4': 30, 'isp_name': 15.0, 'ip_country': 3.0, 'suburd': 30, 'district': 22.5, \
126
+ 'city': 15.0, 'country': 3.0}
127
+ # ThaoLe note:
128
+ # st.write(f"print the user_dict {user_dict}")
129
+ # st.write(f'type of user_dict {type(user_dict)}')
130
+ # end not
131
+
132
+ st.session_state['verification'] = verification
133
+
134
+ # Add to the login database after successful verification
135
+ if verification:
136
+ add_login_data(user_dict)
137
+
138
+ # Open Transfer Money Page
139
+ switch_page("Transfer Money 💸")
140
+
141
+ # Launch Testing Tool
142
+ else:
143
+ st.session_state['raw_location'] = location
144
+ st.session_state['username'] = username
145
+ # st.session_state['typing_speed'] = typing_speed
146
+ switch_page("SelectUseCase")
147
+
148
+ else:
149
+ st.warning("Incorrect Username/Password")
150
+
151
+ # Start counting time
152
+ st.session_state['time_start'] = datetime.now()
153
+
154
+ # #To export database to csv file (can be commented out if not needed)
155
+ # export_csv()
156
+
157
+ if __name__ == '__main__':
158
+ main()
baam_functions.py ADDED
@@ -0,0 +1,766 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Import libraries
2
+ import os
3
+ import streamlit as st
4
+ import pandas as pd
5
+ import platform, uuid, psutil
6
+ import requests
7
+ import json
8
+ from requests import get
9
+ from geopy.geocoders import Nominatim
10
+ from getmac import get_mac_address as gma
11
+ from pathlib import Path
12
+ from streamlit_extras.switch_page_button import switch_page
13
+
14
+ from geopy.distance import geodesic as GD
15
+ from datetime import datetime, timedelta
16
+ import matplotlib
17
+ # from face_verification_helper import face_verfication
18
+
19
+ # Security
20
+ #passlib,hashlib,bcrypt,scrypt
21
+ import hashlib
22
+
23
+ # DB Management
24
+ import sqlite3
25
+
26
+ def make_hashes(password):
27
+ return hashlib.sha256(str.encode(password)).hexdigest()
28
+
29
+ def check_hashes(password, hashed_text):
30
+ if make_hashes(password) == hashed_text:
31
+ return hashed_text
32
+ return False
33
+
34
+ # DB Functions
35
+ # Create table store username and password
36
+ def create_user_table():
37
+
38
+ # Access database
39
+ conn = sqlite3.connect('data.db')
40
+ c = conn.cursor()
41
+ c.execute('CREATE TABLE IF NOT EXISTS users(user_id INTEGER PRIMARY KEY AUTOINCREMENT,\
42
+ username TEXT NOT NULL, password TEXT NOT NULL)')
43
+ c.close()
44
+
45
+ def add_user_data(username, password):
46
+
47
+ # Access database
48
+ conn = sqlite3.connect('data.db')
49
+ c = conn.cursor()
50
+ c.execute('INSERT INTO users(username, password) VALUES (?,?)',(username,password))
51
+ conn.commit()
52
+ c.close()
53
+
54
+ def login_user(username, password):
55
+
56
+ # Access database
57
+ conn = sqlite3.connect('data.db')
58
+ c = conn.cursor()
59
+ c.execute('SELECT * FROM users WHERE username =? AND password = ?',(username,password))
60
+ data = c.fetchall()
61
+ c.close()
62
+ return data
63
+
64
+ # def view_all_users():
65
+
66
+ # Access database
67
+ # conn = sqlite3.connect('data.db')
68
+ # c = conn.cursor()
69
+ # c.execute('SELECT * FROM users')
70
+ # data = c.fetchall()
71
+ # c.close()
72
+ # return data
73
+
74
+ # Export data to CSV
75
+ def export_csv():
76
+
77
+ # Access database
78
+ conn = sqlite3.connect('data.db')
79
+
80
+ # Export table login
81
+ db_df = pd.read_sql_query('SELECT * FROM login', conn)
82
+ db_df.to_csv('login.csv', index=False)
83
+
84
+ # Export table users
85
+ db_df = pd.read_sql_query('SELECT * FROM users', conn)
86
+ db_df.to_csv('users.csv', index=False)
87
+
88
+ # Create table to store login data
89
+ def create_login_table():
90
+ # c.execute('DROP TABLE login')
91
+
92
+ # Access database
93
+ conn = sqlite3.connect('data.db')
94
+ c = conn.cursor()
95
+
96
+ c.execute('CREATE TABLE IF NOT EXISTS login(login_id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL,\
97
+ login_time TEXT NOT NULL,\
98
+ device_name TEXT, device_uuid TEXT, mac_address TEXT, device_vendor TEXT, device_model TEXT, device_ram TEXT,\
99
+ ip_v4 TEXT, ip_country TEXT, ip_region TEXT, ip_city TEXT, ip_lat TEXT, ip_lon TEXT, isp_name TEXT, isp_org TEXT,\
100
+ is_vpn TEXT, is_proxy TEXT, is_tor TEXT, is_relay TEXT,\
101
+ lat TEXT, lon TEXT, suburb TEXT, district TEXT, city TEXT, country TEXT)')
102
+
103
+ c.close()
104
+
105
+ # Add login data to database
106
+ def add_login_data(user_dict):
107
+
108
+ # Create login table if not existed
109
+ create_login_table()
110
+
111
+ # Get data from user dictionary
112
+ username, login_time,\
113
+ device_name, device_uuid, mac_address, device_vendor, device_model, device_ram,\
114
+ ip_v4, ip_country, ip_region, ip_city, ip_lat, ip_lon, isp_name, isp_org,\
115
+ is_vpn, is_proxy, is_tor, is_relay,\
116
+ lat, lon, suburb, district, city, country= get_from_user_dict(user_dict)
117
+
118
+ # Access database
119
+ conn = sqlite3.connect('data.db')
120
+ c = conn.cursor()
121
+
122
+ # Create table to store login information if not existed
123
+ c.execute('INSERT INTO login(username, login_time,\
124
+ device_name, device_uuid, mac_address, device_vendor, device_model, device_ram,\
125
+ ip_v4, ip_country, ip_region, ip_city, ip_lat, ip_lon, isp_name, isp_org,\
126
+ is_vpn, is_proxy, is_tor, is_relay,\
127
+ lat, lon, suburb, district, city, country)\
128
+ VALUES (?, ?,\
129
+ ?, ?, ?, ?, ?, ?,\
130
+ ?, ?, ?, ?, ?, ?, ?, ?,\
131
+ ?, ?, ?, ?,\
132
+ ?, ?, ?, ?, ?, ?)',\
133
+ (username, login_time,\
134
+ device_name, device_uuid, mac_address, device_vendor, device_model, device_ram,\
135
+ ip_v4, ip_country, ip_region, ip_city, ip_lat, ip_lon, isp_name, isp_org,\
136
+ is_vpn, is_proxy, is_tor, is_relay,\
137
+ lat, lon, suburb, district, city, country))
138
+
139
+ conn.commit()
140
+ c.close()
141
+
142
+ # Get login data of the user
143
+ def get_login_data(username):
144
+
145
+ # Create login table if not existed
146
+ create_login_table()
147
+
148
+ # Access database
149
+ conn = sqlite3.connect('data.db')
150
+ c = conn.cursor()
151
+
152
+ c.execute('SELECT * FROM login WHERE username = ?',(username,))
153
+ records = c.fetchall()
154
+ c.close()
155
+
156
+ return records
157
+
158
+ # Get data from user dictionary
159
+ def get_from_user_dict(user_dict):
160
+
161
+ username = user_dict.get('username', '')
162
+ login_time = user_dict.get('login_time', '')
163
+ # typing_speed = user_dict.get('typing_speed', '')
164
+ device_name = user_dict.get('device_name', '')
165
+ device_uuid = user_dict.get('device_uuid', '')
166
+ mac_address = user_dict.get('mac_address', '')
167
+ device_vendor = user_dict.get('device_vendor', '')
168
+ device_model = user_dict.get('device_model', '')
169
+ device_ram = user_dict.get('device_ram', '')
170
+ ip_v4 = user_dict.get('ip_v4', '')
171
+ ip_country = user_dict.get('ip_country', '')
172
+ ip_region = user_dict.get('ip_region', '')
173
+ ip_city = user_dict.get('ip_city', '')
174
+ ip_lat = user_dict.get('ip_lat', '')
175
+ ip_lon = user_dict.get('ip_lon', '')
176
+ isp_name = user_dict.get('isp_name', '')
177
+ isp_org = user_dict.get('isp_org', '')
178
+ is_vpn = user_dict.get('is_vpn', '')
179
+ is_proxy = user_dict.get('is_proxy', '')
180
+ is_tor = user_dict.get('is_tor', '')
181
+ is_relay = user_dict.get('is_relay', '')
182
+ lat = user_dict.get('lat', '')
183
+ lon = user_dict.get('lon', '')
184
+ suburb = user_dict.get('suburb', '')
185
+ district = user_dict.get('district', '')
186
+ city = user_dict.get('city', '')
187
+ country = user_dict.get('country', '')
188
+
189
+
190
+ return username, login_time,\
191
+ device_name, device_uuid, mac_address, device_vendor, device_model, device_ram,\
192
+ ip_v4, ip_country, ip_region, ip_city, ip_lat, ip_lon, isp_name, isp_org,\
193
+ is_vpn, is_proxy, is_tor, is_relay,\
194
+ lat, lon, suburb, district, city, country
195
+
196
+ def get_from_api(url, value=""):
197
+
198
+ # Use get method to fetch details from URL API
199
+ response = get(url + value)
200
+ if response.status_code != 200:
201
+ raise Exception("[!] Invalid request!")
202
+
203
+ return response.content.decode()
204
+
205
+ def get_ip_info(ip_v4):
206
+
207
+ # Get information from the ipv4
208
+ isp = get_from_api("http://ip-api.com/json/", ip_v4)
209
+
210
+ # Convert dictionary string to dictionary
211
+ isp = json.loads(isp)
212
+
213
+ # Get information from the dictionary
214
+ ip_country = isp["country"]
215
+ ip_region = isp["regionName"]
216
+ ip_city = isp["city"]
217
+ ip_lat = isp["lat"]
218
+ ip_lon = isp["lon"]
219
+ isp_name = isp["isp"]
220
+ isp_org = isp["org"]
221
+
222
+ # Detect VPN / proxy / tor
223
+ vpn_api_key = st.secrets["vpn_api_key"]
224
+ response = requests.get("https://vpnapi.io/api/" + ip_v4 + "?key=" + vpn_api_key)
225
+ data = json.loads(response.text)
226
+
227
+ is_vpn = data["security"]['vpn']
228
+ is_proxy = data["security"]['proxy']
229
+ is_tor = data["security"]['tor']
230
+ is_relay = data["security"]['relay']
231
+
232
+ return ip_country, ip_region, ip_city, ip_lat, ip_lon, isp_name, isp_org, is_vpn, is_proxy, is_tor, is_relay
233
+
234
+ def get_location(lat, lon):
235
+
236
+ suburb = ''
237
+ district = ''
238
+ city = ''
239
+ country = ''
240
+
241
+ # Get address from given coordinate
242
+ geolocator = Nominatim(user_agent="BAAM")
243
+
244
+ location = geolocator.reverse(lat + "," + lon)
245
+ address = location.raw['address']
246
+
247
+ suburb = address.get('suburb', '')
248
+
249
+ if address.get('city_district', ''):
250
+ district = address.get('city_district', '')
251
+ else:
252
+ district = address.get('district', '')
253
+
254
+ city = address.get('city', '')
255
+ country = address.get('country', '')
256
+
257
+ return location, suburb, district, city, country
258
+
259
+ # def collect_data(username, result, login_time, typing_speed):
260
+ def collect_data(username, result, login_time):
261
+
262
+ lat = ''
263
+ lon = ''
264
+ suburb = ''
265
+ district = ''
266
+ city = ''
267
+ country = ''
268
+
269
+ if "GET_LOCATION" in result:
270
+ lat = str(result.get("GET_LOCATION")["lat"])
271
+ lon = str(result.get("GET_LOCATION")["lon"])
272
+
273
+ if lat and lon:
274
+ location, suburb, district, city, country = get_location(lat, lon)
275
+
276
+ # Collect device information
277
+ device_name = platform.node()
278
+ device_uuid = uuid.getnode()
279
+ mac_address = gma()
280
+ device_vendor = get_from_api("https://api.macvendors.com/", mac_address)
281
+ device_model = platform.platform()
282
+ device_ram = str(round(psutil.virtual_memory().total / (1024.0 **3)))+" GB"
283
+
284
+ # Collect IP information
285
+ ip_v4 = get_from_api('https://api.ipify.org')
286
+ ip_country, ip_region, ip_city, ip_lat, ip_lon, isp_name, isp_org, is_vpn, is_proxy, is_tor, is_relay = get_ip_info(ip_v4)
287
+
288
+ user_dict = {
289
+ "username": username,
290
+ "login_time": login_time,
291
+ # "typing_speed": typing_speed,
292
+ "device_name": device_name,
293
+ "device_uuid": device_uuid,
294
+ "mac_address": mac_address,
295
+ "device_vendor": device_vendor,
296
+ "device_model": device_model,
297
+ "device_ram": device_ram,
298
+ "ip_v4": ip_v4,
299
+ "ip_country": ip_country,
300
+ "ip_region": ip_region,
301
+ "ip_city": ip_city,
302
+ "ip_lat": ip_lat,
303
+ "ip_lon": ip_lon,
304
+ "isp_name": isp_name,
305
+ "isp_org": isp_org,
306
+ "is_vpn": is_vpn,
307
+ "is_proxy": is_proxy,
308
+ "is_tor": is_tor,
309
+ "is_relay": is_relay,
310
+ "lat": lat,
311
+ "lon": lon,
312
+ "suburb": suburb,
313
+ "district": district,
314
+ "city": city,
315
+ "country": country
316
+ }
317
+
318
+ return user_dict, str(location)
319
+
320
+ # Retrieve login history of the user
321
+ def get_login_history(username):
322
+
323
+ login_time_history = []
324
+ # typing_speed_history = []
325
+ device_name_history = []
326
+ device_uuid_history = []
327
+ mac_address_history = []
328
+ device_vendor_history = []
329
+ device_model_history = []
330
+ device_ram_history = []
331
+ ip_v4_history = []
332
+ ip_country_history = []
333
+ ip_region_history = []
334
+ ip_city_history = []
335
+ ip_lat_history = []
336
+ ip_lon_history = []
337
+ isp_name_history = []
338
+ isp_org_history = []
339
+ is_vpn_history = []
340
+ is_proxy_history = []
341
+ is_tor_history = []
342
+ is_relay_history = []
343
+ lat_history = []
344
+ lon_history = []
345
+ suburb_history = []
346
+ district_history = []
347
+ city_history = []
348
+ country_history = []
349
+
350
+ login_data = get_login_data(username)
351
+
352
+ if login_data:
353
+
354
+ for row in login_data:
355
+ login_time_history.append(row[2])
356
+ # typing_speed_history.append(row[3])
357
+ device_name_history.append(row[3])
358
+ device_uuid_history.append(row[4])
359
+ mac_address_history.append(row[5])
360
+ device_vendor_history.append(row[6])
361
+ device_model_history.append(row[7])
362
+ device_ram_history.append(row[8])
363
+ ip_v4_history.append(row[9])
364
+ ip_country_history.append(row[10])
365
+ ip_region_history.append(row[11])
366
+ ip_city_history.append(row[12])
367
+ ip_lat_history.append(row[13])
368
+ ip_lon_history.append(row[14])
369
+ isp_name_history.append(row[15])
370
+ isp_org_history.append(row[16])
371
+ is_vpn_history.append(row[17])
372
+ is_proxy_history.append(row[18])
373
+ is_tor_history.append(row[19])
374
+ is_relay_history.append(row[20])
375
+ lat_history.append(row[21])
376
+ lon_history.append(row[22])
377
+ suburb_history.append(row[23])
378
+ district_history.append(row[24])
379
+ city_history.append(row[25])
380
+ country_history.append(row[26])
381
+
382
+ return login_time_history,\
383
+ device_name_history, device_uuid_history, mac_address_history, device_vendor_history, device_model_history, device_ram_history,\
384
+ ip_v4_history, ip_country_history, ip_region_history, ip_city_history, ip_lat_history, ip_lon_history, isp_name_history, isp_org_history,\
385
+ is_vpn_history, is_proxy_history, is_tor_history, is_relay_history,\
386
+ lat_history, lon_history, suburb_history, district_history, city_history, country_history
387
+
388
+ def submit_test_case(user_dict, location):
389
+
390
+ submit_button = st.button("Start test case")
391
+
392
+ # When clicking submit button
393
+ if submit_button:
394
+
395
+ # Call function to verify test case with historical data
396
+ verification = verify_user(user_dict)
397
+
398
+ # If fail the user verification logic
399
+ # if not(verification):
400
+
401
+ # # Check face verification
402
+ # verification = verify_face(user_dict.get('username', ''))
403
+
404
+ # Update location, user_dict to pass to other pages
405
+ st.session_state['location'] = location
406
+ st.session_state['user_dict'] = user_dict
407
+ st.session_state['verification'] = verification
408
+
409
+ # If passed all verification logic
410
+ if verification:
411
+
412
+ # Open Sent Page
413
+ switch_page("TestPass")
414
+
415
+ else:
416
+
417
+ # Open Failed Page
418
+ switch_page("InputImage")
419
+
420
+ def show_test_data(user_dict, location):
421
+
422
+ # Current information
423
+ username, login_time,\
424
+ device_name, device_uuid, mac_address, device_vendor, device_model, device_ram,\
425
+ ip_v4, ip_country, ip_region, ip_city, ip_lat, ip_lon, isp_name, isp_org,\
426
+ is_vpn, is_proxy, is_tor, is_relay,\
427
+ lat, lon, suburb, district, city, country = get_from_user_dict(user_dict)
428
+
429
+ # Show location
430
+ st.write('Location:', location)
431
+
432
+ col1, col2 = st.columns(2)
433
+
434
+ with col1:
435
+
436
+ # Show IP IP info
437
+ st.write('IP address:', ip_v4)
438
+ st.write('IP region:', ip_region)
439
+ st.write('IP city:', ip_city)
440
+ st.write('IP country:', ip_country)
441
+ st.write('Is VPN?', is_vpn)
442
+ st.write('Is Proxy?', is_proxy)
443
+ st.write('Is Tor Node?', is_tor)
444
+ st.write('Is Relay?', is_relay)
445
+
446
+ with col2:
447
+
448
+ # Show Device
449
+ st.write('ISP Name:', isp_name)
450
+ st.write('ISP Organisation:', isp_org)
451
+ st.write('Device Mac Address:', mac_address)
452
+ st.write('Device UUID:', device_uuid)
453
+ st.write('Device Name:', device_name)
454
+ st.write('Device Vendor:', device_vendor)
455
+ st.write('Device Model:', device_model)
456
+ st.write('Device Ram:', device_ram)
457
+
458
+ # Show Login time
459
+ st.write('Login time:', login_time)
460
+
461
+ def save_user_image(username, image, input_time):
462
+ save_dir = f"img/user_image/{username}"
463
+ save_file_path = f"img/user_image/{username}/{username}_" \
464
+ f"{int(input_time)}.jpg"
465
+ if not os.path.exists(save_dir):
466
+ os.makedirs(save_dir)
467
+ with open(save_file_path, mode='wb') as w:
468
+ w.write(image.getbuffer())
469
+ return save_file_path
470
+
471
+ def read_user_image(username):
472
+ list_image_path = []
473
+ image_dir = f"img/user_image/{username}"
474
+ if not os.path.exists(image_dir):
475
+ os.makedirs(image_dir)
476
+ for x in os.listdir(image_dir):
477
+ if x.split(".")[1].lower() in ("jpg", "png", "jpeg"):
478
+ image_path = os.path.join(image_dir, x)
479
+ list_image_path.append(image_path)
480
+ if list_image_path:
481
+ return max(list_image_path, key=os.path.getctime)
482
+ else:
483
+ return None
484
+
485
+ # @Thao: Here is to put user historical data verification logic to determine if this is the real user
486
+ # THAO LE CODE
487
+
488
+ # 0.5 to get the more suitable per
489
+ # get all the values with count in range ~20% less than the highest
490
+ def get_right_per(user_dict,user_db,match_value,per):
491
+ if match_value == "":
492
+ return 0
493
+ range_highest = 0.2
494
+ count_all = user_db.groupby(match_value).count().login_time
495
+ up = count_all.max()
496
+ down = up*(1-range_highest)
497
+ # print(f'range is from {down} to {up}')
498
+ if count_all[user_dict[match_value]] >= down and count_all[user_dict[match_value]] <= up:
499
+ return 1
500
+ else:
501
+ return per
502
+
503
+ # 1.1 to get the match value and percentage
504
+ def check_match_per(user_dict,user_db,check = 'location'):
505
+ """
506
+ input the check is one of 'location','device','ip'
507
+ """
508
+ match_value = ''
509
+ final_per = 0
510
+ per = 0
511
+ total_txn = len(user_db)
512
+
513
+ if check == 'location':
514
+ fields_check = ['country','city', 'district','suburb']
515
+ elif check == 'device':
516
+ fields_check = ['device_vendor','device_model','device_name','mac_address','device_uuid']
517
+ else:
518
+ fields_check = [ 'ip_country', 'isp_name','ip_v4']
519
+
520
+ for i in fields_check:
521
+ # print(user_db)
522
+ # print(user_dict[i])
523
+ count = len(user_db[user_db[i] == user_dict[i]])
524
+ if count > 0:
525
+ # if user[i] in user_db[i].values and user[i] != '':
526
+ match_value = i
527
+ per = count/total_txn
528
+
529
+ elif i == 'mac_address' and match_value != i:
530
+ continue
531
+ else:
532
+ break
533
+
534
+ final_per = get_right_per(user_dict,user_db,match_value,per)
535
+ # print('match value ',{match_value})
536
+ return match_value,final_per
537
+
538
+ # 2.1 Get velocity of all transactions
539
+ def get_vel_all(row):
540
+ # print(row)
541
+ dist = 0
542
+ coor = (row['lat'],row['lon'])
543
+ coor_pre = (row['pre_lat'],row['pre_lon'])
544
+ interval = ''
545
+ vel = ''
546
+ if coor_pre != (0,0):
547
+ dist = GD(coor,coor_pre).km
548
+ interval = (row['login_time'] - row['pre_time']).total_seconds()/(60*60)
549
+ # interval = (row['login_time'] - row['pre_time']).days
550
+ if interval != 0:
551
+ vel = dist/interval
552
+ else:
553
+ vel = 0
554
+ return vel
555
+
556
+ # 2.2 get vel of the latest txn
557
+ def get_vel_txn(user_dict,user_db):
558
+ """
559
+ get the velocity of the new transaction in user_dict and the latest transaction in user's history
560
+ """
561
+ dist = 0
562
+ interval = ''
563
+ latest_txn = user_db.iloc[[-1]]
564
+ # print(latest_txn)
565
+ coor_txn = (user_dict['lat'],user_dict['lon'])
566
+ coor_latest = (float(latest_txn['lat']),float(latest_txn['lon']))
567
+ dist = GD(coor_txn,coor_latest).km
568
+
569
+ print(type(user_dict['login_time']))
570
+ try:
571
+ time_txn = user_dict['login_time']
572
+ interval = (time_txn - latest_txn['login_time'].to_list()[0]).total_seconds()/(60*60)
573
+ except:
574
+ time_txn = datetime.strptime(user_dict['login_time'],'%Y-%m-%d %H:%M:%S')
575
+ interval = (time_txn - latest_txn['login_time'].to_list()[0]).total_seconds()/(60*60)
576
+
577
+ # if type(user_dict['login_time']) == 'str':
578
+ # time_txn = datetime.strptime(user_dict['login_time'],'%Y-%m-%d %H:%M:%S')
579
+ # else:
580
+ # time_txn = user_dict['login_time']
581
+
582
+
583
+ # print(latest_txn['login_time'])
584
+ # interval = (time_txn - latest_txn['login_time'].to_list()[0]).total_seconds()/(60*60)
585
+ try:
586
+ vel = dist/interval
587
+ except:
588
+ vel = 0
589
+ print(f'This is vel {vel}')
590
+ return(vel)
591
+
592
+ # 2 to get score will be reduced because of jumping
593
+ def get_score_jump (user_dict,user_db):
594
+ threshold_vel = {
595
+ 'H':600,
596
+ 'M':80,
597
+ 'L':40,
598
+ 'frequency':0.1
599
+ }
600
+ weight_vel = {
601
+ 'H':20,
602
+ 'M':10,
603
+ "L":5
604
+ }
605
+ many_vel = {
606
+ 'Y':0.1,
607
+ 'N':1
608
+ }
609
+
610
+ # to get how many jumping - low or not
611
+ count_jump = len(user_db[user_db.apply(lambda x: float(x.vel) > threshold_vel['L'] if x.vel != "" else False,axis=1)])
612
+
613
+ if count_jump > len(user_db)*threshold_vel['frequency']:
614
+ many_jump = 'Y'
615
+ else:
616
+ many_jump = 'N'
617
+
618
+ vel_txn = float(get_vel_txn(user_dict,user_db))
619
+
620
+ if vel_txn > threshold_vel['H']:
621
+ score_jump = many_vel[many_jump] * weight_vel['H']
622
+ elif vel_txn > threshold_vel['M']:
623
+ score_jump = many_vel[many_jump] * weight_vel['M']
624
+ elif vel_txn > threshold_vel['L']:
625
+ score_jump = many_vel[many_jump] * weight_vel['L']
626
+ else:
627
+ score_jump = 0
628
+ return(score_jump)
629
+
630
+ # 3.1 check vpn (new IP + vpn)
631
+ def get_vpn_score (user_dict,user_db):
632
+ vpn_fields = ['is_vpn','is_proxy', 'is_tor','is_relay']
633
+ weight_vpn = 10
634
+ vpn_count = 0
635
+ for i in vpn_fields:
636
+ vpn_count += user_dict[i]
637
+ if check_match_per(user_dict,user_db,check = 'ip')[0] != 'ip_v4' and vpn_count > 0:
638
+ return weight_vpn
639
+ else:
640
+ return 0
641
+
642
+ # 4.Get score
643
+ def get_risk_score (user_dict,user_db):
644
+ weight = {'device_uuid': 40, 'mac_address': 40, 'device_name': 30.0, 'device_model': 20.0, \
645
+ 'device_vendor': 4.0, 'ip_v4': 30, 'isp_name': 15.0, 'ip_country': 3.0, 'suburb': 30, 'district': 22.5, \
646
+ 'city': 15.0, 'country': 3.0}
647
+ device_match,device_per = check_match_per(user_dict,user_db,check='device')
648
+ if device_match != '':
649
+ device_score = weight[device_match] * device_per
650
+ else:
651
+ device_score = 0
652
+
653
+ ip_match,ip_per = check_match_per(user_dict,user_db,check='ip')
654
+ if ip_match != '':
655
+ ip_score = weight[ip_match] * ip_per
656
+ else:
657
+ ip_score = 0
658
+
659
+ # check location
660
+ location_match,location_per = check_match_per(user_dict,user_db,check='location')
661
+ if location_match != '':
662
+ location_score = weight[location_match] * location_per
663
+ else:
664
+ location_score = 0
665
+ # print(f'match location {location_match} with score {location_score}')
666
+ jump_score = get_score_jump (user_dict,user_db)
667
+ vpn_score = get_vpn_score (user_dict,user_db)
668
+
669
+ print(f'device score {device_score}')
670
+ print(f'ip_score {ip_score}')
671
+ print(f'location_score {location_score}')
672
+ print(f'jump_score {jump_score}')
673
+ print(f'vpn_score {vpn_score}')
674
+
675
+ # return device_score+ip_score+location_score-(jump_score + vpn_score)
676
+ total_score = device_score+ip_score+location_score-(jump_score + vpn_score)
677
+ score_dict = {
678
+ "device_score": device_score,
679
+ "ip_score": ip_score,
680
+ "location_score": location_score,
681
+ "jump_score": jump_score,
682
+ "vpn_score": vpn_score,
683
+ "total_score": total_score
684
+ }
685
+ st.session_state['score_dict'] = score_dict
686
+
687
+ return total_score
688
+
689
+ # User verification
690
+ def verify_user(user_dict):
691
+
692
+ verification = False
693
+
694
+ # Current information
695
+ # user_dict is dictionary
696
+ username, login_time,\
697
+ device_name, device_uuid, mac_address, device_vendor, device_model, device_ram,\
698
+ ip_v4, ip_country, ip_region, ip_city, ip_lat, ip_lon, isp_name, isp_org,\
699
+ is_vpn, is_proxy, is_tor, is_relay,\
700
+ lat, lon, suburb, district, city, country = get_from_user_dict(user_dict)
701
+
702
+ # Retrieve login history of the user - a tuple
703
+ # login_time_history, \
704
+ # device_name_history, device_uuid_history, mac_address_history, device_vendor_history, device_model_history, device_ram_history,\
705
+ # ip_v4_history, ip_country_history, ip_region_history, ip_city_history, ip_lat_history, ip_lon_history, isp_name_history, isp_org_history,\
706
+ # is_vpn_history, is_proxy_history, is_tor_history, is_relay_history,\
707
+ # lat_history, lon_history, suburb_history, district_history, city_history, country_history = get_login_history(username)
708
+
709
+ #Thao Le note:
710
+ col = ['login_time','device_name', 'device_uuid','mac_address', 'device_vendor', 'device_model', 'device_ram',\
711
+ 'ip_v4','ip_country', 'ip_region', 'ip_city', 'ip_lat', 'ip_lon', 'isp_name','isp_org',\
712
+ 'is_vpn', 'is_proxy', 'is_tor', 'is_relay', \
713
+ 'lat', 'lon','suburb', 'district', 'city', 'country']
714
+ df = get_login_history(username)
715
+
716
+ user_db = pd.DataFrame(get_login_history(username)).T
717
+ user_db.columns= col
718
+ # print(f'this is from BAAM function, username is {username}')
719
+ # print('this is user_dict',user_dict)
720
+ if len(user_db) == 0:
721
+ print('This is the 1st login time of this username')
722
+ verification = True
723
+ score_dict = {}
724
+ st.session_state['score_dict'] = score_dict
725
+ return verification
726
+
727
+ # print(user_db)
728
+ # 2. Def to check score_jumping: velocity (H: 600+, M: 80 - 600, S: 40<80) and frequency_jumping (rare or ussually)
729
+ user_db.login_time = pd.to_datetime(user_db.login_time)
730
+ user_db['pre_lat'] = user_db['lat'].shift(periods=1, fill_value=0)
731
+ user_db['pre_lon'] = user_db['lon'].shift(periods=1, fill_value=0)
732
+ user_db['pre_time'] = user_db['login_time'].shift(periods=1,fill_value=0)
733
+ user_db['vel'] = user_db.apply(lambda x: get_vel_all(x),axis=1)
734
+
735
+ trust_score =get_risk_score (user_dict,user_db)
736
+ risk_threshold = 30
737
+ st.session_state['risk_threshold'] = risk_threshold
738
+ print(f'trust_score is {trust_score}')
739
+ if trust_score < risk_threshold:
740
+ verification = False
741
+ else:
742
+ verification = True
743
+ print(f'verification {verification}')
744
+
745
+
746
+ # verification = True # This should be removed after @Thao adds the function user historical data verification
747
+
748
+ return verification
749
+
750
+ # @Dora: This is to put the function face verification
751
+ # Face verification
752
+ def verify_face(username, img_file_buffer):
753
+ face_verification = True
754
+ # input_time = datetime.now().timestamp()
755
+ # latest_history_image = read_user_image(username)
756
+ # if latest_history_image:
757
+ # image_path = save_user_image(username, img_file_buffer, input_time)
758
+ # face_verification = face_verfication([[latest_history_image,image_path]])
759
+ # os.remove(image_path)
760
+ # else:
761
+ # face_verification = True
762
+ # if face_verification:
763
+ # image_path = save_user_image(username, img_file_buffer, input_time)
764
+ # print(f"verify face:{face_verification}")
765
+
766
+ return face_verification
backup-data.db ADDED
Binary file (24.6 kB). View file
 
data.db ADDED
Binary file (238 kB). View file
 
demo_user_flow.jpg ADDED
demo_user_profile.jpg ADDED
dummy_db.csv ADDED
The diff for this file is too large to render. See raw diff
 
face_verification/.DS_Store ADDED
Binary file (6.15 kB). View file
 
face_verification/image/.DS_Store ADDED
Binary file (6.15 kB). View file
 
face_verification/image/charlie-puth.jpg ADDED
face_verification/image/iu.jpeg ADDED
face_verification/image/lilitran2.JPG ADDED
face_verification/image/linhvuu.jpg ADDED
face_verification/image/linhvuu2.png ADDED
face_verification/image/linhvuu3.JPG ADDED
face_verification/test_deepface.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
face_verification_helper.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from deepface import DeepFace
2
+
3
+ DISTANCE_METRIC = "cosine" # cosine, euclidean, euclidean_l2
4
+ MODEL_NAME = 'ArcFace' # VGG-Face, Facenet, OpenFace, DeepFace, DeepID, Dlib, ArcFace or Ensemble
5
+ DETECTOR_BACKEND = "opencv" # Retinaface, mtcnn, opencv, ssd or dlib
6
+
7
+
8
+ def face_verfication(image_array):
9
+ """
10
+ Checking user's face image with user face history images
11
+ If one check is True -> True
12
+ """
13
+ result = False
14
+ verifications = DeepFace.verify(img1_path=image_array,
15
+ model_name=MODEL_NAME,
16
+ distance_metric=DISTANCE_METRIC,
17
+ detector_backend=DETECTOR_BACKEND)
18
+ for k, verification in verifications.items():
19
+ if verification["verified"]:
20
+ result = True
21
+ break
22
+
23
+ return result
24
+
25
+
img/.DS_Store ADDED
Binary file (6.15 kB). View file
 
img/Standard_Chartered.png ADDED
img/TestFail.png ADDED
img/TestPass.png ADDED
img/bank_sidebar.png ADDED
img/failed.png ADDED
img/failed_da.png ADDED
img/home.png ADDED
img/logo.png ADDED
img/menu.png ADDED
img/otp.jpg ADDED