migrate to secret manager, closes #45
This commit is contained in:
parent
de23eb0065
commit
c7fe8fada5
@ -4,6 +4,7 @@ from music.model.user import User
|
|||||||
from music.model.config import Config
|
from music.model.config import Config
|
||||||
from music.auth.jwt_keys import generate_key
|
from music.auth.jwt_keys import generate_key
|
||||||
from music.api.decorators import no_cache
|
from music.api.decorators import no_cache
|
||||||
|
from music.cloud import SPOT_CLIENT_URI, SPOT_SECRET_URI
|
||||||
|
|
||||||
from urllib.parse import urlencode, urlunparse
|
from urllib.parse import urlencode, urlunparse
|
||||||
import datetime
|
import datetime
|
||||||
@ -11,11 +12,14 @@ from datetime import timedelta
|
|||||||
from numbers import Number
|
from numbers import Number
|
||||||
import logging
|
import logging
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
|
|
||||||
|
from google.cloud import secretmanager
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
blueprint = Blueprint('authapi', __name__)
|
blueprint = Blueprint('authapi', __name__)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
secret_client = secretmanager.SecretManagerServiceClient()
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/login', methods=['GET', 'POST'])
|
@blueprint.route('/login', methods=['GET', 'POST'])
|
||||||
@ -194,10 +198,10 @@ def auth():
|
|||||||
|
|
||||||
if 'username' in session:
|
if 'username' in session:
|
||||||
|
|
||||||
config = Config.collection.get("config/music-tools")
|
spot_client = secret_client.access_secret_version(request={"name": SPOT_CLIENT_URI})
|
||||||
params = urlencode(
|
params = urlencode(
|
||||||
{
|
{
|
||||||
'client_id': config.spotify_client_id,
|
'client_id': spot_client.payload.data.decode("UTF-8"),
|
||||||
'response_type': 'code',
|
'response_type': 'code',
|
||||||
'scope': 'playlist-modify-public playlist-modify-private playlist-read-private '
|
'scope': 'playlist-modify-public playlist-modify-private playlist-read-private '
|
||||||
'user-read-playback-state user-modify-playback-state user-library-read',
|
'user-read-playback-state user-modify-playback-state user-library-read',
|
||||||
@ -221,10 +225,11 @@ def token():
|
|||||||
flash('authorization failed')
|
flash('authorization failed')
|
||||||
return redirect('app_route')
|
return redirect('app_route')
|
||||||
else:
|
else:
|
||||||
config = Config.collection.get("config/music-tools")
|
spot_client = secret_client.access_secret_version(request={"name": SPOT_CLIENT_URI})
|
||||||
|
spot_secret = secret_client.access_secret_version(request={"name": SPOT_SECRET_URI})
|
||||||
|
|
||||||
idsecret = b64encode(
|
idsecret = b64encode(
|
||||||
bytes(config.spotify_client_id + ':' + config.spotify_client_secret, "utf-8")
|
bytes(spot_client.payload.data.decode("UTF-8") + ':' + spot_secret.payload.data.decode("UTF-8"), "utf-8")
|
||||||
).decode("ascii")
|
).decode("ascii")
|
||||||
headers = {'Authorization': 'Basic %s' % idsecret}
|
headers = {'Authorization': 'Basic %s' % idsecret}
|
||||||
|
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
from datetime import timedelta, datetime, timezone
|
from datetime import timedelta, datetime, timezone
|
||||||
import jwt
|
import jwt
|
||||||
|
from music.cloud import JWT_SECRET_URI
|
||||||
from music.model.user import User
|
from music.model.user import User
|
||||||
from music.model.config import Config
|
|
||||||
|
from google.cloud import secretmanager
|
||||||
|
|
||||||
|
secret_client = secretmanager.SecretManagerServiceClient()
|
||||||
|
|
||||||
|
|
||||||
def get_jwt_secret_key() -> str:
|
def get_jwt_secret_key() -> str:
|
||||||
|
return secret_client.access_secret_version(request={"name": JWT_SECRET_URI}).payload.data.decode("UTF-8")
|
||||||
config = Config.collection.get("config/music-tools")
|
|
||||||
|
|
||||||
if config.jwt_secret_key is None or len(config.jwt_secret_key) == 0:
|
|
||||||
raise KeyError("no jwt secret key found")
|
|
||||||
|
|
||||||
return config.jwt_secret_key
|
|
||||||
|
|
||||||
|
|
||||||
def generate_key(user: User, timeout: datetime | timedelta = timedelta(minutes=60)) -> str:
|
def generate_key(user: User, timeout: datetime | timedelta = timedelta(minutes=60)) -> str:
|
||||||
|
@ -10,6 +10,12 @@ from .tasks import run_user_playlist_task
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
SPOT_CLIENT_URI = "projects/sarsooxyz/secrets/spotify-client/versions/latest"
|
||||||
|
SPOT_SECRET_URI = "projects/sarsooxyz/secrets/spotify-secret/versions/latest"
|
||||||
|
LASTFM_CLIENT_URI = "projects/sarsooxyz/secrets/lastfm-client/versions/latest"
|
||||||
|
JWT_SECRET_URI = "projects/sarsooxyz/secrets/jwt-secret/versions/latest"
|
||||||
|
COOKIE_SECRET_URI = "projects/sarsooxyz/secrets/cookie-secret/versions/latest"
|
||||||
|
|
||||||
|
|
||||||
def queue_run_user_playlist(username: str, playlist_name: str):
|
def queue_run_user_playlist(username: str, playlist_name: str):
|
||||||
config = Config.collection.get("config/music-tools")
|
config = Config.collection.get("config/music-tools")
|
||||||
|
@ -7,9 +7,13 @@ from spotframework.net.network import Network as SpotifyNetwork, SpotifyNetworkE
|
|||||||
from spotframework.net.user import NetworkUser
|
from spotframework.net.user import NetworkUser
|
||||||
from fmframework.net.network import Network as FmNetwork
|
from fmframework.net.network import Network as FmNetwork
|
||||||
from music.model.user import User
|
from music.model.user import User
|
||||||
from music.model.config import Config
|
|
||||||
|
from music.cloud import SPOT_CLIENT_URI, SPOT_SECRET_URI, LASTFM_CLIENT_URI
|
||||||
|
|
||||||
|
from google.cloud import secretmanager
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
secret_client = secretmanager.SecretManagerServiceClient()
|
||||||
|
|
||||||
|
|
||||||
def refresh_token_database_callback(user: User) -> None:
|
def refresh_token_database_callback(user: User) -> None:
|
||||||
@ -50,10 +54,11 @@ def get_authed_spotify_network(user: User) -> Optional[SpotifyNetwork]:
|
|||||||
|
|
||||||
if user is not None:
|
if user is not None:
|
||||||
if user.spotify_linked:
|
if user.spotify_linked:
|
||||||
config = Config.collection.get("config/music-tools")
|
spot_client = secret_client.access_secret_version(request={"name": SPOT_CLIENT_URI})
|
||||||
|
spot_secret = secret_client.access_secret_version(request={"name": SPOT_SECRET_URI})
|
||||||
|
|
||||||
user_obj = DatabaseUser(client_id=config.spotify_client_id,
|
user_obj = DatabaseUser(client_id=spot_client.payload.data.decode("UTF-8"),
|
||||||
client_secret=config.spotify_client_secret,
|
client_secret=spot_secret.payload.data.decode("UTF-8"),
|
||||||
refresh_token=user.refresh_token,
|
refresh_token=user.refresh_token,
|
||||||
user_id=user.username,
|
user_id=user.username,
|
||||||
access_token=user.access_token)
|
access_token=user.access_token)
|
||||||
@ -92,8 +97,9 @@ def get_authed_lastfm_network(user: User) -> Optional[FmNetwork]:
|
|||||||
|
|
||||||
if user is not None:
|
if user is not None:
|
||||||
if user.lastfm_username:
|
if user.lastfm_username:
|
||||||
config = Config.collection.get("config/music-tools")
|
lastfm_client = secret_client.access_secret_version(request={"name": LASTFM_CLIENT_URI})
|
||||||
return FmNetwork(username=user.lastfm_username, api_key=config.last_fm_client_id)
|
|
||||||
|
return FmNetwork(username=user.lastfm_username, api_key=lastfm_client.payload.data.decode("UTF-8"))
|
||||||
else:
|
else:
|
||||||
logger.error(f'{user.username} has no last.fm username')
|
logger.error(f'{user.username} has no last.fm username')
|
||||||
else:
|
else:
|
||||||
|
@ -11,14 +11,8 @@ class Config(Model):
|
|||||||
"""Set correct path in Firestore
|
"""Set correct path in Firestore
|
||||||
"""
|
"""
|
||||||
|
|
||||||
spotify_client_id = TextField()
|
|
||||||
spotify_client_secret = TextField()
|
|
||||||
last_fm_client_id = TextField()
|
|
||||||
|
|
||||||
playlist_cloud_operating_mode = TextField() # task, function
|
playlist_cloud_operating_mode = TextField() # task, function
|
||||||
"""Determines whether playlist and tag update operations are done by Cloud Tasks or Functions
|
"""Determines whether playlist and tag update operations are done by Cloud Tasks or Functions
|
||||||
"""
|
"""
|
||||||
secret_key = TextField()
|
|
||||||
jwt_secret_key = TextField()
|
|
||||||
jwt_max_length = NumberField()
|
jwt_max_length = NumberField()
|
||||||
jwt_default_length = NumberField()
|
jwt_default_length = NumberField()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from flask import Flask, render_template, redirect, session, flash, url_for
|
from flask import Flask, render_template, redirect, session, flash, url_for
|
||||||
|
from google.cloud import secretmanager
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -6,9 +7,10 @@ import os
|
|||||||
from music.auth import auth_blueprint
|
from music.auth import auth_blueprint
|
||||||
from music.api import api_blueprint, player_blueprint, fm_blueprint, \
|
from music.api import api_blueprint, player_blueprint, fm_blueprint, \
|
||||||
spotfm_blueprint, spotify_blueprint, admin_blueprint, tag_blueprint
|
spotfm_blueprint, spotify_blueprint, admin_blueprint, tag_blueprint
|
||||||
from music.model.config import Config
|
from music.cloud import COOKIE_SECRET_URI
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
secret_client = secretmanager.SecretManagerServiceClient()
|
||||||
|
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
@ -20,11 +22,7 @@ def create_app():
|
|||||||
|
|
||||||
app = Flask(__name__, static_folder=os.path.join(os.path.dirname(__file__), '..', 'build'), template_folder="templates")
|
app = Flask(__name__, static_folder=os.path.join(os.path.dirname(__file__), '..', 'build'), template_folder="templates")
|
||||||
|
|
||||||
config = Config.collection.get("config/music-tools")
|
app.secret_key = secret_client.access_secret_version(request={"name": COOKIE_SECRET_URI}).payload.data.decode("UTF-8")
|
||||||
if config is not None:
|
|
||||||
app.secret_key = config.secret_key
|
|
||||||
else:
|
|
||||||
logger.error('no config returned, skipping secret key')
|
|
||||||
|
|
||||||
app.register_blueprint(auth_blueprint, url_prefix='/auth')
|
app.register_blueprint(auth_blueprint, url_prefix='/auth')
|
||||||
app.register_blueprint(api_blueprint, url_prefix='/api')
|
app.register_blueprint(api_blueprint, url_prefix='/api')
|
||||||
|
23
poetry.lock
generated
23
poetry.lock
generated
@ -291,6 +291,23 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
libcst = ["libcst (>=0.3.10)"]
|
libcst = ["libcst (>=0.3.10)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "google-cloud-secret-manager"
|
||||||
|
version = "2.12.6"
|
||||||
|
description = "Secret Manager API API client library"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]}
|
||||||
|
grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev"
|
||||||
|
proto-plus = ">=1.22.0,<2.0.0dev"
|
||||||
|
protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
libcst = ["libcst (>=0.2.5)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "google-cloud-tasks"
|
name = "google-cloud-tasks"
|
||||||
version = "2.10.4"
|
version = "2.10.4"
|
||||||
@ -835,7 +852,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "~3.10"
|
python-versions = "~3.10"
|
||||||
content-hash = "fef135789689e4ab5a67d8e37ce0da3ca5b772a500a3ce9ac69ac5b2876b5175"
|
content-hash = "ab592da5f6b73bd51841928689b332bdd994e188398b85a1d5e48cc402c89dac"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
alabaster = [
|
alabaster = [
|
||||||
@ -923,6 +940,10 @@ google-cloud-pubsub = [
|
|||||||
{file = "google-cloud-pubsub-2.13.10.tar.gz", hash = "sha256:3e746a1a97c96bdc63fc5499529ed7262f1464f6de8c8bae6238c12200919ac0"},
|
{file = "google-cloud-pubsub-2.13.10.tar.gz", hash = "sha256:3e746a1a97c96bdc63fc5499529ed7262f1464f6de8c8bae6238c12200919ac0"},
|
||||||
{file = "google_cloud_pubsub-2.13.10-py2.py3-none-any.whl", hash = "sha256:1a29795265ddb2eff23b4e4c2c4594a56f5a861827fe1cc72c53de42a3a13def"},
|
{file = "google_cloud_pubsub-2.13.10-py2.py3-none-any.whl", hash = "sha256:1a29795265ddb2eff23b4e4c2c4594a56f5a861827fe1cc72c53de42a3a13def"},
|
||||||
]
|
]
|
||||||
|
google-cloud-secret-manager = [
|
||||||
|
{file = "google-cloud-secret-manager-2.12.6.tar.gz", hash = "sha256:7aaba2997f43f8c3b35903efc54c389a802f39853ba81e81e1190f093420ecf6"},
|
||||||
|
{file = "google_cloud_secret_manager-2.12.6-py2.py3-none-any.whl", hash = "sha256:585e2ccd05038526b39eeabfe6932a7398341a76770ee0dd797c8581313e5212"},
|
||||||
|
]
|
||||||
google-cloud-tasks = [
|
google-cloud-tasks = [
|
||||||
{file = "google-cloud-tasks-2.10.4.tar.gz", hash = "sha256:650f4f43be725841103727ea3bc13227ae9ebaa2f99dadaad53888831cfc1d70"},
|
{file = "google-cloud-tasks-2.10.4.tar.gz", hash = "sha256:650f4f43be725841103727ea3bc13227ae9ebaa2f99dadaad53888831cfc1d70"},
|
||||||
{file = "google_cloud_tasks-2.10.4-py2.py3-none-any.whl", hash = "sha256:d5bc498eff25b145bd0a2d67ee466814dc9baf259a5ee8c4187aef267f4d83d4"},
|
{file = "google_cloud_tasks-2.10.4-py2.py3-none-any.whl", hash = "sha256:d5bc498eff25b145bd0a2d67ee466814dc9baf259a5ee8c4187aef267f4d83d4"},
|
||||||
|
@ -19,6 +19,7 @@ Flask = "^2.1.3"
|
|||||||
google-cloud-firestore = "<=2.3.4"
|
google-cloud-firestore = "<=2.3.4"
|
||||||
google-cloud-logging = "^3.2.1"
|
google-cloud-logging = "^3.2.1"
|
||||||
google-cloud-pubsub = "^2.13.4"
|
google-cloud-pubsub = "^2.13.4"
|
||||||
|
google-cloud-secret-manager = "^2.12.6"
|
||||||
google-cloud-tasks = "^2.10.0"
|
google-cloud-tasks = "^2.10.0"
|
||||||
requests = "^2.28.1"
|
requests = "^2.28.1"
|
||||||
PyJWT = "^2.4.0"
|
PyJWT = "^2.4.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user