identity: present cert instead of waiting for 60
This commit is contained in:
parent
57f01720d6
commit
8b1561e689
|
@ -17,7 +17,9 @@ from bebop.colors import ColorPair, init_colors
|
|||
from bebop.command_line import CommandLine
|
||||
from bebop.external import open_external_program
|
||||
from bebop.help import HELP_PAGE
|
||||
from bebop.fs import get_identities_list_path
|
||||
from bebop.history import History
|
||||
from bebop.identity import load_identities
|
||||
from bebop.links import Links
|
||||
from bebop.mime import MimeType
|
||||
from bebop.mouse import ButtonState
|
||||
|
@ -41,14 +43,15 @@ class Browser:
|
|||
- command_line: a CommandLine object for the user to interact with.
|
||||
- running: the browser will continue running while this is true.
|
||||
- status_data: 3-uple of status text, color pair and attributes of the
|
||||
status line, used to reset status after an error.
|
||||
status line, used to reset status after an error.
|
||||
- history: an History object.
|
||||
- cache: a dict containing cached pages
|
||||
- cache: a dict containing cached pages.
|
||||
- special_pages: a dict containing page names used with "bebop" scheme;
|
||||
values are dicts as well: the "open" key maps to a callable to use when
|
||||
the page is accessed, and the optional "source" key maps to callable
|
||||
returning the page source path.
|
||||
values are dicts as well: the "open" key maps to a callable to use when
|
||||
the page is accessed, and the optional "source" key maps to callable
|
||||
returning the page source path.
|
||||
- last_download: tuple of MimeType and path, or None.
|
||||
- identities: identities map.
|
||||
"""
|
||||
|
||||
def __init__(self, config, cert_stash):
|
||||
|
@ -65,6 +68,7 @@ class Browser:
|
|||
self.cache = {}
|
||||
self.special_pages = self.setup_special_pages()
|
||||
self.last_download: Optional[Tuple[MimeType, Path]] = None
|
||||
self.identities = load_identities(get_identities_list_path()) or {}
|
||||
self._current_url = ""
|
||||
|
||||
@property
|
||||
|
|
|
@ -26,7 +26,7 @@ def open_gemini_url(
|
|||
url: str,
|
||||
redirects: int =0,
|
||||
use_cache: bool =True,
|
||||
identity=None
|
||||
cert_and_key=None
|
||||
) -> Optional[str]:
|
||||
"""Open a Gemini URL and set the formatted response as content.
|
||||
|
||||
|
@ -50,7 +50,7 @@ def open_gemini_url(
|
|||
- url: a valid URL with Gemini scheme to open.
|
||||
- redirects: current amount of redirections done to open the initial URL.
|
||||
- use_cache: if true, look up if the page is cached before requesting it.
|
||||
- identity: if not None, a tuple of paths to a client cert/key to use.
|
||||
- cert_and_key: if not None, a tuple of paths to a client cert/key to use.
|
||||
|
||||
Returns:
|
||||
The final successfully handled URL on success, None otherwise. Redirected
|
||||
|
@ -64,13 +64,20 @@ def open_gemini_url(
|
|||
loading_message = f"{loading_message_verb} {url}…"
|
||||
browser.set_status(loading_message)
|
||||
|
||||
# If this URL used to request an identity, provide it.
|
||||
if not cert_and_key:
|
||||
url_identities = get_identities_for_url(browser.identities, url)
|
||||
identity = select_identity(url_identities)
|
||||
if identity:
|
||||
cert_and_key = get_cert_and_key(identity["id"])
|
||||
|
||||
if use_cache and url in browser.cache:
|
||||
browser.load_page(browser.cache[url])
|
||||
browser.current_url = url
|
||||
browser.set_status(url)
|
||||
return url
|
||||
|
||||
req = Request(url, browser.stash, identity=identity)
|
||||
req = Request(url, browser.stash, identity=cert_and_key)
|
||||
connect_timeout = browser.config["connect_timeout"]
|
||||
connected = req.connect(connect_timeout)
|
||||
if not connected:
|
||||
|
@ -281,21 +288,20 @@ def _handle_cert_required(
|
|||
The result of `open_gemini_url` with the client certificate provided.
|
||||
"""
|
||||
identities = load_identities(get_identities_list_path())
|
||||
if isinstance(identities, str):
|
||||
browser.set_status_error(f"Can't load identities: {identities}")
|
||||
if not identities:
|
||||
browser.set_status_error(f"Can't load identities.")
|
||||
return None
|
||||
browser.identities = identities
|
||||
|
||||
url_identities = get_identities_for_url(identities, url)
|
||||
url_identities = get_identities_for_url(browser.identities, url)
|
||||
if not url_identities:
|
||||
identity = create_identity(browser, url)
|
||||
if not identity:
|
||||
return None
|
||||
identities[url] = [identity]
|
||||
save_identities(identities, get_identities_list_path())
|
||||
browser.identities[url] = [identity]
|
||||
save_identities(browser.identities, get_identities_list_path())
|
||||
else:
|
||||
# TODO support multiple identities; for now we just use the first
|
||||
# available.
|
||||
identity = url_identities[0]
|
||||
identity = select_identity(url_identities)
|
||||
|
||||
cert_path, key_path = get_cert_and_key(identity["id"])
|
||||
return open_gemini_url(
|
||||
|
@ -303,10 +309,16 @@ def _handle_cert_required(
|
|||
url,
|
||||
redirects=redirects + 1,
|
||||
use_cache=False,
|
||||
identity=(cert_path, key_path)
|
||||
cert_and_key=(cert_path, key_path)
|
||||
)
|
||||
|
||||
|
||||
def select_identity(identities: list):
|
||||
"""Let user select the appropriate identity among candidates."""
|
||||
# TODO support multiple identities; for now we just use the first available.
|
||||
return identities[0] if identities else None
|
||||
|
||||
|
||||
def create_identity(browser: Browser, url: str):
|
||||
"""Walk the user through identity creation.
|
||||
|
||||
|
|
|
@ -33,24 +33,24 @@ from typing import Optional, Union
|
|||
from bebop.fs import get_identities_path, get_user_data_path
|
||||
|
||||
|
||||
def load_identities(identities_path: Path) -> Union[dict, str]:
|
||||
"""Return saved identities, else an error str."""
|
||||
def load_identities(identities_path: Path) -> Optional[dict]:
|
||||
"""Return saved identities or None on error."""
|
||||
identities = {}
|
||||
try:
|
||||
with open(identities_path, "rt") as identities_file:
|
||||
identities = json.load(identities_file)
|
||||
except (OSError, ValueError) as exc:
|
||||
return f"Failed to load identities '{identities_path}': {exc}"
|
||||
return None
|
||||
return identities
|
||||
|
||||
|
||||
def save_identities(identities: dict, identities_path: Path):
|
||||
"""Save the certificate stash. Return True on success, else an error str."""
|
||||
"""Save the certificate stash. Return True on success."""
|
||||
try:
|
||||
with open(identities_path, "wt") as identities_file:
|
||||
json.dump(identities, identities_file)
|
||||
except (OSError, ValueError) as exc:
|
||||
return f"Failed to save identities '{identities_path}': {exc}"
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
@ -102,8 +102,8 @@ def create_certificate(url: str, common_name: str):
|
|||
try:
|
||||
subprocess.check_call(
|
||||
command,
|
||||
# stdout=subprocess.DEVNULL,
|
||||
# stderr=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
error = "Could not create certificate: " + str(exc)
|
||||
|
|
Reference in a new issue