forked from SystemRage/py-kms
-
Notifications
You must be signed in to change notification settings - Fork 154
Record client ips #141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Record client ips #141
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
eae3e5f
Cleanup
simonmicro 4c1d7b5
Fix warnings due to deprecation
simonmicro 2baf218
Added lastRequstIP and renamed appId to applicationId
simonmicro a5502e5
Actually show exceptions during processing
simonmicro eafe387
Adapt new sqlite-availability check
simonmicro 25679ef
Reformatting and exposed new ip-field
simonmicro af2527a
Also fix legacy format
simonmicro cadcb7a
Typos
simonmicro c8d460f
Corrected parameter bindings
simonmicro 95b4c2e
Add test instructions
simonmicro ca722bb
Fixed another location of db-ordered fields
simonmicro dcf9051
Nitpick newline spaces
simonmicro cd7cbd1
Also test new clients explicitly :/
simonmicro bb9946b
Enforce correct empty default value
simonmicro 24b2630
Added a simple client test
simonmicro f8e4253
Removed now useless error-wrapping
simonmicro 9c83557
Prevent unknown column names from being passed to DB
simonmicro ba9d1f0
Migrated to named columns to prevent any accidential re-order
simonmicro File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| name: "Test: Basic Client" | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| push: | ||
|
|
||
| jobs: | ||
| run-test: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v5 | ||
| - name: Set up Python | ||
| uses: actions/setup-python@v6 | ||
| with: | ||
| python-version: "3.11" | ||
| - name: Install dependencies | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| pip install -r requirements.txt | ||
| - name: Run tests | ||
| run: | | ||
| cd py-kms; timeout 30 python3 pykms_Server.py -F STDOUT -s ./pykms_database.db & | ||
| sleep 5 | ||
| python3 pykms_Client.py -F STDOUT # fresh client | ||
| python3 pykms_Client.py -F STDOUT -c 174f5409-0624-4ce3-b209-adde1091956b # (maybe) existing client | ||
| python3 pykms_Client.py -F STDOUT -c 174f5409-0624-4ce3-b209-adde1091956b # now-for-sure existing client | ||
2 changes: 1 addition & 1 deletion
2
.github/workflows/bake_to_test.yml → .github/workflows/test_image_build.yml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| name: Test-Build Docker Image | ||
| name: "Test: Build Docker Image" | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,121 +1,131 @@ | ||
| #!/usr/bin/env python3 | ||
|
|
||
| import datetime | ||
| from datetime import datetime | ||
| import os | ||
| import logging | ||
|
|
||
| #-------------------------------------------------------------------------------------------------------------------------------------------------------- | ||
|
|
||
| loggersrv = logging.getLogger('logsrv') | ||
| _column_names = ('clientMachineId', 'machineName', 'applicationId', 'skuId', 'licenseStatus', 'lastRequestTime', 'kmsEpid', 'requestCount', 'lastRequestIP') | ||
|
|
||
| # sqlite3 is optional. | ||
| available = False | ||
| try: | ||
| import sqlite3 | ||
| available = True | ||
| except ImportError: | ||
| pass | ||
|
|
||
| from pykms_Format import pretty_printer | ||
|
|
||
| #-------------------------------------------------------------------------------------------------------------------------------------------------------- | ||
|
|
||
| loggersrv = logging.getLogger('logsrv') | ||
|
|
||
| def sql_initialize(dbName): | ||
| if available is False: | ||
| loggersrv.info("'sqlite3' module not found! SQLite database support cannot be enabled.") | ||
| return | ||
| loggersrv.debug(f'SQLite database support enabled. Database file: "{dbName}"') | ||
| if not os.path.isfile(dbName): | ||
| # Initialize the database. | ||
| # Initialize the database | ||
| loggersrv.debug(f'Initializing database file "{dbName}"...') | ||
| con = None | ||
| try: | ||
| con = sqlite3.connect(dbName) | ||
| with sqlite3.connect(dbName) as con: | ||
| cur = con.cursor() | ||
| cur.execute("CREATE TABLE clients(clientMachineId TEXT , machineName TEXT, applicationId TEXT, skuId TEXT, licenseStatus TEXT, lastRequestTime INTEGER, kmsEpid TEXT, requestCount INTEGER, PRIMARY KEY(clientMachineId, applicationId))") | ||
| cur.execute("CREATE TABLE clients(clientMachineId TEXT, machineName TEXT, applicationId TEXT, skuId TEXT, licenseStatus TEXT, lastRequestTime INTEGER, kmsEpid TEXT, requestCount INTEGER, PRIMARY KEY(clientMachineId, applicationId))") | ||
|
simonmicro marked this conversation as resolved.
|
||
|
|
||
| if os.path.isfile(dbName): | ||
| # Update database | ||
| with sqlite3.connect(dbName) as con: | ||
| cur = con.cursor() | ||
| # Create simple "metadata" table if not exists. | ||
| cur.execute("CREATE TABLE IF NOT EXISTS metadata (key TEXT PRIMARY KEY, value TEXT);") | ||
| # Get the current schema version | ||
| cur.execute("SELECT value FROM metadata WHERE key='schema_version';") | ||
| row = cur.fetchone() | ||
| if row is None: | ||
| current_version = 0 | ||
| else: | ||
| current_version = int(row[0]) | ||
| loggersrv.debug(f'Current database schema version: {current_version}') | ||
| # Apply necessary migrations | ||
| if current_version < 1: | ||
| # v1: Add "lastRequestIP" column to "clients" table. | ||
| loggersrv.info("Upgrading database schema to version 1...") | ||
| cur.execute("ALTER TABLE clients ADD COLUMN lastRequestIP TEXT;") | ||
| cur.execute("INSERT OR REPLACE INTO metadata (key, value) VALUES ('schema_version', '1');") | ||
| loggersrv.info("Database schema updated to version 1.") | ||
|
|
||
| except sqlite3.Error as e: | ||
| pretty_printer(log_obj = loggersrv.error, to_exit = True, put_text = "{reverse}{red}{bold}Sqlite Error: %s. Exiting...{end}" %str(e)) | ||
| finally: | ||
| if con: | ||
| con.commit() | ||
| con.close() | ||
|
|
||
| def sql_get_all(dbName): | ||
| if available is False: | ||
| return | ||
| if not os.path.isfile(dbName): | ||
| return None | ||
| with sqlite3.connect(dbName) as con: | ||
| con.row_factory = sqlite3.Row | ||
| cur = con.cursor() | ||
| cur.execute("SELECT * FROM clients") | ||
| cur.execute(f"SELECT {', '.join(_column_names)} FROM clients") | ||
| clients = [] | ||
| for row in cur.fetchall(): | ||
| clients.append({ | ||
| 'clientMachineId': row[0], | ||
| 'machineName': row[1], | ||
| 'applicationId': row[2], | ||
| 'skuId': row[3], | ||
| 'licenseStatus': row[4], | ||
| 'lastRequestTime': datetime.datetime.fromtimestamp(row[5]).isoformat(), | ||
| 'kmsEpid': row[6], | ||
| 'requestCount': row[7] | ||
| }) | ||
| loggersrv.debug(f"Row: {row}") | ||
| obj = {} | ||
| for col_name in _column_names: | ||
| if col_name == "lastRequestTime": | ||
| obj[col_name] = datetime.fromtimestamp(row['lastRequestTime']).isoformat() | ||
| else: | ||
| obj[col_name] = row[col_name] | ||
| loggersrv.debug(f"Obj: {obj}") | ||
| clients.append(obj) | ||
| return clients | ||
|
|
||
| def sql_update(dbName, infoDict): | ||
| con = None | ||
| try: | ||
| con = sqlite3.connect(dbName) | ||
| if available is False: | ||
| return | ||
|
|
||
| # make sure all column names are present | ||
| for col_name in _column_names: | ||
| if col_name in ["requestCount", "kmsEpid"]: | ||
| continue | ||
| if col_name not in infoDict: | ||
| raise ValueError(f"infoDict is missing required column: {col_name}") | ||
|
|
||
| with sqlite3.connect(dbName) as con: | ||
| con.row_factory = sqlite3.Row | ||
| cur = con.cursor() | ||
| cur.execute("SELECT * FROM clients WHERE clientMachineId=:clientMachineId AND applicationId=:appId;", infoDict) | ||
| try: | ||
| data = cur.fetchone() | ||
| if not data: | ||
| # Insert row. | ||
| cur.execute("INSERT INTO clients (clientMachineId, machineName, applicationId, \ | ||
| skuId, licenseStatus, lastRequestTime, requestCount) VALUES (:clientMachineId, :machineName, :appId, :skuId, :licenseStatus, :requestTime, 1);", infoDict) | ||
| else: | ||
| # Update data. | ||
| if data[1] != infoDict["machineName"]: | ||
| cur.execute("UPDATE clients SET machineName=:machineName WHERE \ | ||
| clientMachineId=:clientMachineId AND applicationId=:appId;", infoDict) | ||
| if data[2] != infoDict["appId"]: | ||
| cur.execute("UPDATE clients SET applicationId=:appId WHERE \ | ||
| clientMachineId=:clientMachineId AND applicationId=:appId;", infoDict) | ||
| if data[3] != infoDict["skuId"]: | ||
| cur.execute("UPDATE clients SET skuId=:skuId WHERE \ | ||
| clientMachineId=:clientMachineId AND applicationId=:appId;", infoDict) | ||
| if data[4] != infoDict["licenseStatus"]: | ||
| cur.execute("UPDATE clients SET licenseStatus=:licenseStatus WHERE \ | ||
| clientMachineId=:clientMachineId AND applicationId=:appId;", infoDict) | ||
| if data[5] != infoDict["requestTime"]: | ||
| cur.execute("UPDATE clients SET lastRequestTime=:requestTime WHERE \ | ||
| clientMachineId=:clientMachineId AND applicationId=:appId;", infoDict) | ||
| # Increment requestCount | ||
| cur.execute("UPDATE clients SET requestCount=requestCount+1 WHERE \ | ||
| clientMachineId=:clientMachineId AND applicationId=:appId;", infoDict) | ||
|
|
||
| except sqlite3.Error as e: | ||
| pretty_printer(log_obj = loggersrv.error, to_exit = True, | ||
| put_text = "{reverse}{red}{bold}Sqlite Error: %s. Exiting...{end}" %str(e)) | ||
| except sqlite3.Error as e: | ||
| pretty_printer(log_obj = loggersrv.error, to_exit = True, | ||
| put_text = "{reverse}{red}{bold}Sqlite Error: %s. Exiting...{end}" %str(e)) | ||
| finally: | ||
| if con: | ||
| con.commit() | ||
| con.close() | ||
| cur.execute(f"SELECT {', '.join(_column_names)} FROM clients WHERE clientMachineId=:clientMachineId AND applicationId=:applicationId;", infoDict) | ||
| data = cur.fetchone() | ||
| if not data: | ||
| # Insert new row with all given info | ||
| infoDict["kmsEpid"] = "" # Default empty value | ||
| infoDict["requestCount"] = 1 | ||
| cur.execute(f"""INSERT INTO clients ({', '.join(_column_names)}) | ||
| VALUES ({', '.join(':' + col for col in _column_names)});""", infoDict) | ||
|
|
||
| else: | ||
| # Update only changed columns | ||
| common_postfix = "WHERE clientMachineId=:clientMachineId AND applicationId=:applicationId" | ||
| def update_column_if_changed(column_name, new_value): | ||
| assert "clientMachineId" in infoDict and "applicationId" in infoDict, "infoDict must contain 'clientMachineId' and 'applicationId'" | ||
|
simonmicro marked this conversation as resolved.
|
||
| if column_name not in _column_names: | ||
| raise ValueError(f"Unknown column name: {column_name}") | ||
| if data[column_name] != new_value: | ||
| query = f"UPDATE clients SET {column_name}=:value {common_postfix}" | ||
| cur.execute(query, {"value": new_value, "clientMachineId": infoDict['clientMachineId'], "applicationId": infoDict['applicationId']}) | ||
|
|
||
| # Dynamically check and maybe update all columns | ||
| for column_name in _column_names: | ||
| if column_name in ["clientMachineId", "applicationId", "requestCount"]: | ||
| continue # Skip these columns | ||
| if column_name == "kmsEpid": | ||
| # this one can only be updated by the special function | ||
| continue | ||
| update_column_if_changed(column_name, infoDict[column_name]) | ||
|
|
||
| # Finally increment requestCount | ||
| cur.execute(f"UPDATE clients SET requestCount=requestCount+1 {common_postfix}", infoDict) | ||
|
|
||
|
simonmicro marked this conversation as resolved.
|
||
| def sql_update_epid(dbName, kmsRequest, response, appName): | ||
| if available is False: | ||
| return | ||
|
|
||
| cmid = str(kmsRequest['clientMachineId'].get()) | ||
| con = None | ||
| try: | ||
| con = sqlite3.connect(dbName) | ||
| with sqlite3.connect(dbName) as con: | ||
| cur = con.cursor() | ||
| cur.execute("SELECT * FROM clients WHERE clientMachineId=? AND applicationId=?;", (cmid, appName)) | ||
| try: | ||
| data = cur.fetchone() | ||
| cur.execute("UPDATE clients SET kmsEpid=? WHERE \ | ||
| clientMachineId=? AND applicationId=?;", (str(response["kmsEpid"].decode('utf-16le')), cmid, appName)) | ||
|
|
||
| except sqlite3.Error as e: | ||
| pretty_printer(log_obj = loggersrv.error, to_exit = True, | ||
| put_text = "{reverse}{red}{bold}Sqlite Error: %s. Exiting...{end}" %str(e)) | ||
| except sqlite3.Error as e: | ||
| pretty_printer(log_obj = loggersrv.error, to_exit = True, | ||
| put_text = "{reverse}{red}{bold}Sqlite Error: %s. Exiting...{end}" %str(e)) | ||
| finally: | ||
| if con: | ||
| con.commit() | ||
| con.close() | ||
| cur.execute("UPDATE clients SET kmsEpid=? WHERE clientMachineId=? AND applicationId=?;", | ||
| (str(response["kmsEpid"].decode('utf-16le')), cmid, appName)) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.