fix: allow CLI file tokens for RPC access
Build and Push Docker Container / build-and-push (push) Successful in 1m2s
Build and Push Docker Container / build-and-push (push) Successful in 1m2s
- Accept files-scoped bearer tokens on servicelink RPC calls. - Keep mesh shared-secret auth for trusted internal callers. - Validate CLI auth scopes and reject unsupported values early. - Stop CLI browser login waiting for the full timeout after callback. - Add tests for scope normalization, RPC access, and login callback timing.
This commit is contained in:
@@ -22,6 +22,7 @@ class _CallbackServer:
|
||||
self.code: str | None = None
|
||||
self.error: str | None = None
|
||||
self.expected_state = expected_state
|
||||
self.done = threading.Event()
|
||||
outer = self
|
||||
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
@@ -36,6 +37,7 @@ class _CallbackServer:
|
||||
self.send_response(400)
|
||||
self.end_headers()
|
||||
self.wfile.write(b'NanoShare CLI login failed: state mismatch')
|
||||
outer.done.set()
|
||||
return
|
||||
outer.code = params.get('code', [''])[0]
|
||||
outer.error = params.get('error', [''])[0] or None
|
||||
@@ -45,31 +47,40 @@ class _CallbackServer:
|
||||
self.wfile.write(b'NanoShare CLI login complete. You can close this browser tab.')
|
||||
else:
|
||||
self.wfile.write(b'NanoShare CLI login failed.')
|
||||
outer.done.set()
|
||||
|
||||
self.httpd = ThreadingHTTPServer((host, port), Handler)
|
||||
self.url = f'http://{host}:{self.httpd.server_port}/callback'
|
||||
self.thread = threading.Thread(target=self.httpd.serve_forever, daemon=True)
|
||||
self.started = False
|
||||
|
||||
def start(self) -> None:
|
||||
if not self.started:
|
||||
self.thread.start()
|
||||
self.started = True
|
||||
|
||||
def wait_for_code(self, timeout: float) -> str:
|
||||
self.thread.start()
|
||||
self.thread.join(timeout)
|
||||
self.start()
|
||||
received_callback = self.done.wait(timeout)
|
||||
self.httpd.shutdown()
|
||||
self.thread.join(5)
|
||||
if self.error:
|
||||
raise RuntimeError(self.error)
|
||||
if not self.code:
|
||||
if not received_callback or not self.code:
|
||||
raise TimeoutError('login timed out')
|
||||
return self.code
|
||||
|
||||
def login(args) -> int:
|
||||
state = secrets.token_urlsafe(24)
|
||||
server = _CallbackServer(args.callback_host, args.callback_port, state)
|
||||
server.start()
|
||||
authorize_url = _make_authorize_url(args.base_url, server.url, state, args.scope)
|
||||
|
||||
print(f'Opening browser for NanoShare login: {authorize_url}')
|
||||
print(f'Opening browser for NanoShare login: {authorize_url}', flush=True)
|
||||
if not args.no_browser:
|
||||
webbrowser.open(authorize_url)
|
||||
else:
|
||||
print(authorize_url)
|
||||
print(authorize_url, flush=True)
|
||||
|
||||
try:
|
||||
code = server.wait_for_code(args.login_timeout)
|
||||
|
||||
Reference in New Issue
Block a user