rbeesoft 2.0.6__tar.gz → 2.0.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/PKG-INFO +1 -1
  2. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/pyproject.toml +1 -1
  3. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/main.py +5 -1
  4. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/ui/processes/processrunner.py +1 -1
  5. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/ui/rbeesoftmainwindow.py +7 -7
  6. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/common/__init__.py +2 -1
  7. rbeesoft-2.0.7/src/rbeesoft/common/license.py +38 -0
  8. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/common/licensemanager.py +13 -5
  9. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/common/logmanager.py +1 -1
  10. rbeesoft-2.0.6/src/rbeesoft/app/generatekeys.py +0 -37
  11. rbeesoft-2.0.6/src/rbeesoft/app/license.json +0 -15
  12. rbeesoft-2.0.6/src/rbeesoft/app/signlicense.py +0 -76
  13. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/README.md +0 -0
  14. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/__init__.py +0 -0
  15. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/__init__.py +0 -0
  16. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/core/__init__.py +0 -0
  17. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/ui/__init__.py +0 -0
  18. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/ui/processes/__init__.py +0 -0
  19. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/ui/processes/dummyprocess.py +0 -0
  20. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/ui/processes/process.py +0 -0
  21. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/ui/settings.py +0 -0
  22. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/ui/widgets/__init__.py +0 -0
  23. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/ui/widgets/centraldockwidget.py +0 -0
  24. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/ui/widgets/logdockwidget.py +0 -0
  25. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/ui/widgets/pages/__init__.py +0 -0
  26. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/ui/widgets/pages/page.py +0 -0
  27. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/app/ui/widgets/pages/pagerouter.py +0 -0
  28. /rbeesoft-2.0.6/src/rbeesoft/common/singleton.py → /rbeesoft-2.0.7/src/rbeesoft/common/decorators.py +0 -0
  29. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/common/exceptions/__init__.py +0 -0
  30. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/common/exceptions/licenseexception.py +0 -0
  31. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/common/logmanagerlistener.py +0 -0
  32. {rbeesoft-2.0.6 → rbeesoft-2.0.7}/src/rbeesoft/webapp/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rbeesoft
3
- Version: 2.0.6
3
+ Version: 2.0.7
4
4
  Summary:
5
5
  Author: Brecheisen
6
6
  Author-email: r.brecheisen@maastrichtuniversity.nl
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "rbeesoft"
3
- version = "2.0.6"
3
+ version = "2.0.7"
4
4
  description = ""
5
5
  authors = [
6
6
  {name = "Brecheisen", email = "r.brecheisen@maastrichtuniversity.nl"}
@@ -4,6 +4,7 @@ from rbeesoft.app.ui import RbeesoftMainWindow
4
4
  from rbeesoft.app.ui.widgets.pages import Page
5
5
  from rbeesoft.app.ui.processes import Process
6
6
  from rbeesoft.app.ui.processes import ProcessRunner
7
+ from rbeesoft.common import LicenseManager
7
8
 
8
9
 
9
10
  class MainWindow(RbeesoftMainWindow):
@@ -32,7 +33,10 @@ class HomePage(Page):
32
33
  layout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
33
34
  layout.addWidget(QtWidgets.QLabel(self.title()))
34
35
  layout.addWidget(button)
35
- layout.addWidget(process_button)
36
+ manager = LicenseManager(settings)
37
+ license = manager.license()
38
+ if license.has_all_features('x'):
39
+ layout.addWidget(process_button)
36
40
  self.setLayout(layout)
37
41
 
38
42
  def handle_button(self):
@@ -1,4 +1,4 @@
1
- from rbeesoft.common.singleton import singleton
1
+ from rbeesoft.common.decorators import singleton
2
2
  from rbeesoft.common.logmanager import LogManager
3
3
 
4
4
  LOG = LogManager()
@@ -3,11 +3,11 @@ from PySide6.QtCore import Qt, QByteArray
3
3
  from PySide6.QtWidgets import QMainWindow, QStyle, QFileDialog
4
4
  from PySide6.QtGui import QGuiApplication, QAction
5
5
  from rbeesoft.app.ui.settings import Settings
6
- from rbeesoft.app.ui.widgets import CentralDockWidget
7
- from rbeesoft.app.ui.widgets import LogDockWidget
8
- from rbeesoft.common import LicenseManager
9
- from rbeesoft.common.exceptions import LicenseException
10
- from rbeesoft.common import LogManager
6
+ from rbeesoft.app.ui.widgets.centraldockwidget import CentralDockWidget
7
+ from rbeesoft.app.ui.widgets.logdockwidget import LogDockWidget
8
+ from rbeesoft.common.licensemanager import LicenseManager
9
+ from rbeesoft.common.exceptions.licenseexception import LicenseException
10
+ from rbeesoft.common.logmanager import LogManager
11
11
 
12
12
  LOG = LogManager()
13
13
 
@@ -87,7 +87,7 @@ class RbeesoftMainWindow(QMainWindow):
87
87
 
88
88
  def license_manager(self):
89
89
  if not self._license_manager:
90
- self._license_manager = LicenseManager(self.settings().get('public_key', None))
90
+ self._license_manager = LicenseManager(self.settings())
91
91
  return self._license_manager
92
92
 
93
93
  def license(self):
@@ -116,7 +116,7 @@ class RbeesoftMainWindow(QMainWindow):
116
116
  file_path = self.settings().get('mainwindow/license_file', None)
117
117
  if file_path:
118
118
  try:
119
- self._license = self.license_manager().verify(file_path)
119
+ self._license = self.license_manager().check_license(file_path)
120
120
  LOG.info(f'License found at {file_path}')
121
121
  LOG.info('License OK')
122
122
  return True
@@ -1,3 +1,4 @@
1
1
  from rbeesoft.common.logmanager import LogManager
2
2
  from rbeesoft.common.logmanagerlistener import LogManagerListener
3
- from rbeesoft.common.licensemanager import LicenseManager
3
+ from rbeesoft.common.licensemanager import LicenseManager
4
+ from rbeesoft.common.license import License
@@ -0,0 +1,38 @@
1
+ import time
2
+
3
+
4
+ class License:
5
+ def __init__(self, payload):
6
+ self._payload = payload
7
+
8
+ def customer(self):
9
+ return self._payload['customer']
10
+
11
+ def product(self):
12
+ return self._payload['product']
13
+
14
+ def feature_list(self):
15
+ return self._payload['features']
16
+
17
+ def expiry_days(self):
18
+ return int(self._payload['exp'])
19
+
20
+ def id(self):
21
+ return self._payload['license_id']
22
+
23
+ # CHECKS
24
+
25
+ def is_expired(self):
26
+ now = int(time.time())
27
+ if now > self.expiry_days():
28
+ return True
29
+ return False
30
+
31
+ def is_valid(self):
32
+ return not self.is_expired()
33
+
34
+ def has_all_features(self):
35
+ return 'all' in self.feature_list()
36
+
37
+ def has_feature(self, name):
38
+ return name in self.feature_list()
@@ -3,19 +3,26 @@ import time
3
3
  import base64
4
4
  from pathlib import Path
5
5
  from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
6
- from rbeesoft.common.exceptions.licenseexception import LicenseException
6
+ from rbeesoft.common.exceptions import LicenseException
7
+ from rbeesoft.common.license import License
8
+ from rbeesoft.common.decorators import singleton
7
9
 
8
10
 
11
+ @singleton
9
12
  class LicenseManager:
10
- def __init__(self, public_key):
13
+ def __init__(self, settings):
11
14
  self._file_path = None
12
- self._public_key = public_key
15
+ self._public_key = settings.get('public_key', None)
16
+ self._license = None
13
17
 
14
18
  def file_path(self):
15
19
  return self._file_path
16
20
 
17
21
  def public_key(self):
18
22
  return self._public_key
23
+
24
+ def license(self):
25
+ return self._license
19
26
 
20
27
  def canonical_json_bytes(self, obj: dict) -> bytes:
21
28
  return json.dumps(
@@ -25,7 +32,7 @@ class LicenseManager:
25
32
  ensure_ascii=False
26
33
  ).encode("utf-8")
27
34
 
28
- def verify(self, file_path):
35
+ def check_license(self, file_path):
29
36
  if isinstance(file_path, str):
30
37
  file_path = Path(file_path)
31
38
  if not file_path.exists():
@@ -44,6 +51,7 @@ class LicenseManager:
44
51
  exp = int(payload["exp"])
45
52
  if now > exp:
46
53
  raise LicenseException('License expired')
47
- return payload
54
+ self._license = License(payload)
55
+ return self._license
48
56
  except Exception as e:
49
57
  raise LicenseException(f'Invalid license: {e}')
@@ -2,7 +2,7 @@ import os
2
2
  import atexit
3
3
  import datetime
4
4
  from pathlib import Path
5
- from rbeesoft.common.singleton import singleton
5
+ from rbeesoft.common.decorators import singleton
6
6
 
7
7
 
8
8
  @singleton
@@ -1,37 +0,0 @@
1
- import os
2
- import base64
3
- from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
4
- from cryptography.hazmat.primitives import serialization
5
- from pathlib import Path
6
-
7
- # RUN THIS ONLY ONCE!!! You can distribute the public key to clients
8
-
9
-
10
- def main():
11
- output_dir = Path.home() / 'keys'
12
- os.makedirs(output_dir, exist_ok=True)
13
- priv = Ed25519PrivateKey.generate()
14
- pub = priv.public_key()
15
- priv_bytes = priv.private_bytes(
16
- encoding=serialization.Encoding.Raw,
17
- format=serialization.PrivateFormat.Raw,
18
- encryption_algorithm=serialization.NoEncryption(),
19
- )
20
- pub_bytes = pub.public_bytes(
21
- encoding=serialization.Encoding.Raw,
22
- format=serialization.PublicFormat.Raw,
23
- )
24
- priv_file = output_dir / 'ed25519_private.key'
25
- priv_file.write_bytes(priv_bytes)
26
- pub_file = output_dir / 'ed25519_public.key'
27
- pub_file.write_bytes(pub_bytes)
28
- print("Wrote:")
29
- print(" - $HOME/keys/ed25519_private.key (KEEP SECRET)")
30
- print(" - $HOME/keys/ed25519_public.key (embed in app)")
31
- print("\nPublic key (base64) to embed:")
32
- print(base64.b64encode(pub_bytes).decode("ascii"))
33
-
34
-
35
- if __name__ == '__main__':
36
- main()
37
-
@@ -1,15 +0,0 @@
1
- {
2
- "payload": {
3
- "customer": "Ralph Example",
4
- "product": "MyPySideApp Pro",
5
- "features": [
6
- "pro",
7
- "export_pdf"
8
- ],
9
- "issued_at": 1770031472,
10
- "exp": 1772623472,
11
- "license_id": "LIC-1770031472"
12
- },
13
- "signature": "2NPw5sb9zeOF3b6Sm5hrxWcne/cX0G0vJwVJ+bG+0i7/WY6SJyCNcmN5v+2eBe7WZjs+E+c33JOA7mX8BLTmBA==",
14
- "alg": "Ed25519"
15
- }
@@ -1,76 +0,0 @@
1
- import base64
2
- import argparse
3
- import json
4
- import time
5
- from pathlib import Path
6
- from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
7
-
8
- def canonical_json_bytes(obj: dict) -> bytes:
9
- # Deterministic JSON -> same bytes everywhere
10
- return json.dumps(
11
- obj,
12
- sort_keys=True,
13
- separators=(",", ":"),
14
- ensure_ascii=False
15
- ).encode("utf-8")
16
-
17
- def load_private_key(path: Path) -> Ed25519PrivateKey:
18
- key_bytes = path.read_bytes()
19
- return Ed25519PrivateKey.from_private_bytes(key_bytes)
20
-
21
- def make_license_payload(
22
- customer: str,
23
- product: str,
24
- expires_days_from_now: int,
25
- features: list[str],
26
- machine_fp: str | None = None,
27
- ) -> dict:
28
- now = int(time.time())
29
- exp = now + expires_days_from_now * 24 * 3600
30
- payload = {
31
- "customer": customer,
32
- "product": product,
33
- "features": features,
34
- "issued_at": now,
35
- "exp": exp,
36
- "license_id": f"LIC-{now}",
37
- }
38
- # Optional machine binding (keep None for portable)
39
- if machine_fp:
40
- payload["machine_fp"] = machine_fp
41
- return payload
42
-
43
- def sign_license(payload: dict, priv: Ed25519PrivateKey) -> dict:
44
- msg = canonical_json_bytes(payload)
45
- sig = priv.sign(msg)
46
- return {
47
- "payload": payload,
48
- "signature": base64.b64encode(sig).decode("ascii"),
49
- "alg": "Ed25519",
50
- }
51
-
52
- def main():
53
- parser = argparse.ArgumentParser()
54
- parser.add_argument('private_key_path', help='Path to private key file')
55
- parser.add_argument('output_dir', help='Path to output directory where license.json is written')
56
- parser.add_argument('product', help='Name of product')
57
- parser.add_argument('expires_days_from_now', help='Number of days to expire', type=int)
58
- parser.add_argument('--features', help='Feature list', default='')
59
- args = parser.parse_args()
60
- priv_path = Path(args.private_key_path) # Path.home() / 'keys/ed25519_private.key'
61
- out_path = Path(args.output_dir) / 'license.json'
62
- priv = load_private_key(priv_path)
63
- payload = make_license_payload(
64
- customer='Default customer',
65
- product=args.product,
66
- expires_days_from_now=args.expires_days_from_now,
67
- features=[x.strip() for x in args.features.split(',')],
68
- machine_fp=None, # set to a fingerprint to bind to a device
69
- )
70
- signed = sign_license(payload, priv)
71
- out_path.write_text(json.dumps(signed, indent=2, ensure_ascii=False), encoding="utf-8")
72
- print(f"Wrote signed license to: {out_path.resolve()}")
73
- print(f"Expires at (unix): {payload['exp']}")
74
-
75
- if __name__ == "__main__":
76
- main()
File without changes