Skip to content
Closed
71 changes: 71 additions & 0 deletions .github/workflows/main_articlescmsapp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions
# More info on Python, GitHub Actions, and Azure App Service: https://aka.ms/python-webapps-actions

name: Build and deploy Python app to Azure Web App - articlescmsapp

on:
push:
branches:
- main
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read #This is required for actions/checkout

steps:
- uses: actions/checkout@v4

- name: Set up Python version
uses: actions/setup-python@v5
with:
python-version: '3.13'

- name: Create and start virtual environment
run: |
python -m venv venv
source venv/bin/activate

- name: Install dependencies
run: pip install -r requirements.txt

# Optional: Add step to run tests here (PyTest, Django test suites, etc.)

- name: Zip artifact for deployment
run: zip release.zip ./* -r

- name: Upload artifact for deployment jobs
uses: actions/upload-artifact@v4
with:
name: python-app
path: |
release.zip
!venv/

deploy:
runs-on: ubuntu-latest
needs: build
environment:
name: 'Production'
url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}

steps:
- name: Download artifact from build job
uses: actions/download-artifact@v4
with:
name: python-app

- name: Unzip artifact for deployment
run: unzip release.zip


- name: 'Deploy to Azure Web App'
uses: azure/webapps-deploy@v3
id: deploy-to-webapp
with:
app-name: 'articlescmsapp'
slot-name: 'Production'
publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_D5A95387FEDF4652A1B0A3CE5CA93B2D }}
21 changes: 17 additions & 4 deletions FlaskWebProject/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ def authorized():
if request.args.get('code'):
cache = _load_cache()
# TODO: Acquire a token from a built msal app, along with the appropriate redirect URI
result = None
result = _build_msal_app(cache=cache).acquire_token_by_authorization_code(
request.args['code'],
scopes=Config.SCOPE,
redirect_uri=url_for('authorized',_external=True,_scheme='https')
)
if "error" in result:
return render_template("auth_error.html", result=result)
session["user"] = result.get("id_token_claims")
Expand All @@ -112,17 +116,26 @@ def logout():

def _load_cache():
# TODO: Load the cache from `msal`, if it exists
cache = None
cache = msal.SerializableTokenCache()
if session.get('token_cache'):
cache.deserialize(session['token_cache'])
return cache

def _save_cache(cache):
# TODO: Save the cache, if it has changed
if cache.has_state_changed:
session['token_cache']=cache.serialize()
pass

def _build_msal_app(cache=None, authority=None):
# TODO: Return a ConfidentialClientApplication
return None
return msal.ConfidentialClientApplication(
Config.CLIENT_ID, authority=authority or Config.AUTHORITY,
client_credential=Config.CLIENT_SECRET, token_cache=cache)

def _build_auth_url(authority=None, scopes=None, state=None):
# TODO: Return the full Auth Request URL with appropriate Redirect URI
return None
return _build_msal_app(authority=authority).get_authorization_request_url(
scopes or [],
state=state or str(uuid.uuid4()),
redirect_uri=url_for('authorized', _external=True))
22 changes: 11 additions & 11 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@
basedir = os.path.abspath(os.path.dirname(__file__))

class Config(object):
SECRET_KEY = os.environ.get('SECRET_KEY') or 'secret-key'
SECRET_KEY = os.environ.get('SECRET_KEY') #or 'secret-key'

BLOB_ACCOUNT = os.environ.get('BLOB_ACCOUNT') or 'ENTER_STORAGE_ACCOUNT_NAME'
BLOB_STORAGE_KEY = os.environ.get('BLOB_STORAGE_KEY') or 'ENTER_BLOB_STORAGE_KEY'
BLOB_CONTAINER = os.environ.get('BLOB_CONTAINER') or 'ENTER_IMAGES_CONTAINER_NAME'
BLOB_ACCOUNT = os.environ.get('BLOB_ACCOUNT') #or 'ENTER_STORAGE_ACCOUNT_NAME'
BLOB_STORAGE_KEY = os.environ.get('BLOB_STORAGE_KEY') #or 'ENTER_BLOB_STORAGE_KEY'
BLOB_CONTAINER = os.environ.get('BLOB_CONTAINER') #or 'ENTER_IMAGES_CONTAINER_NAME'

SQL_SERVER = os.environ.get('SQL_SERVER') or 'ENTER_SQL_SERVER_NAME.database.windows.net'
SQL_DATABASE = os.environ.get('SQL_DATABASE') or 'ENTER_SQL_DB_NAME'
SQL_USER_NAME = os.environ.get('SQL_USER_NAME') or 'ENTER_SQL_SERVER_USERNAME'
SQL_PASSWORD = os.environ.get('SQL_PASSWORD') or 'ENTER_SQL_SERVER_PASSWORD'
SQL_SERVER = os.environ.get('SQL_SERVER') #or 'ENTER_SQL_SERVER_NAME.database.windows.net'
SQL_DATABASE = os.environ.get('SQL_DATABASE') #or 'ENTER_SQL_DB_NAME'
SQL_USER_NAME = os.environ.get('SQL_USER_NAME') #or 'ENTER_SQL_SERVER_USERNAME'
SQL_PASSWORD = os.environ.get('SQL_PASSWORD') #or 'ENTER_SQL_SERVER_PASSWORD'
# Below URI may need some adjustments for driver version, based on your OS, if running locally
SQLALCHEMY_DATABASE_URI = 'mssql+pyodbc://' + SQL_USER_NAME + '@' + SQL_SERVER + ':' + SQL_PASSWORD + '@' + SQL_SERVER + ':1433/' + SQL_DATABASE + '?driver=ODBC+Driver+17+for+SQL+Server'
SQLALCHEMY_TRACK_MODIFICATIONS = False

### Info for MS Authentication ###
### As adapted from: https://github.com/Azure-Samples/ms-identity-python-webapp ###
CLIENT_SECRET = "ENTER_CLIENT_SECRET_HERE"
CLIENT_SECRET = "Nvx8Q~8LQGMU2qHyQxE5o4PdzDljWomxRrrGuagS"
# In your production app, Microsoft recommends you to use other ways to store your secret,
# such as KeyVault, or environment variable as described in Flask's documentation here:
# https://flask.palletsprojects.com/en/1.1.x/config/#configuring-from-environment-variables
Expand All @@ -30,12 +30,12 @@ class Config(object):
AUTHORITY = "https://login.microsoftonline.com/common" # For multi-tenant app, else put tenant name
# AUTHORITY = "https://login.microsoftonline.com/Enter_the_Tenant_Name_Here"

CLIENT_ID = "ENTER_CLIENT_ID_HERE"
CLIENT_ID = "6428336a-ba8f-4307-8dba-0648d89b2998"

REDIRECT_PATH = "/getAToken" # Used to form an absolute URL; must match to app's redirect_uri set in AAD

# You can find the proper permission names from this document
# https://docs.microsoft.com/en-us/graph/permissions-reference
SCOPE = ["User.Read"] # Only need to read user profile for this app

SESSION_TYPE = "filesystem" # Token cache will be stored in server-side session
SESSION_TYPE = "filesystem" # Token cache will be stored in server-side session
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ MarkupSafe==2.0.1
msal==1.13.0
pip==10.0.1
pycparser==2.20
pyodbc==4.0.28
pyopenssl==19.1.0
python-dateutil==2.8.1
python-editor==1.0.4
Expand Down