Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions lib50/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
DEFAULT_FILE_LIMIT = 10000


def push(tool, slug, config_loader, repo=None, data=None, prompt=lambda question, included, excluded: True, file_limit=DEFAULT_FILE_LIMIT):
def push(tool, slug, config_loader, repo=None, data=None, prompt=lambda question, included, excluded: True, file_limit=DEFAULT_FILE_LIMIT, auth_method=None):
"""
Pushes to Github in name of a tool.
What should be pushed is configured by the tool and its configuration in the .cs50.yml file identified by the slug.
Expand All @@ -61,6 +61,10 @@ def push(tool, slug, config_loader, repo=None, data=None, prompt=lambda question
:type prompt: lambda str, list, list => bool, optional
:param file_limit: maximum number of files to be matched by any globbing pattern.
:type file_limit: int
:param auth_method: The authentication method to use. Accepts `"https"` or `"ssh"`. \
If any other value is provided, attempts SSH \
authentication first and fall back to HTTPS if SSH fails.
:type auth_method: str
:return: GitHub username and the commit hash
:type: tuple(str, str)

Expand Down Expand Up @@ -89,7 +93,7 @@ def push(tool, slug, config_loader, repo=None, data=None, prompt=lambda question
remote, (honesty, included, excluded) = connect(slug, config_loader, file_limit=DEFAULT_FILE_LIMIT)

# Authenticate the user with GitHub, and prepare the submission
with authenticate(remote["org"], repo=repo) as user, prepare(tool, slug, user, included):
with authenticate(remote["org"], repo=repo, auth_method=auth_method) as user, prepare(tool, slug, user, included):

# Show any prompt if specified
if prompt(honesty, included, excluded):
Expand Down
43 changes: 30 additions & 13 deletions lib50/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,18 @@ class User:
init=False)

@contextlib.contextmanager
def authenticate(org, repo=None):
def authenticate(org, repo=None, auth_method=None):
"""
A contextmanager that authenticates a user with GitHub via SSH if possible, otherwise via HTTPS.
A contextmanager that authenticates a user with GitHub.

:param org: GitHub organisation to authenticate with
:type org: str
:param repo: GitHub repo (part of the org) to authenticate with. Default is the user's GitHub login.
:type repo: str, optional
:param auth_method: The authentication method to use. Accepts `"https"` or `"ssh"`. \
If any other value is provided, attempts SSH \
authentication first and fall back to HTTPS if SSH fails.
:type auth_method: str, optional
:return: an authenticated user
:type: lib50.User

Expand All @@ -51,21 +55,34 @@ def authenticate(org, repo=None):
print(user.name)

"""
with api.ProgressBar(_("Authenticating")) as progress_bar:
# Both authentication methods can require user input, best stop the bar
progress_bar.stop()
def try_https(org, repo):
with _authenticate_https(org, repo=repo) as user:
return user

# Try auth through SSH
def try_ssh(org, repo):
user = _authenticate_ssh(org, repo=repo)

# SSH auth failed, fallback to HTTPS
if user is None:
with _authenticate_https(org, repo=repo) as user:
yield user
# yield SSH user
else:
yield user
raise ConnectionError
return user

# Showcase the type of authentication based on input
method_label = f" ({auth_method.upper()})" if auth_method in ("https", "ssh") else ""
with api.ProgressBar(_("Authenticating{}").format(method_label)) as progress_bar:
# Both authentication methods can require user input, best stop the bar
progress_bar.stop()

match auth_method:
case "https":
yield try_https(org, repo)
case "ssh":
yield try_ssh(org, repo)
case _:
# Try auth through SSH
try:
yield try_ssh(org, repo)
except ConnectionError:
# SSH auth failed, fallback to HTTPS
yield try_https(org, repo)

def logout():
"""
Expand Down