analytics-platform 0.3.0__tar.gz → 0.3.1__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. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/PKG-INFO +11 -1
  2. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/README.md +10 -0
  3. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/__init__.py +4 -0
  4. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/core.py +1 -1
  5. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/proxy.py +10 -0
  6. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform.egg-info/PKG-INFO +11 -1
  7. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/pyproject.toml +1 -1
  8. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/tests/test_proxy.py +6 -1
  9. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/LICENSE +0 -0
  10. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/analytics.py +0 -0
  11. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/audit.py +0 -0
  12. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/crawlers.py +0 -0
  13. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/django.py +0 -0
  14. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/errors.py +0 -0
  15. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/firewall.py +0 -0
  16. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/flags.py +0 -0
  17. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/forms.py +0 -0
  18. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/inventory.py +0 -0
  19. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/maintenance.py +0 -0
  20. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/performance.py +0 -0
  21. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/posture.py +0 -0
  22. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/request_rules.py +0 -0
  23. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform/resources.py +0 -0
  24. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform.egg-info/SOURCES.txt +0 -0
  25. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform.egg-info/dependency_links.txt +0 -0
  26. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/analytics_platform.egg-info/top_level.txt +0 -0
  27. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/setup.cfg +0 -0
  28. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/tests/test_crawlers.py +0 -0
  29. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/tests/test_errors.py +0 -0
  30. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/tests/test_firewall.py +0 -0
  31. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/tests/test_request_rules.py +0 -0
  32. {analytics_platform-0.3.0 → analytics_platform-0.3.1}/tests/test_resources.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: analytics-platform
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Firewall, error monitoring and server analytics for your Python server, run from your analytics dashboard.
5
5
  Author: steviec87
6
6
  License-Expression: MIT
@@ -179,6 +179,16 @@ Their errors carry the job's name as the route, a job that raises is sent
179
179
  (status 500) and raised on, and each run is timed (Server speed → Jobs).
180
180
  Several services on one site: `Platform(..., server_name="worker")`.
181
181
 
182
+ **Several apps on one site** (microservices, a worker, a Node service next to
183
+ this one): install the package in each, with the same `site_id`, `secret` and
184
+ `endpoint`, and a `server_name` of its own. Errors, Server speed, Crawlers and
185
+ Security → Updates then tell the apps apart. Without one, each app is named
186
+ after its machine: two apps on one machine are reported as one, and only the
187
+ last package list sent is kept. A service only your other apps call takes
188
+ `firewall=False, proxy=False, maintenance=False` (its requests all come from
189
+ your own apps, so the rate limit would see one visitor); on a machine shared
190
+ by several apps, keep `resources` on in only one of them.
191
+
182
192
  Options: `server_name`, `root` (traceback paths relative to it),
183
193
  `capture_uncaught`, `capture_logging`, `spool_dir` (where unsent errors wait;
184
194
  `False` for none), `before_send(event, error)` (change an error, or return
@@ -153,6 +153,16 @@ Their errors carry the job's name as the route, a job that raises is sent
153
153
  (status 500) and raised on, and each run is timed (Server speed → Jobs).
154
154
  Several services on one site: `Platform(..., server_name="worker")`.
155
155
 
156
+ **Several apps on one site** (microservices, a worker, a Node service next to
157
+ this one): install the package in each, with the same `site_id`, `secret` and
158
+ `endpoint`, and a `server_name` of its own. Errors, Server speed, Crawlers and
159
+ Security → Updates then tell the apps apart. Without one, each app is named
160
+ after its machine: two apps on one machine are reported as one, and only the
161
+ last package list sent is kept. A service only your other apps call takes
162
+ `firewall=False, proxy=False, maintenance=False` (its requests all come from
163
+ your own apps, so the rate limit would see one visitor); on a machine shared
164
+ by several apps, keep `resources` on in only one of them.
165
+
156
166
  Options: `server_name`, `root` (traceback paths relative to it),
157
167
  `capture_uncaught`, `capture_logging`, `spool_dir` (where unsent errors wait;
158
168
  `False` for none), `before_send(event, error)` (change an error, or return
@@ -100,6 +100,10 @@ class Platform:
100
100
  # so its errors, timings and resources can be told apart.
101
101
  if server_name and name in ("errors", "performance", "crawlers", "resources"):
102
102
  opts.setdefault("server_name", server_name)
103
+ # Inventory and posture call it host, and are stored one per site
104
+ # and host: without it two services on one machine overwrite each other's.
105
+ if server_name and name in ("inventory", "posture"):
106
+ opts.setdefault("host", server_name)
103
107
  try:
104
108
  self._modules[name] = _load(name, self.core, opts)
105
109
  except Exception as error:
@@ -16,7 +16,7 @@ import urllib.error
16
16
  import urllib.parse
17
17
  import urllib.request
18
18
 
19
- VERSION = "0.3.0"
19
+ VERSION = "0.3.1"
20
20
  TIMEOUT = 5
21
21
  MIN_SYNC = 30
22
22
  RETRY = 60
@@ -47,6 +47,13 @@ def prefix_for(secret):
47
47
  return hashlib.sha256(("proxy|" + str(secret)).encode("utf-8")).hexdigest()[:8]
48
48
 
49
49
 
50
+ # Sent as X-Proxy-Key, so the collector believes the visitor addresses this
51
+ # proxy passes on: anyone can write X-Forwarded-For. Worked out from the secret
52
+ # as the collector does (expresscollect/utils/proxyKey.js).
53
+ def key_for(secret):
54
+ return hashlib.sha256(("proxykey|" + str(secret)).encode("utf-8")).hexdigest()[:32]
55
+
56
+
50
57
  class Proxy:
51
58
  """prefix: the path it answers on (default: from the secret)."""
52
59
 
@@ -54,6 +61,7 @@ class Proxy:
54
61
  self.core = core
55
62
  self.enabled = enabled
56
63
  self.prefix = re.sub(r"[^A-Za-z0-9_-]", "", str(prefix or prefix_for(core.secret)))
64
+ self.key = key_for(core.secret) if core.secret else ""
57
65
  # One copy of each tracker file, so a repeat visitor costs no upstream
58
66
  # request. A handful of small files: it never grows.
59
67
  self._files = {}
@@ -139,6 +147,8 @@ class Proxy:
139
147
  # The visitor's address: the collector needs it for the country, the
140
148
  # daily visitor ID and the per-visitor rate limit.
141
149
  out = {"X-Forwarded-For": client_ip or ""}
150
+ if self.key:
151
+ out["X-Proxy-Key"] = self.key
142
152
  for name in PASS_HEADERS:
143
153
  value = headers.get(name)
144
154
  if value:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: analytics-platform
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Firewall, error monitoring and server analytics for your Python server, run from your analytics dashboard.
5
5
  Author: steviec87
6
6
  License-Expression: MIT
@@ -179,6 +179,16 @@ Their errors carry the job's name as the route, a job that raises is sent
179
179
  (status 500) and raised on, and each run is timed (Server speed → Jobs).
180
180
  Several services on one site: `Platform(..., server_name="worker")`.
181
181
 
182
+ **Several apps on one site** (microservices, a worker, a Node service next to
183
+ this one): install the package in each, with the same `site_id`, `secret` and
184
+ `endpoint`, and a `server_name` of its own. Errors, Server speed, Crawlers and
185
+ Security → Updates then tell the apps apart. Without one, each app is named
186
+ after its machine: two apps on one machine are reported as one, and only the
187
+ last package list sent is kept. A service only your other apps call takes
188
+ `firewall=False, proxy=False, maintenance=False` (its requests all come from
189
+ your own apps, so the rate limit would see one visitor); on a machine shared
190
+ by several apps, keep `resources` on in only one of them.
191
+
182
192
  Options: `server_name`, `root` (traceback paths relative to it),
183
193
  `capture_uncaught`, `capture_logging`, `spool_dir` (where unsent errors wait;
184
194
  `False` for none), `before_send(event, error)` (change an error, or return
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "analytics-platform"
7
- version = "0.3.0"
7
+ version = "0.3.1"
8
8
  description = "Firewall, error monitoring and server analytics for your Python server, run from your analytics dashboard."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -12,7 +12,7 @@ from http.server import BaseHTTPRequestHandler, HTTPServer
12
12
 
13
13
  sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
14
14
 
15
- from analytics_platform.proxy import Proxy, prefix_for # noqa: E402
15
+ from analytics_platform.proxy import Proxy, key_for, prefix_for # noqa: E402
16
16
 
17
17
  TRACKER = b"// a tracker" + b"x" * 600
18
18
 
@@ -77,6 +77,10 @@ class ProxyTest(unittest.TestCase):
77
77
  Collector.seen = []
78
78
  self.proxy = Proxy(FakeCore(self.endpoint))
79
79
 
80
+ def test_the_proxy_key_is_worked_out_as_the_collector_does(self):
81
+ # The same vector is pinned in the collector, the backend and the other packages.
82
+ self.assertEqual(key_for("0123456789abcdef01234567"), "8fc74dbbd4f088449862841cd29fcaee")
83
+
80
84
  def test_the_address_comes_from_the_secret(self):
81
85
  self.assertEqual(prefix_for("s3cret"), prefix_for("s3cret"))
82
86
  self.assertNotEqual(prefix_for("s3cret"), prefix_for("other"))
@@ -123,6 +127,7 @@ class ProxyTest(unittest.TestCase):
123
127
  self.assertEqual(got["path"], "/collectanalytics/e?v=1")
124
128
  self.assertEqual(got["body"], b'{"event":"pageview"}')
125
129
  self.assertEqual(got["headers"].get("X-Forwarded-For"), "198.51.100.4")
130
+ self.assertEqual(got["headers"].get("X-Proxy-Key"), key_for("s3cret"), "so the collector believes the address")
126
131
  self.assertEqual(got["headers"].get("User-Agent"), "Mozilla/5.0")
127
132
  self.assertIsNone(got["headers"].get("Cookie"), "the site's own cookies stay on the site")
128
133