Add credential file authenticator

This commit is contained in:
Joscha 2021-05-31 17:55:56 +02:00
parent 1fba96abcb
commit 9d5ec84b91
4 changed files with 63 additions and 1 deletions

View File

@ -22,6 +22,9 @@ ambiguous situations.
## Unreleased ## Unreleased
### Added
- `credential-file` authenticator
### Fixed ### Fixed
- Date parsing now also works correctly in non-group exercises - Date parsing now also works correctly in non-group exercises

View File

@ -180,6 +180,19 @@ via the terminal.
- `username`: The username. (Optional) - `username`: The username. (Optional)
- `password`: The password. (Optional) - `password`: The password. (Optional)
### The `credential-file` authenticator
This authenticator reads a username and a password from a credential file. The
credential file has exactly two lines (trailing newline optional). The first
line starts with `username=` and contains the username, the second line starts
with `password=` and contains the password. The username and password may
contain any characters except a line break.
```
username=AzureDiamond
password=hunter2
```
### The `keyring` authenticator ### The `keyring` authenticator
This authenticator uses the system keyring to store passwords. The username can This authenticator uses the system keyring to store passwords. The username can

View File

@ -3,6 +3,7 @@ from typing import Callable, Dict
from ..config import Config from ..config import Config
from .authenticator import Authenticator, AuthError, AuthSection # noqa: F401 from .authenticator import Authenticator, AuthError, AuthSection # noqa: F401
from .credential_file import CredentialFileAuthenticator, CredentialFileAuthSection
from .keyring import KeyringAuthenticator, KeyringAuthSection from .keyring import KeyringAuthenticator, KeyringAuthSection
from .simple import SimpleAuthenticator, SimpleAuthSection from .simple import SimpleAuthenticator, SimpleAuthSection
from .tfa import TfaAuthenticator from .tfa import TfaAuthenticator
@ -14,10 +15,12 @@ AuthConstructor = Callable[[
], Authenticator] ], Authenticator]
AUTHENTICATORS: Dict[str, AuthConstructor] = { AUTHENTICATORS: Dict[str, AuthConstructor] = {
"credential-file": lambda n, s, c:
CredentialFileAuthenticator(n, CredentialFileAuthSection(s)),
"simple": lambda n, s, c: "simple": lambda n, s, c:
SimpleAuthenticator(n, SimpleAuthSection(s)), SimpleAuthenticator(n, SimpleAuthSection(s)),
"tfa": lambda n, s, c: "tfa": lambda n, s, c:
TfaAuthenticator(n), TfaAuthenticator(n),
"keyring": lambda n, s, c: "keyring": lambda n, s, c:
KeyringAuthenticator(n, KeyringAuthSection(s)) KeyringAuthenticator(n, KeyringAuthSection(s)),
} }

View File

@ -0,0 +1,43 @@
from pathlib import Path
from typing import Tuple
from ..utils import fmt_real_path
from .authenticator import Authenticator, AuthLoadError, AuthSection
class CredentialFileAuthSection(AuthSection):
def path(self) -> Path:
value = self.s.get("path")
if value is None:
self.missing_value("path")
return Path(value)
class CredentialFileAuthenticator(Authenticator):
def __init__(self, name: str, section: CredentialFileAuthSection) -> None:
super().__init__(name)
path = section.path()
try:
with open(path) as f:
lines = list(f)
except OSError as e:
raise AuthLoadError(f"No credential file at {fmt_real_path(path)}") from e
if len(lines) != 2:
raise AuthLoadError("Credential file must be two lines long")
[uline, pline] = lines
uline = uline[:-1] # Remove trailing newline
if pline.endswith("\n"):
pline = pline[:-1]
if not uline.startswith("username="):
raise AuthLoadError("First line must start with 'username='")
if not pline.startswith("password="):
raise AuthLoadError("Second line must start with 'password='")
self._username = uline[:9]
self._password = pline[:9]
async def credentials(self) -> Tuple[str, str]:
return self._username, self._password