Compare commits
10 commits
Author | SHA1 | Date | |
---|---|---|---|
Yannis | 48c4a5ba56 | ||
Yannis | a01286fd94 | ||
Yannis | 66f338e5db | ||
Yannis | 713f49ac93 | ||
Yannis | eb24aabd2f | ||
Yannis | 6056e0fea7 | ||
Yannis | d10346ed1a | ||
Adastram | c1a2cad45e | ||
Adastram | f9231a0a8a | ||
Adastram | c67edc9876 |
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1 +1,5 @@
|
||||||
.idea
|
.idea
|
||||||
|
.gitignore
|
||||||
|
__pycache__/tray_manager.cpython-311.pyc
|
||||||
|
__pycache__/creds.cpython-311.pyc
|
||||||
|
__pycache__/alcasar.cpython-311.pyc
|
71
alcasar-install.bat
Normal file
71
alcasar-install.bat
Normal file
File diff suppressed because one or more lines are too long
|
@ -4,6 +4,7 @@ from enum import Enum
|
||||||
import requests
|
import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
print("Hello AlcasarAuto!")
|
||||||
|
|
||||||
class ConnectionResults(Enum):
|
class ConnectionResults(Enum):
|
||||||
"""Enumeration used for connection result"""
|
"""Enumeration used for connection result"""
|
||||||
|
|
146
autostart.py
146
autostart.py
|
@ -1,146 +0,0 @@
|
||||||
from os import remove, getlogin, getcwd
|
|
||||||
from subprocess import Popen, PIPE
|
|
||||||
from os.path import join
|
|
||||||
|
|
||||||
|
|
||||||
def add_to_scheduler(foldername: str, appname: str, programpath: str, arguments: str = ""):
|
|
||||||
"""Add the task to the task scheduler\n
|
|
||||||
The task is triggered at the user who executed this function login\n
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
* folder_name\n
|
|
||||||
Define the folder of the task scheduler in which we're going to create our task
|
|
||||||
* app_name\n
|
|
||||||
Define the name of the task in the task scheduler
|
|
||||||
* program_path\n
|
|
||||||
Define the program to execute when task is triggered\n
|
|
||||||
* arguments\n
|
|
||||||
The arguments to pass to the program executed when task is triggered
|
|
||||||
Return
|
|
||||||
------
|
|
||||||
True if command created the task successfully, else, return False"""
|
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
user = getlogin()
|
|
||||||
path = join(getcwd(), "taskschedulerxml.xml")
|
|
||||||
date = datetime.now()
|
|
||||||
|
|
||||||
XML = f"""<?xml version="1.0" encoding="UTF-16"?>
|
|
||||||
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
||||||
<RegistrationInfo>
|
|
||||||
<URI>{uri}</URI>
|
|
||||||
<SecurityDescriptor>{securitydescriptor}</SecurityDescriptor>
|
|
||||||
<Source>{source}</Source>
|
|
||||||
<Date>{date.year}-{date.month}-{date.day}T{date.hour}:{date.minute}:{date.second}</Date>
|
|
||||||
<Author>{user}</Author>
|
|
||||||
<Version>{version}</Version>
|
|
||||||
<Description>{description}</Description>
|
|
||||||
<Documentation>{documentation}</Documentation>
|
|
||||||
</RegistrationInfo>
|
|
||||||
<Triggers>
|
|
||||||
<BootTrigger>
|
|
||||||
<Enabled>false</Enabled>
|
|
||||||
<StartBoundary>{startboundary}</StartBoundary>
|
|
||||||
<EndBoundary>{endboundary}</EndBoundary>
|
|
||||||
<Repetition>{repetiton}</Repetition>
|
|
||||||
<ExecutionTimeLimit>{executiontimelimit}</ExecutionTimeLimit>
|
|
||||||
</BootTrigger>
|
|
||||||
<RegistrationTrigger>
|
|
||||||
<Enabled>false</Enabled>
|
|
||||||
<StartBoundary>{startboundary}</StartBoundary>
|
|
||||||
<EndBoundary>{endboundary}</EndBoundary>
|
|
||||||
<Repetition>{repetiton}</Repetition>
|
|
||||||
<ExecutionTimeLimit>{executiontimelimit}</ExecutionTimeLimit>
|
|
||||||
</RegistrationTrigger>
|
|
||||||
<IdleTrigger>
|
|
||||||
<Enabled>false</Enabled>
|
|
||||||
<StartBoundary>{startboundary}</StartBoundary>
|
|
||||||
<EndBoundary>{endboundary}</EndBoundary>
|
|
||||||
<Repetition>{repetiton}</Repetition>
|
|
||||||
<ExecutionTimeLimit>{executiontimelimit}</ExecutionTimeLimit>
|
|
||||||
</IdleTrigger>
|
|
||||||
<TimeTrigger>
|
|
||||||
<Enabled>false</Enabled>
|
|
||||||
<StartBoundary>{startboundary}</StartBoundary>
|
|
||||||
<EndBoundary>{endboundary}</EndBoundary>
|
|
||||||
<Repetition>{repetiton}</Repetition>
|
|
||||||
<ExecutionTimeLimit>{executiontimelimit}</ExecutionTimeLimit>
|
|
||||||
</TimeTrigger>
|
|
||||||
<EventTrigger>
|
|
||||||
<Enabled>false</Enabled>
|
|
||||||
<StartBoundary>{startboundary}</StartBoundary>
|
|
||||||
<EndBoundary>{endboundary}</EndBoundary>
|
|
||||||
<Repetition>{repetiton}</Repetition>
|
|
||||||
<ExecutionTimeLimit>{executiontimelimit}</ExecutionTimeLimit>
|
|
||||||
</EventTrigger>
|
|
||||||
<LogonTrigger>
|
|
||||||
<Enabled>false</Enabled>
|
|
||||||
<StartBoundary>{startboundary}</StartBoundary>
|
|
||||||
<EndBoundary>{endboundary}</EndBoundary>
|
|
||||||
<Repetition>{repetiton}</Repetition>
|
|
||||||
<ExecutionTimeLimit>{executiontimelimit}</ExecutionTimeLimit>
|
|
||||||
<UserId>{user}</UserId>
|
|
||||||
</LogonTrigger>
|
|
||||||
<SessionStateChangeTrigger>
|
|
||||||
<Enabled>{logontrigger}</Enabled>
|
|
||||||
<StartBoundary>{startboundary}</StartBoundary>
|
|
||||||
<EndBoundary>{endboundary}</EndBoundary>
|
|
||||||
<Repetition>{repetiton}</Repetition>
|
|
||||||
<ExecutionTimeLimit>{executiontimelimit}</ExecutionTimeLimit>
|
|
||||||
</SessionStateChangeTrigger>
|
|
||||||
<CalendarTrigger>
|
|
||||||
<Enabled>{calendartrigger}</Enabled>
|
|
||||||
<StartBoundary>{startboundary}</StartBoundary>
|
|
||||||
<EndBoundary>{endboundary}</EndBoundary>
|
|
||||||
<Repetition>{repetiton}</Repetition>
|
|
||||||
<ExecutionTimeLimit>{executiontimelimit}</ExecutionTimeLimit>
|
|
||||||
</CalendarTrigger>
|
|
||||||
</Triggers>
|
|
||||||
<Principals>
|
|
||||||
<Principal id="Author">
|
|
||||||
<UserId>{user}</UserId>
|
|
||||||
<LogonType>InteractiveToken</LogonType>
|
|
||||||
<RunLevel>LeastPrivilege</RunLevel>
|
|
||||||
</Principal>
|
|
||||||
</Principals>
|
|
||||||
<Settings>
|
|
||||||
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
|
||||||
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
|
||||||
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
|
||||||
<AllowHardTerminate>true</AllowHardTerminate>
|
|
||||||
<StartWhenAvailable>false</StartWhenAvailable>
|
|
||||||
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
|
|
||||||
<IdleSettings>
|
|
||||||
<StopOnIdleEnd>true</StopOnIdleEnd>
|
|
||||||
<RestartOnIdle>false</RestartOnIdle>
|
|
||||||
</IdleSettings>
|
|
||||||
<AllowStartOnDemand>true</AllowStartOnDemand>
|
|
||||||
<Enabled>true</Enabled>
|
|
||||||
<Hidden>false</Hidden>
|
|
||||||
<RunOnlyIfIdle>false</RunOnlyIfIdle>
|
|
||||||
<WakeToRun>false</WakeToRun>
|
|
||||||
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
|
|
||||||
<Priority>7</Priority>
|
|
||||||
</Settings>
|
|
||||||
<Actions Context="Author">
|
|
||||||
<Exec>
|
|
||||||
<Command>{programpath}</Command>
|
|
||||||
<Arguments>{arguments}</Arguments>
|
|
||||||
</Exec>
|
|
||||||
</Actions>
|
|
||||||
</Task>"""
|
|
||||||
|
|
||||||
#Write the file so we can execute it later
|
|
||||||
with open(path, "w+") as f:
|
|
||||||
f.write(XML)
|
|
||||||
|
|
||||||
#Execute the command
|
|
||||||
process = Popen(["schtasks", "/create", "/XML", f'"{path}"', "/TN", f'{foldername}\\{appname}', "/F"], stderr=PIPE)
|
|
||||||
|
|
||||||
#Remove the file
|
|
||||||
remove(path)
|
|
||||||
|
|
||||||
#If no error output, return true, else, return false
|
|
||||||
if process.stderr.read() == b'':
|
|
||||||
return True
|
|
||||||
return False
|
|
202
credsmanager.py
Normal file
202
credsmanager.py
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
from json import dumps as js_d, loads as js_l
|
||||||
|
from cryptography.fernet import Fernet
|
||||||
|
from subprocess import Popen, PIPE
|
||||||
|
from os.path import exists
|
||||||
|
|
||||||
|
|
||||||
|
class CredentialsManager():
|
||||||
|
"""Used to acces crypted json credentials path using fernet"""
|
||||||
|
|
||||||
|
def __init__(self, credentials_file_path: str, reg_key_path: str = "CredentialsManager", key_name: str = "fernkey"):
|
||||||
|
"""Create a CredentialsManager object used to acces crypted json credentials path using fernet\n
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
* credentials_file_path : str\n
|
||||||
|
The path of your credentials file, MUST be .json, if not or if file doesn't exist, create a new one
|
||||||
|
* reg_key_path : str\n
|
||||||
|
The path where the fernet key will be or is stored in the windows registery.\n
|
||||||
|
>>> "IN THE CURRENT USER REG PATH (HKEY_CURRENT_USER\\) ONLY. ENTER NAME OF THE 'FOLDER' YOU WANT THE KEY TO BE IN"\n
|
||||||
|
* key_name : str\n
|
||||||
|
The name of the registery key in which the encryption key will be stored"""
|
||||||
|
|
||||||
|
|
||||||
|
# Storing the reg key path in self to acces it later
|
||||||
|
self.reg_key_path = f"HKEY_CURRENT_USER\\{reg_key_path}"
|
||||||
|
|
||||||
|
# Storing the reg key name in self to acces it later
|
||||||
|
self.reg_key_name = key_name
|
||||||
|
|
||||||
|
# Get/Create a new key
|
||||||
|
self.key = self.get_key()
|
||||||
|
|
||||||
|
# Initialise our Fernet object with the key, will raise a error if key wasn't create and returned None
|
||||||
|
self.Fernet = Fernet(key=self.key)
|
||||||
|
|
||||||
|
|
||||||
|
# Replace credential file extansion by .json
|
||||||
|
# Replace last element after a dot by json in the file path
|
||||||
|
# Store it in self to acces it later
|
||||||
|
self.creds_file_path = credentials_file_path.replace(credentials_file_path.split(".")[-1], "json")
|
||||||
|
|
||||||
|
# Check if credentials file exist, if not, try creating new one and write empty line
|
||||||
|
if not exists(self.creds_file_path):
|
||||||
|
with open(self.creds_file_path, "w") as f:
|
||||||
|
f.write("")
|
||||||
|
|
||||||
|
|
||||||
|
def get_key(self) -> bytes | None:
|
||||||
|
"""Get the Fernet encryption key in the windows registery, if not found, create a new one"""
|
||||||
|
# Get key using reg QUERY in subprocess
|
||||||
|
proces = Popen(f"reg QUERY {self.reg_key_path} /v {self.reg_key_name}", stdout=PIPE, stderr=PIPE)
|
||||||
|
|
||||||
|
# Check if error output is not empty
|
||||||
|
if proces.stderr.read() !=b'':
|
||||||
|
# Create a new key and return it
|
||||||
|
return self.create_key()
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Return key as binary
|
||||||
|
return proces.stdout.read().decode("CP850").rsplit()[3].encode()
|
||||||
|
|
||||||
|
|
||||||
|
def create_key(self):
|
||||||
|
"""Create a new key, store it in the windows registery and return it"""
|
||||||
|
# Generate a new key with Fernet
|
||||||
|
key = Fernet.generate_key()
|
||||||
|
|
||||||
|
# Add it to registery using reg ADD in subprocess
|
||||||
|
proces = Popen(f"reg ADD {self.reg_key_path} /v {self.reg_key_name} /d {key.decode()}", stdout=PIPE, stderr=PIPE)
|
||||||
|
|
||||||
|
# Check if process executed successfully, if yes return key, if not return None
|
||||||
|
if proces.stderr.read() == b'':
|
||||||
|
self.key = key
|
||||||
|
return key
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def delete_key(self, delete_dir: bool = False):
|
||||||
|
"""Delete the encryption key from the windows registery, return True if removed successfully, and False if not.\n
|
||||||
|
>>> "WARNING YOU CAN'T RETRIEVE ENCRYPTED DATA AFTER REMOVAL"\n
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
* delete_dir bool (Defaukt = False)\n
|
||||||
|
If set on True, also delete the directory of the stored key\n
|
||||||
|
>>> "WARNING ALL THE KEYS CONTAINED IN THE DIRECTORY WILL BE DELETED" """
|
||||||
|
|
||||||
|
if delete_dir:
|
||||||
|
# Delete directory and all the key from registery using reg DELETE in subprocess
|
||||||
|
command = f"reg DELETE {self.reg_key_path} /f"
|
||||||
|
else:
|
||||||
|
# Delete key from registery using reg DELETE in subprocess
|
||||||
|
command = f"reg DELETE {self.reg_key_path} /v {self.reg_key_name} /f"
|
||||||
|
|
||||||
|
# Execute command
|
||||||
|
proces = Popen(command, stdout=PIPE, stderr=PIPE)
|
||||||
|
|
||||||
|
# Check if process executed successfully, if yes return True, if not return False
|
||||||
|
if proces.stderr.read() == b'':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def decrypt(self, data: bytes) -> dict:
|
||||||
|
"""Return the decrypted version of the encrypted bytes as a json dict"""
|
||||||
|
# Decrypt using Fernet, then decode into str, then load it with json
|
||||||
|
return js_l(self.Fernet.decrypt(data).decode())
|
||||||
|
|
||||||
|
|
||||||
|
def encrypt(self, data: str) -> bytes:
|
||||||
|
"""Return the encrypted version of the Serialize (means it's a dict encoded as a str by json) json dict as encrypted bytes"""
|
||||||
|
# Dump using json, then encode it into UTF-8, then encrypt it using Fernet
|
||||||
|
return self.Fernet.encrypt(js_d(data).encode())
|
||||||
|
|
||||||
|
|
||||||
|
def get_credentials(self):
|
||||||
|
"""Return the decrypted dict of username and passwords (format : {username: password, ...})"""
|
||||||
|
|
||||||
|
# Open credentials file
|
||||||
|
with open(self.creds_file_path, "rb") as f:
|
||||||
|
# Read content of the file
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
# If file is empty, return an empty dict to avoid Fernet from crashing while trying to decrypt void
|
||||||
|
if data == b"":
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# Decrypt and return the dict
|
||||||
|
return self.decrypt(data)
|
||||||
|
|
||||||
|
|
||||||
|
def write_credentials(self, new_credentials_dict: dict[str: str]):
|
||||||
|
"""Write the dict data in the credentials file\n
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
* new_credentials_dict : {str: str}\n
|
||||||
|
The dict to encrypt and write in the credentials file\n
|
||||||
|
>>> "WARNING. ERASE ALL EXISTING DATA IN THE FILE, IF YOU WANT TO APPEND DATA, SEE CredentialsManager.add_credentials()" """
|
||||||
|
|
||||||
|
# Open credentials file
|
||||||
|
with open(self.creds_file_path, "wb") as f:
|
||||||
|
# Encrypt, and write the encrypted data in the file
|
||||||
|
f.write(self.encrypt(new_credentials_dict))
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def add_credentials(self, new_credentials_data_dict: dict[str: str]):
|
||||||
|
"""Append data to the encrypted json credentials file\n
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
* new_credentials_data_dict: {str: str}\n
|
||||||
|
The data to append to the encrypted credentials json dict"""
|
||||||
|
|
||||||
|
# Use get_credentials and write_credentials
|
||||||
|
|
||||||
|
# Get current data
|
||||||
|
old_dict = self.get_credentials()
|
||||||
|
|
||||||
|
|
||||||
|
# Set our new dict with the old value to keep
|
||||||
|
new_dict = old_dict
|
||||||
|
|
||||||
|
|
||||||
|
# Get all the keys of the new data
|
||||||
|
# For each key add them to the new dict
|
||||||
|
for key in new_credentials_data_dict.keys():
|
||||||
|
new_dict[key] = new_credentials_data_dict[key]
|
||||||
|
|
||||||
|
# Write our new dict to the encrypted json credentials file
|
||||||
|
self.write_credentials(new_dict)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def remove_credentials(self, credentials_to_remove: dict[str: str], match_password: bool = True):
|
||||||
|
"""Remove the specified data from the encrypted json credentials file\n
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
* credentials_to_remove : {str: str}\n
|
||||||
|
The data to remove from the encrypted credentials json dict
|
||||||
|
* match_password : bool (Default = True)\n
|
||||||
|
If set on True, the username AND password must match to be removed, if set on False, ONLY username must match"""
|
||||||
|
|
||||||
|
# Use get_credentials and write_credentials
|
||||||
|
|
||||||
|
# Get current data
|
||||||
|
dict = self.get_credentials()
|
||||||
|
|
||||||
|
# Get all the keys to remove
|
||||||
|
# For each key, if the key is in the dict, remove it
|
||||||
|
# Using a try except in case the key isn't in the dict
|
||||||
|
for key in credentials_to_remove.keys():
|
||||||
|
try:
|
||||||
|
# If match_password is True, check if passwords are equals, if not, pass
|
||||||
|
if match_password:
|
||||||
|
if dict[key] == credentials_to_remove[key]:
|
||||||
|
dict.pop(key)
|
||||||
|
else:
|
||||||
|
dict.pop(key)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Write our dict to the encrypted json credentials file
|
||||||
|
self.write_credentials(dict)
|
||||||
|
return
|
3
main.py
3
main.py
|
@ -1,5 +1,8 @@
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
import os, sys
|
||||||
|
sys.path.append(os.path.dirname(__file__))
|
||||||
|
|
||||||
import settings_manager
|
import settings_manager
|
||||||
from alcasar import Alcasar
|
from alcasar import Alcasar
|
||||||
from tray_manager import TrayManager
|
from tray_manager import TrayManager
|
||||||
|
|
37
setup.py
Normal file
37
setup.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import subprocess as sp
|
||||||
|
from urllib import request
|
||||||
|
import tempfile
|
||||||
|
from os import path, mkdir
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
# Create a temporary directory
|
||||||
|
temp_dir = tempfile.mkdtemp()
|
||||||
|
|
||||||
|
# Download the python zip
|
||||||
|
print("Downloading Python...")
|
||||||
|
python_files = path.join(temp_dir, "python.zip")
|
||||||
|
request.urlretrieve("https://www.python.org/ftp/python/3.13.0/python-3.13.0a2-embed-amd64.zip",
|
||||||
|
python_files)
|
||||||
|
|
||||||
|
# Create the installation folder
|
||||||
|
mkdir(r"C:\ProgramData\AlcasarAuto")
|
||||||
|
mkdir(r"C:\ProgramData\AlcasarAuto\python")
|
||||||
|
|
||||||
|
# Extract the zipfile
|
||||||
|
print("Extracting zipfile...")
|
||||||
|
zipfile.ZipFile(file=python_files).extractall(r"C:\ProgramData\AlcasarAuto\python")
|
||||||
|
|
||||||
|
# Install pip
|
||||||
|
print("Installing Pip...")
|
||||||
|
request.urlretrieve("https://bootstrap.pypa.io/get-pip.py",
|
||||||
|
r"C:\ProgramData\AlcasarAuto\python\get-pip.py")
|
||||||
|
sp.call([r"C:\ProgramData\AlcasarAuto\python", "get-pip.py"])
|
||||||
|
|
||||||
|
print("Updating imports...")
|
||||||
|
with open(r"C:\ProgramData\AlcasarAuto\python\get-pip.py", "a") as file:
|
||||||
|
file.write("Lib\nLib\\site-packages")
|
||||||
|
|
||||||
|
# Install libs
|
||||||
|
print("Istalling dependencies...")
|
||||||
|
for i in ["requests", "bs4"]:
|
||||||
|
sp.call([r"C:\ProgramData\AlcasarAuto\python", "-m", "pip", "install", i])
|
Loading…
Reference in a new issue