diff --git a/app.py b/app.py index 569cc02..bfa3fa8 100644 --- a/app.py +++ b/app.py @@ -1,3 +1,4 @@ +import os import logging import json @@ -8,9 +9,13 @@ import calendar_interface import db import auth +logging.basicConfig(format='%(asctime)s,%(msecs)d %(levelname)-4s [%(filename)s:%(lineno)d] %(message)s', + datefmt='%Y-%m-%d:%H:%M:%S', + level=logging.INFO + ) + app = Flask(__name__) -app.config['SECRET_KEY'] = '\xacI4\x077\x16?Q\xb4")\xdb\x066\x95\x11i\x0b\x0c&\xb6rP\'' -app.config['SECURITY_PASSWORD_SALT'] = '>\xe3\x9bz\xfd\xbc[\xe22\xcfK\xca\x88!\xd8\xd5,\xd0\x95\x0c\x02\xad\xfa\x9d' +app.config.from_object(os.environ["CONFIG"])# load config, pointed to by env var app.teardown_appcontext(db.close_db) app.cli.add_command(db.init_db_command) @@ -28,7 +33,7 @@ def index(): else: return render_template('index.html', events=events, user=g.user) -# @app.route('/') +# @app.route('/') # TODO: access detailed event view # def post(post_id): # post = get_post(post_id) # return render_template('show_event.html', post=post) diff --git a/auth.py b/auth.py index 8f11074..07da3f3 100644 --- a/auth.py +++ b/auth.py @@ -3,13 +3,12 @@ import functools import logging from flask import ( - Blueprint, flash, g, redirect, render_template, request, session, url_for + Blueprint, flash, g, redirect, render_template, request, session, url_for, current_app ) from werkzeug.security import check_password_hash, generate_password_hash from itsdangerous import URLSafeTimedSerializer from db import get_db -import app import calendar_interface bp = Blueprint('auth', __name__, url_prefix='/auth') @@ -120,16 +119,16 @@ def login_required(view): # use this as decorator def generate_confirmation_token(email): - serializer = URLSafeTimedSerializer(app.app.config['SECRET_KEY']) - return serializer.dumps(email, salt=app.app.config['SECURITY_PASSWORD_SALT']) + serializer = URLSafeTimedSerializer(current_app.config['SECRET_KEY']) + return serializer.dumps(email, salt=current_app.config['SECURITY_PASSWORD_SALT']) def confirm_token(token, expiration=3600*14*28): # 2 Wochen expiration - serializer = URLSafeTimedSerializer(app.app.config['SECRET_KEY']) + serializer = URLSafeTimedSerializer(current_app.config['SECRET_KEY']) try: email = serializer.loads( token, - salt=app.app.config['SECURITY_PASSWORD_SALT'], + salt=current_app.config['SECURITY_PASSWORD_SALT'], max_age=expiration ) except: @@ -138,7 +137,6 @@ def confirm_token(token, expiration=3600*14*28): # 2 Wochen expiration @bp.route('/confirm/') -# @login_required # TODO: notwendig? security ? def confirm_email(token): try: email = confirm_token(token) diff --git a/calendar_interface.py b/calendar_interface.py index f7a1ecf..b1c0faa 100644 --- a/calendar_interface.py +++ b/calendar_interface.py @@ -5,37 +5,24 @@ import argparse import requests import msal +from config import MsalConfig try: from zoneinfo import ZoneInfo except ImportError: from backports.zoneinfo import ZoneInfo -config = { - "authority": "https://login.microsoftonline.com/propedal.at", - "client_id": "52f192c4-875d-44a2-b28a-575e920225e5", # client public id (from azure web interface)#"da3fc28c-5fcf-4884-9477-903a4420cc3d", - "scope": ["https://graph.microsoft.com/.default"], # scopes from api - "secret": "irj8Q~PliZzSe7JnXEaiWKQ6v0CAg1DTZOO~Ccsf" # api secret key (from azure web interface)#"bqP8Q~SEp_AmYYDZoEykXxZdADoCOhzOOhbO3c3T" -} -USER_ID = "simone.profus@propedal.at" # user with calendar #"2af02ca1-77fc-46fd-90af-c754306081cb" # -CALENDAR_ID = "AAMkADY0MDg1MTVjLTg5ZjItNGQxYS04MGQ3LWY2NjJmYjM0YmZhOQBGAAAAAADXD7SdVoWYQI4RYXbBumMEBwAf_ngZxs71RonY3GuLL8TVAAAAAAEGAAAf_ngZxs71RonY3GuLL8TVAADHFxN2AAA=" # calendar id - determined by /users/id/calendars - WEEKDAYS= {0:"Mo", 1:"Di", 2:"Mi", 3:"Do", 4: "Fr", 5:"Sa", 6: "So"} # Optional logging -logging.basicConfig(format='%(asctime)s,%(msecs)d %(levelname)-4s [%(filename)s:%(lineno)d] %(message)s', - datefmt='%Y-%m-%d:%H:%M:%S', - level=logging.INFO) + # logging.getLogger("msal").setLevel(logging.INFO) # Optionally disable MSAL DEBUG logs - def get_access_token(): - #with open("auth_config.json") as f: - # config = json.load(f) # Create a preferably long-lived app instance which maintains a token cache. app = msal.ConfidentialClientApplication( - config["client_id"], authority=config["authority"], - client_credential=config["secret"], + MsalConfig.CLIENT_ID, authority=MsalConfig.AUTHORITY, + client_credential=MsalConfig.SECRET, # token_cache=... # Default cache is in memory only. # You can learn how to use SerializableTokenCache from # https:#msal-python.readthedocs.io/en/latest/#msal.SerializableTokenCache @@ -47,11 +34,11 @@ def get_access_token(): # Firstly, looks up a token from cache # Since we are looking for token for the current app, NOT for an end user, # notice we give account parameter as None. # TODO: token never exists in cache; make app long living - result = app.acquire_token_silent(config["scope"], account=None) + result = app.acquire_token_silent(MsalConfig.SCOPE, account=None) if result is None: logging.info("No suitable token exists in cache. Let's get a new one from AAD.") - return app.acquire_token_for_client(scopes=config["scope"]) + return app.acquire_token_for_client(scopes=MsalConfig.SCOPE) else: logging.info("Token was found in cache.") @@ -82,7 +69,7 @@ def execute_patch_request(token, endpoint, data): #data=data, headers={'Authorization': 'Bearer ' + token['access_token']},).json() -def send_mail(to, subject, content, user_id=USER_ID): +def send_mail(to, subject, content, user_id=MsalConfig.USER_ID): token=get_access_token() @@ -106,24 +93,24 @@ def send_mail(to, subject, content, user_id=USER_ID): return execute_post_request(token, "https://graph.microsoft.com/v1.0/users/" + user_id + "/sendMail", mail) -def update_calendar_event(event_id, data, user_id=USER_ID): +def update_calendar_event(event_id, data, user_id=MsalConfig.USER_ID): token = get_access_token() endpoint= "https://graph.microsoft.com/v1.0/users/" + user_id + f"/calendar/events/{event_id}" return execute_patch_request(token, endpoint, data) -def execute_user_request(token, endpoint, user_id=USER_ID): +def execute_user_request(token, endpoint, user_id=MsalConfig.USER_ID): return execute_get_request(token, "https://graph.microsoft.com/v1.0/users/" + user_id + f"/{endpoint}") def get_all_calendar_events(): token = get_access_token() - return execute_user_request(token, f"calendars/{CALENDAR_ID}/events").get("value") + return execute_user_request(token, f"calendars/{MsalConfig.CALENDAR_ID}/events").get("value") def get_calendar_event(id, filter: str=""): token = get_access_token() - return execute_user_request(token, f"calendars/{CALENDAR_ID}/events/{id}{filter}") + return execute_user_request(token, f"calendars/{MsalConfig.CALENDAR_ID}/events/{id}{filter}") def get_future_calendar_events(): @@ -137,7 +124,7 @@ def get_future_calendar_events(): token = get_access_token() - return execute_user_request(token, f"calendars/{CALENDAR_ID}/calendarview{filter}") + return execute_user_request(token, f"calendars/{MsalConfig.CALENDAR_ID}/calendarview{filter}") def convert_datetimes(events): for event in events: @@ -181,6 +168,10 @@ def delte_attendee(data, email): if __name__ == "__main__": + logging.basicConfig(format='%(asctime)s,%(msecs)d %(levelname)-4s [%(filename)s:%(lineno)d] %(message)s', + datefmt='%Y-%m-%d:%H:%M:%S', + level=logging.INFO) + parser = argparse.ArgumentParser(description='Process some integers.') parser.add_argument('--show_calendars', help='Show available calendars', action='store_true') parser.add_argument('--show_event_entries', help='Show available event fields', action='store_true') diff --git a/config.py b/config.py new file mode 100644 index 0000000..be9a94e --- /dev/null +++ b/config.py @@ -0,0 +1,42 @@ +class DefaultConfig: + DEBUG = False + TESTING = False + DATABASE_URI = "users.db" + SECRET_KEY = SECURITY_PASSWORD_SALT='default' + + +class Config(DefaultConfig): + + AUTHORITY = "https://login.microsoftonline.com/propedal.at", + CLIENT_ID = "52f192c4-875d-44a2-b28a-575e920225e5", # client public id (from azure web interface)#"da3fc28c-5fcf-4884-9477-903a4420cc3d", + scope = ["https://graph.microsoft.com/.default"], # scopes from api + secret = "irj8Q~PliZzSe7JnXEaiWKQ6v0CAg1DTZOO~Ccsf" # api secret key (from azure web interface)#"bqP8Q~SEp_AmYYDZoEykXxZdADoCOhzOOhbO3c3T" + USER_ID = "simone.profus@propedal.at" + CALENDAR_ID = "AAMkADY0MDg1MTVjLTg5ZjItNGQxYS04MGQ3LWY2NjJmYjM0YmZhOQBGAAAAAADXD7SdVoWYQI4RYXbBumMEBwAf_ngZxs71RonY3GuLL8TVAAAAAAEGAAAf_ngZxs71RonY3GuLL8TVAADHFxN2AAA=" # calendar id - determined by /users/id/calendars + + +class ProductionConfig(Config): + SECRET_KEY = '\xacI4\x077\x16?Q\xb4")\xdb\x066\x95\x11i\x0b\x0c&\xb6rP\'' + SECURITY_PASSWORD_SALT = '>\xe3\x9bz\xfd\xbc[\xe22\xcfK\xca\x88!\xd8\xd5,\xd0\x95\x0c\x02\xad\xfa\x9d' + DATABASE_URI = 'mysql://user@localhost/foo' + +class DevelopmentConfig(Config): + DEBUG = True + +class TestingConfig(Config): + TESTING = True + +class MsalDefaulConfig(): + AUTHORITY = "https://login.microsoftonline.com/common" + SCOPE = ["https://graph.microsoft.com/.default"] # scopes from api + CLIENT_ID = "" # client public id (from azure web interface) + SECRET = "" # api secret key (from azure web interface) + USER_ID = "user@domain" + CALENDAR_ID = "" # calendar id - determined by /users/id/calendars + +class MsalConfig(MsalDefaulConfig): + AUTHORITY = "https://login.microsoftonline.com/propedal.at" + CLIENT_ID = "52f192c4-875d-44a2-b28a-575e920225e5" # client public id (from azure web interface) + SECRET = "irj8Q~PliZzSe7JnXEaiWKQ6v0CAg1DTZOO~Ccsf" # api secret key (from azure web interface) + USER_ID = "simone.profus@propedal.at" + CALENDAR_ID = "AAMkADY0MDg1MTVjLTg5ZjItNGQxYS04MGQ3LWY2NjJmYjM0YmZhOQBGAAAAAADXD7SdVoWYQI4RYXbBumMEBwAf_ngZxs71RonY3GuLL8TVAAAAAAEGAAAf_ngZxs71RonY3GuLL8TVAADHFxN2AAA=" # calendar id - determined by /users/id/calendars diff --git a/db.py b/db.py index fbca571..755cd07 100644 --- a/db.py +++ b/db.py @@ -4,12 +4,11 @@ import click from flask import current_app, g from flask.cli import with_appcontext -DATABASE_PATH = "users.db" def get_db(): if 'db' not in g: g.db = sqlite3.connect( - DATABASE_PATH, + current_app.config["DATABASE_URI"], detect_types=sqlite3.PARSE_DECLTYPES ) g.db.row_factory = sqlite3.Row diff --git a/run_dev_server.sh b/run_dev_server.sh index 43a3f09..6f514ee 100644 --- a/run_dev_server.sh +++ b/run_dev_server.sh @@ -1,2 +1,3 @@ export FLASK_ENV=development +export CONFIG=config.DevelopmentConfig flask run --host=0.0.0.0 \ No newline at end of file