-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1927 from DSD-DBS/pre-authentication-jwt
feat!: Issue JWT for session pre-authentication
- Loading branch information
Showing
27 changed files
with
625 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -75,6 +75,7 @@ repos: | |
- capellambse | ||
- typer | ||
- types-lxml | ||
- cryptography | ||
- repo: local | ||
hooks: | ||
- id: pylint | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
from __future__ import annotations | ||
|
||
import enum | ||
import pathlib | ||
|
||
import typer | ||
|
||
from capellacollab.sessions import auth as sessions_auth | ||
|
||
app = typer.Typer( | ||
help="Import and export RSA keys used for session pre-authentication." | ||
) | ||
|
||
|
||
@app.command(name="import") | ||
def import_private_key(file: pathlib.Path): | ||
"""Read and load a private key from a file. | ||
After importing the key, it will be used to sign the JWT session tokens. | ||
The previous key will be discarded. | ||
Please note that we can only accept private keys which have | ||
been exported using the `export` command of this CLI. | ||
""" | ||
|
||
key = sessions_auth.load_private_key_from_disk(file) | ||
if key is None: | ||
raise typer.BadParameter( | ||
"The provided file does not contain a valid RSA private key." | ||
) | ||
|
||
sessions_auth.save_private_key_to_disk(key, sessions_auth.PRIVATE_KEY_PATH) | ||
sessions_auth.load_private_key_in_memory(key) | ||
|
||
|
||
class KeyType(str, enum.Enum): | ||
PRIVATE = "private" | ||
PUBLIC = "public" | ||
|
||
|
||
@app.command(name="export") | ||
def export_private_key( | ||
file: pathlib.Path, type: KeyType = typer.Option(default=KeyType.PRIVATE) | ||
): | ||
"""Export the current private or public key to a file. | ||
The private key will be exported in PEM format. | ||
""" | ||
|
||
private_key = sessions_auth.load_private_key_from_disk( | ||
sessions_auth.PRIVATE_KEY_PATH | ||
) | ||
if private_key is None: | ||
raise typer.BadParameter( | ||
"No private key has been loaded. Use the `import` command to load a key" | ||
" or start the backend once to auto-generate a key." | ||
) | ||
|
||
if type == KeyType.PRIVATE: | ||
sessions_auth.save_private_key_to_disk(private_key, file) | ||
else: | ||
with open(file, "wb") as f: | ||
f.write( | ||
private_key.public_key().public_bytes( | ||
encoding=sessions_auth.serialization.Encoding.PEM, | ||
format=sessions_auth.serialization.PublicFormat.SubjectPublicKeyInfo, | ||
) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# SPDX-FileCopyrightText: Copyright DB InfraGO AG and contributors | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
import logging | ||
import pathlib | ||
|
||
import appdirs | ||
from cryptography.hazmat.primitives import serialization | ||
from cryptography.hazmat.primitives.asymmetric import rsa | ||
|
||
PRIVATE_KEY: rsa.RSAPrivateKey | None = None | ||
PUBLIC_KEY: rsa.RSAPublicKey | None = None | ||
|
||
PRIVATE_KEY_PATH = ( | ||
pathlib.Path(appdirs.user_data_dir("capellacollab")) / "private_key.pem" | ||
) | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def generate_private_key() -> rsa.RSAPrivateKey: | ||
logger.info( | ||
"Generating a new private key for session pre-authentication..." | ||
) | ||
return rsa.generate_private_key( | ||
public_exponent=65537, | ||
key_size=4096, | ||
) | ||
|
||
|
||
def serialize_private_key(key: rsa.RSAPrivateKey) -> bytes: | ||
return key.private_bytes( | ||
encoding=serialization.Encoding.PEM, | ||
format=serialization.PrivateFormat.PKCS8, | ||
encryption_algorithm=serialization.NoEncryption(), | ||
) | ||
|
||
|
||
def save_private_key_to_disk(key: rsa.RSAPrivateKey, path: pathlib.Path): | ||
logger.info( | ||
"Saving private key for session pre-authentication to %s", path | ||
) | ||
path.parent.mkdir(parents=True, exist_ok=True) | ||
with open(path, "wb") as f: | ||
f.write( | ||
serialize_private_key(key), | ||
) | ||
|
||
|
||
def load_private_key_from_disk(path: pathlib.Path) -> rsa.RSAPrivateKey | None: | ||
logger.info( | ||
"Trying to load private key for session pre-authentication from %s", | ||
path, | ||
) | ||
|
||
if not path.exists(): | ||
logger.info("No private key found at %s", path) | ||
return None | ||
|
||
with open(path, "rb") as f: | ||
key = serialization.load_pem_private_key( | ||
f.read(), | ||
password=None, | ||
) | ||
|
||
if not isinstance(key, rsa.RSAPrivateKey): | ||
logger.exception("The loaded private key is not an RSA key.") | ||
return None | ||
|
||
logger.info( | ||
"Successfully loaded private key for session pre-authentication from %s", | ||
path, | ||
) | ||
|
||
return key | ||
|
||
|
||
def load_private_key_in_memory(key: rsa.RSAPrivateKey): | ||
global PRIVATE_KEY | ||
global PUBLIC_KEY | ||
|
||
PRIVATE_KEY = key | ||
PUBLIC_KEY = PRIVATE_KEY.public_key() | ||
|
||
|
||
def initialize_session_pre_authentication(): | ||
private_key = load_private_key_from_disk(PRIVATE_KEY_PATH) | ||
|
||
if not private_key: | ||
private_key = generate_private_key() | ||
save_private_key_to_disk(private_key, PRIVATE_KEY_PATH) | ||
|
||
load_private_key_in_memory(private_key) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.