Post Black Linting
This commit is contained in:
+38
-34
@@ -23,7 +23,6 @@ import requests
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
class AirlockAPIWrapper:
|
||||
"""
|
||||
A wrapper class for interacting with the Airlock API.
|
||||
@@ -141,25 +140,25 @@ class AirlockAPIWrapper:
|
||||
payload = {"status": "0"}
|
||||
result = self._post("/v1/otp/usage", payload)
|
||||
return pd.DataFrame(result["response"]["otpusage"])
|
||||
|
||||
|
||||
def otp_find_enforced(self) -> pd.DataFrame:
|
||||
"""Find OTPs that are awaiting activation."""
|
||||
payload = {"status": "2"}
|
||||
result = self._post("/v1/otp/usage", payload)
|
||||
return pd.DataFrame(result["response"]["otpusage"])
|
||||
|
||||
|
||||
def otp_find_revoked(self) -> pd.DataFrame:
|
||||
"""Find OTPs that are awaiting activation."""
|
||||
payload = {"status": "3"}
|
||||
result = self._post("/v1/otp/usage", payload)
|
||||
return pd.DataFrame(result["response"]["otpusage"])
|
||||
|
||||
|
||||
def otp_find_by_agent(self, agentid) -> pd.DataFrame:
|
||||
"""Find OTP by agent."""
|
||||
payload = {"agentid": agentid}
|
||||
result = self._post("/v1/otp/usage", payload)
|
||||
return pd.DataFrame(result["response"]["otpusage"])
|
||||
|
||||
|
||||
def otp_generate(self, agentid: str, duration: int, purpose: str) -> str:
|
||||
"""Generate a new OTP for an agent."""
|
||||
payload = {
|
||||
@@ -175,7 +174,7 @@ class AirlockAPIWrapper:
|
||||
payload = {"otpid": otpid}
|
||||
result = self._post("/v1/otp/activities", payload)
|
||||
return pd.DataFrame(result["response"]["otpactivities"])
|
||||
|
||||
|
||||
def otp_revoke(self, otpid: str) -> dict:
|
||||
"""
|
||||
Revoke an active OTP.
|
||||
@@ -186,18 +185,17 @@ class AirlockAPIWrapper:
|
||||
"""
|
||||
payload = {"otpid": otpid}
|
||||
return self._post("/v1/otp/revoke", payload)
|
||||
|
||||
def otp_validate(self, otpcode: str) -> dict:
|
||||
"""
|
||||
Validate an OTP code.
|
||||
Parameters:
|
||||
- otpcode (str): The OTP code to validate.
|
||||
Returns:
|
||||
- dict: JSON response indicating validity.
|
||||
"""
|
||||
payload = {"otpcode": otpcode}
|
||||
return self._post("/v1/otp/validate", payload)
|
||||
|
||||
def otp_validate(self, otpcode: str) -> dict:
|
||||
"""
|
||||
Validate an OTP code.
|
||||
Parameters:
|
||||
- otpcode (str): The OTP code to validate.
|
||||
Returns:
|
||||
- dict: JSON response indicating validity.
|
||||
"""
|
||||
payload = {"otpcode": otpcode}
|
||||
return self._post("/v1/otp/validate", payload)
|
||||
|
||||
# Policy Management
|
||||
def policy_add_path_exclusions(self, groupid: str, paths: List[str]) -> dict:
|
||||
@@ -225,7 +223,7 @@ class AirlockAPIWrapper:
|
||||
payload = {"groupid": groupid}
|
||||
result = self._post("/v1/group/agents", payload)
|
||||
return pd.DataFrame(result["response"]["agents"])
|
||||
|
||||
|
||||
def policy_list_allowlists(self, groupid: str) -> pd.DataFrame:
|
||||
"""List allowlists assigned to a specific policy group."""
|
||||
payload = {"groupid": groupid}
|
||||
@@ -236,31 +234,37 @@ class AirlockAPIWrapper:
|
||||
"""Set audit mode for a policy group. 1=Audit, 0=Enforcement"""
|
||||
payload = {"groupid": groupid, "auditmode": auditmode}
|
||||
return self._post("/v1/group/settings/auditmode", payload)
|
||||
|
||||
def policy_set_script_custom(self,
|
||||
groupid: str,
|
||||
script_custom: int,
|
||||
scripts_audit: List[str],
|
||||
scripts_disabled: List[str],
|
||||
scripts_respect: List[str],
|
||||
) -> dict:
|
||||
|
||||
def policy_set_script_custom(
|
||||
self,
|
||||
groupid: str,
|
||||
script_custom: int,
|
||||
scripts_audit: List[str],
|
||||
scripts_disabled: List[str],
|
||||
scripts_respect: List[str],
|
||||
) -> dict:
|
||||
"""Set audit mode for a policy group. 1=Audit, 0=Enforcement"""
|
||||
payload = {"groupid": groupid,
|
||||
"script_custom": script_custom,
|
||||
"scripts_audit": scripts_audit,
|
||||
"scripts_disabled": scripts_disabled,
|
||||
"scripts_respect": scripts_respect
|
||||
}
|
||||
payload = {
|
||||
"groupid": groupid,
|
||||
"script_custom": script_custom,
|
||||
"scripts_audit": scripts_audit,
|
||||
"scripts_disabled": scripts_disabled,
|
||||
"scripts_respect": scripts_respect,
|
||||
}
|
||||
return self._post("/v1/group/settings/script_custom", payload)
|
||||
|
||||
# Execution History
|
||||
def history_logging(self, type: List[str], checkpoint: str, policy: List[str]) -> str:
|
||||
def history_logging(
|
||||
self, type: List[str], checkpoint: str, policy: List[str]
|
||||
) -> str:
|
||||
"""Retrieve execution history logs."""
|
||||
payload = {"type": type, "checkpoint": checkpoint, "policy": policy}
|
||||
result = self._post("/v1/logging/exechistories", payload)
|
||||
return result["response"]["exechistories"]
|
||||
|
||||
def history_execution(self, today: str, date_selected: str, agent_name: str) -> List[Dict]:
|
||||
def history_execution(
|
||||
self, today: str, date_selected: str, agent_name: str
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
Retrieve execution history logs.
|
||||
|
||||
|
||||
+93
-28
@@ -47,7 +47,9 @@ def devicehistory(api: AirlockAPIWrapper, outputjson: bool):
|
||||
print(colorText("No agents selected or invalid history range.", "red"))
|
||||
return
|
||||
|
||||
historical_date = (datetime.now() - timedelta(days=history_days)).strftime("%Y-%m-%d")
|
||||
historical_date = (datetime.now() - timedelta(days=history_days)).strftime(
|
||||
"%Y-%m-%d"
|
||||
)
|
||||
today = datetime.now().strftime("%Y-%m-%d")
|
||||
|
||||
all_history = []
|
||||
@@ -56,7 +58,11 @@ def devicehistory(api: AirlockAPIWrapper, outputjson: bool):
|
||||
try:
|
||||
exechistory = api.history_execution(today, historical_date, agent.hostname)
|
||||
except Exception as e:
|
||||
print(colorText(f"❌ Error retrieving history for {agent.hostname}: {e}", "red"))
|
||||
print(
|
||||
colorText(
|
||||
f"❌ Error retrieving history for {agent.hostname}: {e}", "red"
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
if isinstance(exechistory, list):
|
||||
@@ -76,7 +82,9 @@ def devicehistory(api: AirlockAPIWrapper, outputjson: bool):
|
||||
print(colorText(f"{key}: {value}", "green"))
|
||||
print("\n")
|
||||
else:
|
||||
print(colorText(f"No execution history found for {agent.hostname}.", "yellow"))
|
||||
print(
|
||||
colorText(f"No execution history found for {agent.hostname}.", "yellow")
|
||||
)
|
||||
|
||||
if outputjson:
|
||||
print(json.dumps(all_history, indent=2))
|
||||
@@ -92,6 +100,7 @@ def findAllAgents(api):
|
||||
|
||||
return agents
|
||||
|
||||
|
||||
def findAgents(api, return_dataframe):
|
||||
agents = selectAgents(api)
|
||||
working_dir = load_env("WORKING_DIR")
|
||||
@@ -113,8 +122,14 @@ def findAgents(api, return_dataframe):
|
||||
print(agent_df)
|
||||
logging.debug("Displayed DataFrame to console.")
|
||||
|
||||
user_input = get_sanitized_input("\nWould you like to export the results to a CSV file? (y/n): ").strip().lower()
|
||||
if user_input == 'y':
|
||||
user_input = (
|
||||
get_sanitized_input(
|
||||
"\nWould you like to export the results to a CSV file? (y/n): "
|
||||
)
|
||||
.strip()
|
||||
.lower()
|
||||
)
|
||||
if user_input == "y":
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||
filename = f"agentsearch_{timestamp}.csv"
|
||||
file_path = os.path.join(str(working_dir), filename)
|
||||
@@ -131,17 +146,27 @@ def findAgents(api, return_dataframe):
|
||||
else:
|
||||
logging.debug("User declined to export the DataFrame.")
|
||||
|
||||
|
||||
def collect_device_names() -> List[str]:
|
||||
print(colorText("🔍 Device Search", "cyan"))
|
||||
print(colorText("Enter the device hostnames you'd like to search for, one per line.", "cyan"))
|
||||
print(colorText("When you're done, press Enter twice (Three times if you have a single device).\n", "cyan"))
|
||||
print(
|
||||
colorText(
|
||||
"Enter the device hostnames you'd like to search for, one per line.", "cyan"
|
||||
)
|
||||
)
|
||||
print(
|
||||
colorText(
|
||||
"When you're done, press Enter twice (Three times if you have a single device).\n",
|
||||
"cyan",
|
||||
)
|
||||
)
|
||||
print(colorText("Example:", "cyan"))
|
||||
print(colorText("H00000\nUTN00000\ni-hSuperSecretServer\nu-hVenderBroke\n", "cyan"))
|
||||
print(colorText("Paste or type your device names below:", "white"))
|
||||
|
||||
device_input_lines = []
|
||||
empty_line_count = 0
|
||||
valid_line_pattern = re.compile(r'^[a-zA-Z0-9_\- ]+$')
|
||||
valid_line_pattern = re.compile(r"^[a-zA-Z0-9_\- ]+$")
|
||||
|
||||
while True:
|
||||
line = get_sanitized_input("")
|
||||
@@ -158,7 +183,12 @@ def collect_device_names() -> List[str]:
|
||||
if valid_line_pattern.match(stripped_line):
|
||||
device_input_lines.append(stripped_line)
|
||||
else:
|
||||
print(colorText(f"⚠️ Invalid input: '{stripped_line}' — only letters, numbers, underscores, spaces, and hyphens are allowed.", "yellow"))
|
||||
print(
|
||||
colorText(
|
||||
f"⚠️ Invalid input: '{stripped_line}' — only letters, numbers, underscores, spaces, and hyphens are allowed.",
|
||||
"yellow",
|
||||
)
|
||||
)
|
||||
|
||||
return [name for name in device_input_lines if name]
|
||||
|
||||
@@ -168,10 +198,13 @@ def choose_match_type() -> bool:
|
||||
return get_sanitized_input("").strip().lower() in ["y", "yes"]
|
||||
|
||||
|
||||
def match_agents(device_names: List[str], agents: List['Agent'], use_exact: bool) -> List['Agent']:
|
||||
def match_agents(
|
||||
device_names: List[str], agents: List["Agent"], use_exact: bool
|
||||
) -> List["Agent"]:
|
||||
if use_exact:
|
||||
return [
|
||||
agent for agent in agents
|
||||
agent
|
||||
for agent in agents
|
||||
if agent.hostname.lower() in [name.lower() for name in device_names]
|
||||
]
|
||||
else:
|
||||
@@ -180,23 +213,38 @@ def match_agents(device_names: List[str], agents: List['Agent'], use_exact: bool
|
||||
return [agent for agent in agents if regex.search(agent.hostname)]
|
||||
|
||||
|
||||
def show_unmatched(device_names: List[str], matched_agents: List['Agent'], use_exact: bool):
|
||||
def show_unmatched(
|
||||
device_names: List[str], matched_agents: List["Agent"], use_exact: bool
|
||||
):
|
||||
if use_exact:
|
||||
unmatched = [name for name in device_names if not any(agent.hostname.lower() == name.lower() for agent in matched_agents)]
|
||||
unmatched = [
|
||||
name
|
||||
for name in device_names
|
||||
if not any(
|
||||
agent.hostname.lower() == name.lower() for agent in matched_agents
|
||||
)
|
||||
]
|
||||
else:
|
||||
unmatched = [name for name in device_names if not any(re.search(re.escape(name), agent.hostname, re.IGNORECASE) for agent in matched_agents)]
|
||||
unmatched = [
|
||||
name
|
||||
for name in device_names
|
||||
if not any(
|
||||
re.search(re.escape(name), agent.hostname, re.IGNORECASE)
|
||||
for agent in matched_agents
|
||||
)
|
||||
]
|
||||
|
||||
if unmatched:
|
||||
logger.debug(f"⚠️ No matches for: {', '.join(unmatched)}")
|
||||
print(colorText(f"⚠️ No matches for: {', '.join(unmatched)}", "yellow"))
|
||||
|
||||
|
||||
def enrich_agents(agents: List['Agent'], policies: List['Policy']):
|
||||
def enrich_agents(agents: List["Agent"], policies: List["Policy"]):
|
||||
for agent in agents:
|
||||
agent.enrich_with_policies(policies)
|
||||
|
||||
|
||||
def selectAgents(api: 'AirlockAPIWrapper') -> List['Agent']:
|
||||
def selectAgents(api: "AirlockAPIWrapper") -> List["Agent"]:
|
||||
device_names = collect_device_names()
|
||||
if not device_names:
|
||||
logger.debug("No device names entered")
|
||||
@@ -227,11 +275,11 @@ def selectAgents(api: 'AirlockAPIWrapper') -> List['Agent']:
|
||||
if idx < len(matched_agents):
|
||||
line += f"{matched_agents[idx].hostname:<30}"
|
||||
logger.info(line)
|
||||
|
||||
|
||||
matched_agents = Selector.select_with_mode(
|
||||
matched_agents,
|
||||
label_func=lambda agent: agent.hostname,
|
||||
header="Matched Devices:"
|
||||
header="Matched Devices:",
|
||||
)
|
||||
|
||||
if not matched_agents:
|
||||
@@ -263,11 +311,17 @@ def moveAgentToRelatedPolicy(
|
||||
if agent.groupid in policy_relationship_map:
|
||||
target_policy = policy_relationship_map[agent.groupid]
|
||||
elif agent.groupid in policy_relationship_map.values():
|
||||
logger.debug(f"Agent {agent.hostname} is already in an audit group. No action needed.")
|
||||
print(f"Agent {agent.hostname} is already in an audit group. No action needed.")
|
||||
logger.debug(
|
||||
f"Agent {agent.hostname} is already in an audit group. No action needed."
|
||||
)
|
||||
print(
|
||||
f"Agent {agent.hostname} is already in an audit group. No action needed."
|
||||
)
|
||||
return
|
||||
else:
|
||||
logger.warning(f"Error: No corresponding audit policy found for groupid: {agent.groupid}.")
|
||||
logger.warning(
|
||||
f"Error: No corresponding audit policy found for groupid: {agent.groupid}."
|
||||
)
|
||||
return
|
||||
|
||||
elif mode == "enforcement":
|
||||
@@ -275,10 +329,14 @@ def moveAgentToRelatedPolicy(
|
||||
if agent.groupid in inverse_map:
|
||||
target_policy = inverse_map[agent.groupid]
|
||||
elif agent.groupid in inverse_map.values():
|
||||
logger.info(f"Agent {agent.hostname} is already in an enforcement group. No action needed.")
|
||||
logger.info(
|
||||
f"Agent {agent.hostname} is already in an enforcement group. No action needed."
|
||||
)
|
||||
return
|
||||
else:
|
||||
logger.warning(f"Error: No corresponding enforcement policy found for groupid: {agent.groupid}.")
|
||||
logger.warning(
|
||||
f"Error: No corresponding enforcement policy found for groupid: {agent.groupid}."
|
||||
)
|
||||
return
|
||||
|
||||
else:
|
||||
@@ -299,25 +357,32 @@ def toggleEnforcement(api: AirlockAPIWrapper):
|
||||
devices = selectAgents(api)
|
||||
for device in devices:
|
||||
print(device.hostname)
|
||||
confirm = Selector.confirm("Would you like to continue with these devices? Y/N: ")
|
||||
confirm = Selector.confirm(
|
||||
"Would you like to continue with these devices? Y/N: "
|
||||
)
|
||||
if direction and devices and confirm:
|
||||
for device in devices:
|
||||
result = moveAgentToRelatedPolicy(api,device, str(direction).lower())
|
||||
result = moveAgentToRelatedPolicy(api, device, str(direction).lower())
|
||||
logger.info(f"{device.hostname}: result: {result}")
|
||||
get_sanitized_input("Press enter to continue")
|
||||
|
||||
|
||||
def moveAgents(api: AirlockAPIWrapper):
|
||||
devices = selectAgents(api)
|
||||
for device in devices:
|
||||
print(device.hostname)
|
||||
confirm_devices = Selector.confirm("Would you like to continue with these devices? Y/N: ")
|
||||
confirm_devices = Selector.confirm(
|
||||
"Would you like to continue with these devices? Y/N: "
|
||||
)
|
||||
if devices and confirm_devices:
|
||||
policies = selectPolicies(api, False)
|
||||
confirm_move = Selector.confirm(f"Would you like to move these devices to {policies[0].name}?")
|
||||
confirm_move = Selector.confirm(
|
||||
f"Would you like to move these devices to {policies[0].name}?"
|
||||
)
|
||||
if confirm_move:
|
||||
for device in devices:
|
||||
result = api.agent_move(device.agentid, policies[0].groupid)
|
||||
logger.info(f"{device.hostname}: result: {result}")
|
||||
else:
|
||||
logger.info("Exiting without change")
|
||||
get_sanitized_input("Press enter to continue")
|
||||
get_sanitized_input("Press enter to continue")
|
||||
|
||||
@@ -34,7 +34,6 @@ from utils.utils import areYouSure, colorText, get_sanitized_input
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
def pullPolicyExechistories(
|
||||
api: AirlockAPIWrapper,
|
||||
policy: Policy,
|
||||
@@ -72,7 +71,7 @@ def pullPolicyExechistories(
|
||||
) as pbar:
|
||||
while True:
|
||||
histories = api.history_logging(
|
||||
type=type, checkpoint=checkpoint, policy= [policy.name]
|
||||
type=type, checkpoint=checkpoint, policy=[policy.name]
|
||||
)
|
||||
|
||||
# Ensure histories is a list of dictionaries
|
||||
@@ -98,13 +97,17 @@ def pullPolicyExechistories(
|
||||
|
||||
# Update checkpoint on last item
|
||||
if index == len(histories) - 1:
|
||||
checkpoint = history_item["checkpoint"] # pyright: ignore[reportArgumentType]
|
||||
checkpoint = history_item[
|
||||
"checkpoint"
|
||||
] # pyright: ignore[reportArgumentType]
|
||||
filebar.desc = f"Checkpoint Progress: {checkpoint}"
|
||||
break
|
||||
|
||||
try:
|
||||
history_date = datetime.datetime.strptime(
|
||||
history_item["datetime"].replace(" +0000 UTC", ""), # pyright: ignore[reportArgumentType]
|
||||
history_item["datetime"].replace(
|
||||
" +0000 UTC", ""
|
||||
), # pyright: ignore[reportArgumentType]
|
||||
"%Y-%m-%dT%H:%M:%SZ",
|
||||
).date()
|
||||
except ValueError:
|
||||
@@ -178,7 +181,7 @@ def pullPolicyExechistories(
|
||||
|
||||
def getPolicyInfo(api: AirlockAPIWrapper, policy, type, days):
|
||||
import airlock_libs
|
||||
|
||||
|
||||
executionhist_policy = pd.DataFrame()
|
||||
exehist = airlock_libs.pull_policy_exec_histories(api, policy.name, str(type), days)
|
||||
if exehist is not None:
|
||||
@@ -205,7 +208,7 @@ def getPolicyInfo(api: AirlockAPIWrapper, policy, type, days):
|
||||
executionhist_policy = executionhist_policy.sort_values(
|
||||
by=["sha256", "filename"]
|
||||
)
|
||||
logger.debug( f"Staging of Execution history for policy: {policy} is complete")
|
||||
logger.debug(f"Staging of Execution history for policy: {policy} is complete")
|
||||
print(
|
||||
colorText(
|
||||
f"Staging of Execution history for policy: {policy} is complete",
|
||||
@@ -237,10 +240,10 @@ def updateAuditPoliciesFromEnforcementPolices(api: AirlockAPIWrapper):
|
||||
for enforcement_policy, audit_policy in policy_relationship_map.items():
|
||||
api.policy_clone(enforcement_policy, audit_policy)
|
||||
api.policy_set_auditmode(audit_policy, "1")
|
||||
|
||||
|
||||
|
||||
def confirmUpdateAfromE(api: AirlockAPIWrapper):
|
||||
areYouSure()
|
||||
confirmation = get_sanitized_input("Type 'I AGREE' to continue: ")
|
||||
if confirmation.strip() == "I AGREE":
|
||||
updateAuditPoliciesFromEnforcementPolices(api)
|
||||
updateAuditPoliciesFromEnforcementPolices(api)
|
||||
|
||||
+25
-12
@@ -28,8 +28,8 @@ import keyring
|
||||
|
||||
# Constants
|
||||
KDF_ITERATIONS = 200_000
|
||||
SALT_SIZE = 16 # 128-bit Salt
|
||||
NONCE_SIZE = 12 # AES-GCM
|
||||
SALT_SIZE = 16 # 128-bit Salt
|
||||
NONCE_SIZE = 12 # AES-GCM
|
||||
KEY_SIZE = 32 # AES-256
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -49,9 +49,11 @@ def configure_keyring_backend():
|
||||
system = platform.system()
|
||||
if system == "Windows":
|
||||
import keyring.backends.Windows
|
||||
|
||||
keyring.set_keyring(keyring.backends.Windows.WinVaultKeyring())
|
||||
elif system == "Linux":
|
||||
import keyring.backends.kwallet
|
||||
|
||||
keyring.set_keyring(keyring.backends.kwallet.DBusKeyring())
|
||||
else:
|
||||
raise EnvironmentError(f"Unsupported OS: {system}")
|
||||
@@ -68,8 +70,9 @@ def store_api_key(service: str, username: str, api_key: str, password: str):
|
||||
b64 = base64.b64encode(blob).decode()
|
||||
keyring.set_password(service, username, b64)
|
||||
|
||||
|
||||
logger.debug(f"API key for service '{service}' and user '{username}' stored successfully.")
|
||||
logger.debug(
|
||||
f"API key for service '{service}' and user '{username}' stored successfully."
|
||||
)
|
||||
|
||||
print("\n✅ API key stored securely.")
|
||||
print("The program will now exit. Press Enter to continue...")
|
||||
@@ -90,8 +93,8 @@ def retrieve_api_key(service: str, username: str, password: str) -> str:
|
||||
raise ValueError("No stored secret for this service/username.")
|
||||
blob = base64.b64decode(b64)
|
||||
salt = blob[:SALT_SIZE]
|
||||
nonce = blob[SALT_SIZE:SALT_SIZE + NONCE_SIZE]
|
||||
ct = blob[SALT_SIZE + NONCE_SIZE:]
|
||||
nonce = blob[SALT_SIZE : SALT_SIZE + NONCE_SIZE]
|
||||
ct = blob[SALT_SIZE + NONCE_SIZE :]
|
||||
key = _derive_key(password.encode(), salt)
|
||||
aesgcm = AESGCM(key)
|
||||
pt = aesgcm.decrypt(nonce, ct, associated_data=None)
|
||||
@@ -124,7 +127,9 @@ def getAPI(USERNAME, SERVICE_NAME):
|
||||
|
||||
if api_key_exists(SERVICE_NAME, USERNAME):
|
||||
for attempt in range(1, 4):
|
||||
password = getpass(f"Attempt {attempt}/3 - Enter password to unlock your API key: ")
|
||||
password = getpass(
|
||||
f"Attempt {attempt}/3 - Enter password to unlock your API key: "
|
||||
)
|
||||
try:
|
||||
apikey = retrieve_api_key(SERVICE_NAME, USERNAME, password)
|
||||
logging.debug("API key successfully retrieved.")
|
||||
@@ -134,9 +139,15 @@ def getAPI(USERNAME, SERVICE_NAME):
|
||||
logging.error("Failed to retrieve API key after 3 incorrect attempts.")
|
||||
raise ValueError("Failed to retrieve API key after 3 incorrect attempts.")
|
||||
else:
|
||||
logging.warning(f"No API key found for user '{USERNAME}' in service '{SERVICE_NAME}'.")
|
||||
api_key = getpass(f"No API key found. Please enter your API key for '{SERVICE_NAME}': ").strip()
|
||||
print("Please exit and relaunch program after saving your credential to avoid errors")
|
||||
logging.warning(
|
||||
f"No API key found for user '{USERNAME}' in service '{SERVICE_NAME}'."
|
||||
)
|
||||
api_key = getpass(
|
||||
f"No API key found. Please enter your API key for '{SERVICE_NAME}': "
|
||||
).strip()
|
||||
print(
|
||||
"Please exit and relaunch program after saving your credential to avoid errors"
|
||||
)
|
||||
|
||||
while True:
|
||||
password = getpass("Create a password to encrypt your API key: ")
|
||||
@@ -155,7 +166,9 @@ def getAPI(USERNAME, SERVICE_NAME):
|
||||
logging.error(f"Failed to store API key: {e}")
|
||||
break
|
||||
else:
|
||||
logging.warning("Password does not meet complexity requirements. Try again.")
|
||||
logging.warning(
|
||||
"Password does not meet complexity requirements. Try again."
|
||||
)
|
||||
|
||||
|
||||
class APIKeyManager:
|
||||
@@ -169,4 +182,4 @@ class APIKeyManager:
|
||||
def get(cls) -> str:
|
||||
if cls._api_key is None:
|
||||
raise ValueError("API key not loaded. Call APIKeyManager.load() first.")
|
||||
return cls._api_key
|
||||
return cls._api_key
|
||||
|
||||
Reference in New Issue
Block a user