identity: present cert instead of waiting for 60

This commit is contained in:
dece 2021-05-13 01:24:29 +02:00
parent 57f01720d6
commit 8b1561e689
3 changed files with 40 additions and 24 deletions

View file

@ -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

View file

@ -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.

View file

@ -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)