Coretex
user.py
1 # Copyright (C) 2023 Coretex LLC
2 
3 # This file is part of Coretex.ai
4 
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
9 
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Affero General Public License for more details.
14 
15 # You should have received a copy of the GNU Affero General Public License
16 # along with this program. If not, see <https://www.gnu.org/licenses/>.
17 
18 from requests.exceptions import RequestException
19 
20 import requests
21 
22 from . import ui
23 from ...networking import networkManager, NetworkRequestError, baseUrl
24 from ...configuration import UserConfiguration, InvalidConfiguration, ConfigurationNotFound
25 
26 
27 def validateServerUrl(serverUrl: str) -> bool:
28  try:
29  endpoint = baseUrl(serverUrl) + "/info.json"
30  response = requests.get(endpoint, timeout = 5)
31  return response.ok
32  except RequestException:
33  return False
34 
35 
36 def configureServerUrl() -> str:
37  availableChoices = ["Official Coretex Server URL", "Custom Server URL"]
38  selectedChoice = ui.arrowPrompt(availableChoices, "Please select server url that you want to use (use arrow keys to select an option):")
39 
40  if not "Official" in selectedChoice:
41  serverUrl: str = ui.clickPrompt("Enter server url that you wish to use", type = str)
42 
43  while not validateServerUrl(serverUrl):
44  serverUrl = ui.clickPrompt("You've entered invalid server url. Please try again.", type = str)
45 
46  return serverUrl
47 
48  return "https://api.coretex.ai/"
49 
50 
51 def configUser(retryCount: int = 0) -> UserConfiguration:
52  if retryCount >= 3:
53  raise RuntimeError("Failed to authenticate. Terminating...")
54 
55  userConfig = UserConfiguration({}) # create new empty user config
56  userConfig.serverUrl = configureServerUrl()
57  username = ui.clickPrompt("Email", type = str)
58  password = ui.clickPrompt("Password", type = str, hide_input = True)
59 
60  ui.progressEcho("Authenticating...")
61  response = networkManager.authenticate(username, password, False)
62 
63  if response.hasFailed():
64  ui.errorEcho("Failed to authenticate. Please try again...")
65  return configUser(retryCount + 1)
66 
67  jsonResponse = response.getJson(dict)
68  userConfig.username = username
69  userConfig.password = password
70  userConfig.token = jsonResponse["token"]
71  userConfig.tokenExpirationDate = jsonResponse["expires_on"]
72  userConfig.refreshToken = jsonResponse.get("refresh_token")
73  userConfig.refreshTokenExpirationDate = jsonResponse.get("refresh_expires_on")
74 
75  return userConfig
76 
77 
78 def authenticateUser(userConfig: UserConfiguration) -> None:
79  response = networkManager.authenticate(userConfig.username, userConfig.password)
80 
81  if response.statusCode >= 500:
82  raise NetworkRequestError(response, "Something went wrong, please try again later.")
83  elif response.statusCode >= 400:
84  ui.errorEcho(f"Failed to authenticate with stored credentials (Server URL: {userConfig.serverUrl}).")
85  if not ui.clickPrompt("Would you like to reconfigure the user? (Y/n)", type = bool, default = True, show_default = False):
86  raise RuntimeError
87 
88  userConfig.update(configUser())
89  else:
90  jsonResponse = response.getJson(dict)
91  userConfig.token = jsonResponse["token"]
92  userConfig.tokenExpirationDate = jsonResponse["expires_on"]
93  userConfig.refreshToken = jsonResponse.get("refresh_token")
94  userConfig.refreshTokenExpirationDate = jsonResponse.get("refresh_expires_on")
95 
96 
97 def authenticateWithRefreshToken(userConfig: UserConfiguration) -> None:
98  if not isinstance(userConfig.refreshToken, str):
99  raise TypeError(f"Expected \"str\" received \"{type(userConfig.refreshToken)}\"")
100 
101  response = networkManager.authenticateWithRefreshToken(userConfig.refreshToken)
102 
103  if response.statusCode >= 500:
104  raise NetworkRequestError(response, "Something went wrong, please try again later.")
105  elif response.statusCode >= 400:
106  authenticateUser(userConfig)
107  else:
108  jsonResponse = response.getJson(dict)
109  userConfig.token = jsonResponse["token"]
110  userConfig.token = jsonResponse["expires_on"]
111 
112 
113 def initializeUserSession() -> None:
114  try:
115  userConfig = UserConfiguration.load()
116  if userConfig.isTokenValid("token"):
117  return
118 
119  if not userConfig.isTokenValid("token") and userConfig.isTokenValid("refreshToken"):
120  authenticateWithRefreshToken(userConfig)
121  else:
122  authenticateUser(userConfig)
123  except ConfigurationNotFound:
124  ui.errorEcho("User configuration not found.")
125  if not ui.clickPrompt("Would you like to configure the user? (Y/n)", type = bool, default = True, show_default = False):
126  raise
127 
128  userConfig = configUser()
129  except InvalidConfiguration as ex:
130  ui.errorEcho("Invalid user configuration found.")
131  for error in ex.errors:
132  ui.errorEcho(f"{error}")
133 
134  if not ui.clickPrompt("Would you like to reconfigure the user? (Y/n)", type = bool, default = True, show_default = False):
135  raise
136 
137  userConfig = configUser()
138 
139  userConfig.save()