geenforce: IP allowlist for client endpoints + admin Settings tab
Fleet PCs on a trusted (vaulted) network can now reach the GE-Enforce client endpoints (manifest, payload, report) without a per-PC token: the auth path accepts a valid geenforce.fetch/report token OR a source IP in the configured allowlist (setting geenforce_allowed_cidrs). Fail-closed; an empty allowlist means the token stays the only path, so existing deployments are unchanged. Rationale: the client token lives in HKLM on every kiosk, so it does not defend against a compromised kiosk anyway - network-perimeter trust is the same practical strength with far less provisioning + no token-rotation churn on a DB wipe. Documented in-UI that this is perimeter trust, not per-device identity. - _ip_allowlisted() (ipaddress, X-Forwarded-For-aware via _client_ip) - /geenforce/config GET/PUT extended with allowedcidrs, server-validated + normalized (bad CIDR -> 400) - new GE-Enforce > Settings tab (GeEnforceSettings.vue) to edit the allowlist in admin, no SQL - 3 regression tests (allow by IP, reject outside list, empty = token required)
This commit is contained in:
@@ -115,6 +115,38 @@ def test_unauthenticated_rejected(client, db, app):
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
def _set_allowlist(app, value):
|
||||
from shopdb.api import Setting
|
||||
with app.app_context():
|
||||
Setting.set('geenforce_allowed_cidrs', value, 'string', 'geenforce')
|
||||
service.db.session.commit()
|
||||
|
||||
|
||||
def test_ip_allowlist_allows_without_token(client, db, app):
|
||||
# An allowlisted caller reaches the manifest with NO token (vault trust).
|
||||
_seed_and_publish(app)
|
||||
_set_allowlist(app, '127.0.0.0/8, 10.134.48.0/23') # test client is 127.0.0.1
|
||||
resp = client.get('/api/geenforce/manifest?pctype=gea-shopfloor-cmm')
|
||||
assert resp.status_code == 200, resp.data
|
||||
assert b'Alpha' in resp.data
|
||||
|
||||
|
||||
def test_ip_not_in_allowlist_still_rejected(client, db, app):
|
||||
# A caller outside the allowlist and with no token is refused (fail-closed).
|
||||
_seed_and_publish(app)
|
||||
_set_allowlist(app, '10.0.0.0/8') # test client 127.0.0.1 is NOT in 10/8
|
||||
resp = client.get('/api/geenforce/manifest?pctype=gea-shopfloor-cmm')
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
def test_empty_allowlist_keeps_token_required(client, db, app):
|
||||
# Empty/unset allowlist = disabled; token stays the only path (back-compat).
|
||||
_seed_and_publish(app)
|
||||
_set_allowlist(app, '')
|
||||
resp = client.get('/api/geenforce/manifest?pctype=gea-shopfloor-cmm')
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
def test_wrong_scope_rejected(client, db, app, auth_headers):
|
||||
_seed_and_publish(app)
|
||||
resp = client.post('/api/apitokens',
|
||||
|
||||
Reference in New Issue
Block a user