1
0
Fork 0
forked from Fuji/Fuji

Reapply "Merge branch 'main' of https://git.wpkg.ovh/Fuji/Fuji"

This reverts commit c58a24a4c6.
This commit is contained in:
Marioneq 2025-02-08 11:19:13 +01:00
parent c58a24a4c6
commit 330d4f694b
32 changed files with 0 additions and 430 deletions

View file

@ -1,152 +0,0 @@
from datetime import datetime
import requests
import json
from bs4 import BeautifulSoup
from impl.hebece.src.signer import *
from impl.hebece.src.utils import *
from impl.hebece.src.const import *
session = requests.Session()
certificate, fingerprint, private_key = generate_key_pair()
def getDebugInfo(data):
data = json.loads(data)
status = data.get("Status", {})
code = status.get("Code")
message = status.get("Message")
return code, message
def makeRequest(url):
digest, canonical_url, signature = get_signature_values(fingerprint, private_key, body=None, full_url=url, timestamp=datetime.now())
headers = makeHeader(signature, canonical_url)
response = requests.get(url, headers=headers)
content = response.text
dinfo = getDebugInfo(content)
return content, dinfo
def APILogin(login, password):
url = "https://eduvulcan.pl/"
response1 = session.get(url)
url = "https://eduvulcan.pl/logowanie"
response2 = session.get(url)
soup = BeautifulSoup(response2.text, 'html.parser')
token_input = soup.find('input', {'name': '__RequestVerificationToken'})
token = {"__RequestVerificationToken": token_input['value']}
cookies = {**response1.cookies.get_dict(), **response2.cookies.get_dict()}
cookies_str = "; ".join([f"{key}={value}" for key, value in cookies.items()])
cookies_str += f"; __RequestVerificationToken={token_input['value']}"
# Prometheus
url = "https://eduvulcan.pl/logowanie?ReturnUrl=%2fapi%2fap"
headers = makeLoginHeader(cookies_str)
data = {
"Alias": login,
"Password": password,
"captchaUser": "",
"__RequestVerificationToken": token_input['value'],
}
response = session.post(url, headers=headers, data=data)
content = response.text
cookie_jar = response.cookies.get_dict()
try:
soup = BeautifulSoup(content, "html.parser")
input_element = soup.find("input", {"id": "ap"})
value = input_element["value"]
parsed_json = json.loads(value)
tokens = parsed_json.get("Tokens", [])
token = " ".join(tokens)
return token
except TypeError:
pass
def JWTLogin(token, debug=False):
tenant = get_tenant_from_jwt(token)
url = f"https://lekcjaplus.vulcan.net.pl/{tenant}{JWT}"
RequestId = getRandomIdentifier()
SelfIdentifier = getRandomIdentifier()
Certificate = certificate
CertificateThumbprint = fingerprint
Tokens = token
digest, canonical_url, signature = get_signature_values(fingerprint, private_key, body=None, full_url=url, timestamp=datetime.now())
headers = makeHeader(signature, canonical_url)
timestamp = datetime.now()
date = getDate()
body = {
"AppName": "DzienniczekPlus 3.0",
"AppVersion": "24.11.07 (G)",
"NotificationToken": None,
"API": 1,
"RequestId": str(RequestId),
"Timestamp": getTimestamp(),
"TimestampFormatted": str(date),
"Envelope": {
"OS": DEVICE_OS,
"Certificate": Certificate,
"CertificateType": "X509",
"DeviceModel": DEVICE,
"SelfIdentifier": str(SelfIdentifier),
"CertificateThumbprint": CertificateThumbprint,
"Tokens": [Tokens]
}
}
body_json = json.dumps(body, indent=4)
response = session.post(url, headers=headers, data=body_json)
content = response.text
if debug:
dinfo = getDebugInfo(content)
return content, dinfo
return content
def HEBELogin(tenant, debug=False):
url = f"https://lekcjaplus.vulcan.net.pl/{tenant}{HEBE}?mode=2&lastSyncDate=1970-01-01%2001%3A00%3A00"
content, dinfo = makeRequest(url)
return content, dinfo
def getLuckyNumber(tenant, schoolid, pupilid, constituentid, debug=False):
timestamp = datetime.now()
date = timestamp.strftime("%Y-%m-%d")
url = f"https://lekcjaplus.vulcan.net.pl/{tenant}/{schoolid}{LUCKY}?pupilId={pupilid}&constituentId={constituentid}&day={date}"
content, dinfo = makeRequest(url)
return content, dinfo
def getGrades(tenant, schoolid, pupilid, unitid, periodid, debug=False):
url = f"https://lekcjaplus.vulcan.net.pl/{tenant}/{schoolid}/api/mobile/grade/byPupil?unitId={unitid}&pupilId={pupilid}&periodId={periodid}&lastSyncDate=1970-01-01%2001%3A00%3A00&lastId=-2147483648&pageSize=500"
content, dinfo = makeRequest(url)
return content, dinfo
def getTimetable(tenant, schoolid, pupilid, start_date, end_date, debug=False):
url = f"https://lekcjaplus.vulcan.net.pl/{tenant}/{schoolid}/api/mobile/schedule/withchanges/byPupil?pupilId={pupilid}&dateFrom={start_date}&dateTo={end_date}&lastId=-2147483648&pageSize=500&lastSyncDate=1970-01-01%2001%3A00%3A00"
content, dinfo = makeRequest(url)
return content, dinfo
def getExams(tenant, schoolid, pupilid, start_date, end_date, debug=False):
url = f"https://lekcjaplus.vulcan.net.pl/{tenant}/{schoolid}/api/mobile/exam/byPupil?pupilId={pupilid}&dateFrom={start_date}&dateTo={end_date}&lastId=-2147483648&pageSize=500&lastSyncDate=1970-01-01%2001%3A00%3A00"
content, dinfo = makeRequest(url)
return content, dinfo

View file

@ -1,52 +0,0 @@
from impl.hebece.src.utils import *
# Endpoints
JWT = "/api/mobile/register/jwt"
HEBE = "/api/mobile/register/hebe"
LUCKY = "/api/mobile/school/lucky"
GRADES = "/api/mobile/grade/byPupil"
TIMETABLE = "/api/mobile/schedule/withchanges/byPupil"
EXAMS = "/api/mobile/exam/byPupil"
# Header
DEVICE = "SM-G935F"
DEVICE_OS = "Android"
APPVERSION = "24.11.07 (G)"
def makeHeader(signature, canonical_url):
return {
"accept-encoding": "gzip",
"content-type": "application/json",
"host": "lekcjaplus.vulcan.net.pl",
"signature": signature,
"user-agent": "Dart/3.3 (dart:io)",
"vapi": "1",
"vcanonicalurl": canonical_url,
"vdate": getDate(),
"vos": DEVICE_OS,
"vversioncode": "640",
}
def makeLoginHeader(cookies):
return {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Accept-Language": "en-US,en;q=0.9",
"Cache-Control": "max-age=0",
"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded",
"Cookie": cookies,
"Host": "eduvulcan.pl",
"Origin": "https://eduvulcan.pl",
"Referer": "https://eduvulcan.pl/logowanie?ReturnUrl=%2fapi%2fap",
"sec-ch-ua": "\"Chromium\";v=\"130\", \"Android WebView\";v=\"130\", \"Not?A_Brand\";v=\"99\"",
"sec-ch-ua-mobile": "?1",
"sec-ch-ua-platform": "\"Android\"",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Linux; Android 13; SM-G935F Build/TQ3A.230901.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Mobile Safari/537.36",
"X-Requested-With": "pl.edu.vulcan.hebe.ce",
}

View file

@ -1,37 +0,0 @@
import requests
import json
import uuid
import hashlib
import sqlite3
import os
import base64
from impl.hebece.src.signer import *
from impl.hebece.src.utils import *
from impl.hebece.src.api import *
from datetime import datetime, timedelta
from bs4 import BeautifulSoup
def getUserInfo(tenant):
content, dinfo = HEBELogin(tenant)
data = json.loads(content)
envelope = data.get("Envelope", [])[0]
pupil = envelope.get("Pupil", {})
unit = envelope.get("Unit", {})
links = envelope.get("Links", {})
ConstituentUnit = envelope.get("ConstituentUnit", {})
periods = envelope.get("Periods", [])
Name = pupil.get("FirstName", {})
SecondName = pupil.get("SecondName", {})
Surname = pupil.get("Surname", {})
Class = envelope.get("ClassDisplay", {})
PupilID = pupil.get("Id", {})
SchoolID = links.get("Symbol", {})
ConstituentID = ConstituentUnit.get("Id", {})
UnitID = unit.get("Id", {})
PeriodID = next((period.get('Id') for period in periods if period.get('Current')), None)
return Name, SecondName, Surname, Class, PupilID, SchoolID, ConstituentID, UnitID, PeriodID

View file

@ -1,98 +0,0 @@
import cryptography
import re
import urllib
import base64
from OpenSSL import crypto
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.serialization import load_der_private_key
from cryptography.hazmat.backends import default_backend
def get_encoded_path(full_url):
path = re.search(r"(api/mobile/.+)", full_url)
if path is None:
raise ValueError(
"The URL does not seem correct (does not match `(api/mobile/.+)` regex)"
)
return urllib.parse.quote(path[1], safe="").lower()
def get_digest(body):
if not body:
return None
m = hashlib.sha256()
m.update(bytes(body, "utf-8"))
return base64.b64encode(m.digest()).decode("utf-8")
def get_headers_list(body, digest, canonical_url, timestamp):
sign_data = [
["vCanonicalUrl", canonical_url],
["Digest", digest] if body else None,
["vDate", timestamp.strftime("%a, %d %b %Y %H:%M:%S GMT")],
]
return (
" ".join(item[0] for item in sign_data if item),
"".join(item[1] for item in sign_data if item),
)
def get_signature(data, private_key):
# Convert data to a string representatio
data_str = (
json.dumps(data)
if isinstance(data, (dict, list))
else str(data)
)
# Decode the base64 private key and load it
private_key_bytes = base64.b64decode(private_key)
pkcs8_key = load_der_private_key(private_key_bytes, password=None, backend=default_backend())
# Sign the data
signature = pkcs8_key.sign(
bytes(data_str, "utf-8"),
padding.PKCS1v15(),
hashes.SHA256()
)
# Encode the signature in base64 and return
return base64.b64encode(signature).decode("utf-8")
def get_signature_values(fingerprint, private_key, body, full_url, timestamp):
canonical_url = get_encoded_path(full_url)
digest = get_digest(body)
headers, values = get_headers_list(body, digest, canonical_url, timestamp)
signature = get_signature(values, private_key)
return (
"SHA-256={}".format(digest) if digest else None,
canonical_url,
'keyId="{}",headers="{}",algorithm="sha256withrsa",signature=Base64(SHA256withRSA({}))'.format(
fingerprint, headers, signature
),
)
def pem_getraw(pem):
return pem.decode("utf-8").replace("\n", "").split("-----")[2]
def generate_key_pair():
pkcs8 = crypto.PKey()
pkcs8.generate_key(crypto.TYPE_RSA, 2048)
x509 = crypto.X509()
x509.set_version(2)
x509.set_serial_number(1)
subject = x509.get_subject()
subject.CN = "APP_CERTIFICATE CA Certificate"
x509.set_issuer(subject)
x509.set_pubkey(pkcs8)
x509.sign(pkcs8, "sha256")
x509.gmtime_adj_notBefore(0)
x509.gmtime_adj_notAfter(20 * 365 * 24 * 60 * 60)
certificate = pem_getraw(crypto.dump_certificate(crypto.FILETYPE_PEM, x509))
fingerprint = x509.digest("sha1").decode("utf-8").replace(":", "").lower()
private_key = pem_getraw(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkcs8))
return certificate, fingerprint, private_key

View file

@ -1,54 +0,0 @@
import base64
import json
import uuid
from datetime import datetime, timedelta
def encodebase64(data):
return base64.b64encode(data.encode("utf-8")).decode("utf-8")
def decodebase64(data):
return base64.b64decode(data.encode("utf-8")).decode("utf-8")
def get_tenant_from_jwt(token):
try:
# Split the JWT into parts
header, payload, signature = token.split('.')
# Decode the payload from Base64
# Add padding
payload += '=' * (-len(payload) % 4)
decoded_payload = base64.urlsafe_b64decode(payload).decode('utf-8')
# Parse the payload as JSON
payload_json = json.loads(decoded_payload)
# Return the tenant
return payload_json.get('tenant')
except (ValueError, json.JSONDecodeError, KeyError) as e:
print(f"Error decoding JWT: {e}")
return None
def getRandomIdentifier():
ruuid = str(uuid.uuid4())
return ruuid
def get_current_week():
# Get today's date
today = datetime.today()
# Calculate the start of the week (Monday)
start_of_week = today - timedelta(days=today.weekday())
# Calculate the end of the week (Sunday)
end_of_week = start_of_week + timedelta(days=6)
# Return the dates as formatted strings
return start_of_week.strftime('%Y-%m-%d'), end_of_week.strftime('%Y-%m-%d')
def getDate():
return datetime.now().strftime("%a, %d %b %Y %H:%M:%S GMT")
def getTimestamp():
now = datetime.now()
Timestamp = now.timestamp()
return Timestamp

View file

@ -1,37 +0,0 @@
from impl.hebece.src.api import *
from impl.hebece.src.parser import *
from impl.hebece.src.utils import *
from impl.hebece.src.signer import *
if __name__ == '__main__':
today = datetime.today().strftime('%d-%m-%y')
start_date, end_date = get_current_week()
token = APILogin(login = input("login: "),password = input("password: "))
if not token:
print("You entered wrong login, password or VULCAN asked for captcha. Verify your login and password and try to log into eduVULCAN from your browser.")
input("Press Enter to exit...")
exit()
tenant = get_tenant_from_jwt(token)
content, dinfoJWT = JWTLogin(token, debug=True)
content, dinfoHEBE = HEBELogin(tenant, debug=True)
Name, SecondName, Surname, Class, PupilID, SchoolID, ConstituentID, UnitID, PeriodID = getUserInfo(tenant)
content, dinfoLUCK = getLuckyNumber(tenant=tenant, schoolid=SchoolID, pupilid=PupilID, constituentid=ConstituentID, debug=True)
content, dinfoGRADE = getGrades(tenant=tenant, schoolid=SchoolID, pupilid=PupilID, unitid=UnitID, periodid=PeriodID, debug=True)
content, dinfoTIME = getTimetable(tenant=tenant, schoolid=SchoolID, pupilid=PupilID, start_date=start_date, end_date=end_date, debug=True)
content, dinfoEXAM = getExams(tenant=tenant, schoolid=SchoolID, pupilid=PupilID, start_date=start_date, end_date=end_date, debug=True)
print(f"\nJWT Status: {dinfoJWT[0]} {dinfoJWT[1]}")
print(f"HEBE Status: {dinfoHEBE[0]} {dinfoHEBE[1]}")
print(f"Lucky Number Status: {dinfoLUCK[0]} {dinfoLUCK[1]}")
print(f"Grades Status: {dinfoGRADE[0]} {dinfoGRADE[1]}")
print(f"Timetable Status: {dinfoTIME[0]} {dinfoTIME[1]}")
print(f"Exams Status: {dinfoEXAM[0]} {dinfoEXAM[1]}")