Fixed room browser connection issues
This commit is contained in:
1 parent
27b12beff8
commit
91b7840535
3 files changed
+48
-2
No files matched your search
@@ -6,6 +6,8 @@ import socket
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import io
|
||||
from contextlib import redirect_stdout
|
||||
from unittest.mock import patch
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
@@ -15,6 +17,21 @@ spec.loader.exec_module(launch)
|
||||
|
||||
|
||||
class LaunchTests(unittest.TestCase):
|
||||
def test_conflicting_environment_warns_without_exposing_tokens(self):
|
||||
output = io.StringIO()
|
||||
with redirect_stdout(output):
|
||||
selected = launch.credential({'ARCADE_OPERATOR_TOKEN':'old-private-token-123'}, 'ARCADE_OPERATOR_TOKEN', 'new-private-token-456', 'Operator token')
|
||||
self.assertEqual(selected, 'old-private-token-123')
|
||||
self.assertIn('overrides a DIFFERENT value', output.getvalue())
|
||||
self.assertNotIn('old-private-token-123', output.getvalue())
|
||||
self.assertNotIn('new-private-token-456', output.getvalue())
|
||||
|
||||
def test_quoted_token_is_rejected_without_exposing_it(self):
|
||||
for token in ['"private-test-token-123"', "'private-test-token-123'"]:
|
||||
with self.assertRaisesRegex(ValueError, 'without quotes') as caught:
|
||||
launch.credential({}, 'ARCADE_OPERATOR_TOKEN', token, 'Operator token')
|
||||
self.assertNotIn('private-test-token-123', str(caught.exception))
|
||||
|
||||
def config(self, folder, gpio='false'):
|
||||
filename = Path(folder) / 'launcher.ini'
|
||||
filename.write_text(f'''[controller]
|
||||
|
||||
@@ -46,6 +46,21 @@ cabinet_tokens = {"cabinet_01":"YOUR_EXISTING_CABINET_TOKEN"}
|
||||
|
||||
Each token must be distinct and at least 16 characters. Existing `ARCADE_OPERATOR_TOKEN`, `ARCADE_CABINET_TOKENS`, and legacy `ARCADE_CABINET_TOKEN` environment variables take precedence. Reuse credentials; the launcher does not generate or rotate them.
|
||||
|
||||
The launcher prints the configuration path and token source without printing the tokens. It warns when an environment operator/cabinet token overrides a different INI value, and rejects surrounding token quotes.
|
||||
|
||||
### Browser says Unauthorized
|
||||
|
||||
The controller was reached, but its operator token does not match what was entered. Use the **operator** token, not a cabinet token, and open the exact operator URL printed by the launcher. “Waiting for the room operator” in the game is normal until a scenario is prepared.
|
||||
|
||||
If you configured `operator_token` in `launcher.ini` but the launcher reports an environment override, stop your running stack with Ctrl+C and clear the old variable **in the same PowerShell terminal you use to launch**:
|
||||
|
||||
```powershell
|
||||
Remove-Item Env:ARCADE_OPERATOR_TOKEN -ErrorAction SilentlyContinue
|
||||
.\Start-Arcade.ps1 -Mode all -Browser
|
||||
```
|
||||
|
||||
Use `-Mode controller` instead if this machine only hosts the controller. Enter the INI's operator token in the browser, without quotes. Clearing the variable affects only that PowerShell session; it does not delete an INI value or change a stored Windows environment variable. The controller must be restarted to pick up credential changes. Never paste real tokens into troubleshooting messages.
|
||||
|
||||
**Set `database` to your existing controller database** when upgrading. The example defaults to a new `tools/launch/state/arcade_room.sqlite3`; that would start a fresh controller history instead of using a database at the project root. To use a root-level database from the default INI location, set `database = ../../arcade_room.sqlite3`.
|
||||
|
||||
Then validate and start:
|
||||
|
||||
+16
-2
@@ -17,6 +17,19 @@ import webbrowser
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def credential(env, name, configured, label):
|
||||
inherited = env.get(name, '')
|
||||
selected = inherited or configured
|
||||
source = 'environment variable '+name if inherited else 'launcher INI'
|
||||
if selected:
|
||||
print(f'{label} source: {source}', flush=True)
|
||||
if inherited and configured and inherited != configured:
|
||||
print(f'WARNING: {name} overrides a DIFFERENT value in the launcher INI. Clear that variable in the launching terminal to use the INI.', flush=True)
|
||||
if selected and (selected[0] in "\"'" or selected[-1] in "\"'"):
|
||||
raise ValueError(f'{label} has surrounding quote characters. In launcher.ini use the token without quotes; enter the same unquoted token in the browser.')
|
||||
return selected
|
||||
|
||||
|
||||
def plan(config_path, mode):
|
||||
config_path = Path(config_path).resolve()
|
||||
config = configparser.ConfigParser(interpolation=None)
|
||||
@@ -41,7 +54,7 @@ def plan(config_path, mode):
|
||||
require_file(script)
|
||||
require_file(script.with_name('operator.html'))
|
||||
require_file(catalog)
|
||||
operator = env.get('ARCADE_OPERATOR_TOKEN') or value('controller', 'operator_token')
|
||||
operator = credential(env, 'ARCADE_OPERATOR_TOKEN', value('controller', 'operator_token'), 'Operator token')
|
||||
tokens = json.loads(env.get('ARCADE_CABINET_TOKENS') or value('controller', 'cabinet_tokens', '{}'))
|
||||
if not isinstance(tokens, dict): raise ValueError('cabinet_tokens must be a JSON object')
|
||||
legacy = env.get('ARCADE_CABINET_TOKEN', '')
|
||||
@@ -66,7 +79,7 @@ def plan(config_path, mode):
|
||||
if not cabinet_id or any(c.isspace() for c in cabinet_id): raise ValueError('Invalid cabinet ID')
|
||||
cabinet_env = dict(env)
|
||||
room_url = env.get('ARCADE_ROOM_URL') or value('cabinet', 'url')
|
||||
token = env.get('ARCADE_CABINET_TOKEN') or value('cabinet', 'token')
|
||||
token = credential(env, 'ARCADE_CABINET_TOKEN', value('cabinet', 'token'), 'Cabinet token')
|
||||
if mode == 'all':
|
||||
room_url = room_url or url
|
||||
token = token or tokens.get(cabinet_id, '')
|
||||
@@ -163,6 +176,7 @@ def main():
|
||||
def interrupted(*_): raise KeyboardInterrupt
|
||||
signal.signal(signal.SIGTERM, interrupted)
|
||||
try:
|
||||
print('Launcher configuration: '+str(Path(args.config).resolve()), flush=True)
|
||||
jobs, logs = plan(args.config, args.mode)
|
||||
if args.check:
|
||||
check_ports(jobs)
|
||||
|
||||
Reference in new issue
Block a user