diff --git a/.github/workflows/main_articlescmsapp.yml b/.github/workflows/main_articlescmsapp.yml new file mode 100644 index 000000000..775c2b817 --- /dev/null +++ b/.github/workflows/main_articlescmsapp.yml @@ -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 }} \ No newline at end of file diff --git a/FlaskWebProject/views.py b/FlaskWebProject/views.py index 62be10a8c..56941cacb 100644 --- a/FlaskWebProject/views.py +++ b/FlaskWebProject/views.py @@ -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") @@ -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)) diff --git a/config.py b/config.py index 0b2223b51..50323e13f 100644 --- a/config.py +++ b/config.py @@ -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 @@ -30,7 +30,7 @@ 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 @@ -38,4 +38,4 @@ class Config(object): # 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 \ No newline at end of file + SESSION_TYPE = "filesystem" # Token cache will be stored in server-side session diff --git a/requirements.txt b/requirements.txt index 5b3acd89d..31af97367 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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