diff --git a/DEPLOYING.md b/DEPLOYING.md new file mode 100644 index 00000000..219793ad --- /dev/null +++ b/DEPLOYING.md @@ -0,0 +1,130 @@ +# Deploying the app + +#### Recommended configuration: + +- *Minimum disk space*: 8GB + +- *Minimum memory*: 4GB + - If too many users using at the same time, it might need to be higher. + +## Azure + +You can modify the code and deploy the container, or use our default container hosted on ghcr.io. + +### Deploying your container +#### TODO + +### Using ghcr.io + +**Prerequisites** + +- Azure Account: Ensure you have an [active Azure subscription](https://azure.microsoft.com/en-us/pricing/purchase-options/azure-account?msockid=1e4bc940d7cf6738158eda91d616667e). + +- Terraform: Install Terraform on your local machine. You can download it from [terraform.io](https://developer.hashicorp.com/terraform/install?product_intent=terraform). + +- Azure CLI: Install Azure CLI. You can download it from [docs.microsoft.com](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli). + +**Steps** + +1. Set Up Azure CLI + Login to Azure: + + `az login` + + This will open a browser window for you to authenticate. + Set the Subscription: + If you have multiple subscriptions, set the one you wish to use: + + `az account set --subscription "your-subscription-id"` + + Create a Directory for Your Project: + + ``` + mkdir my-terraform-app + cd my-terraform-app + ``` + +2. Use our Terraform Configuration File: + Download the [terraform configuration file here](https://github.com/microsoft/intelligence-toolkit/blob/main/deploy/azure/main.tf) + + + -Modify the `variables` default field to match your desired resource configuration + + - az_webapp_name (change necessary. Should be unique within Global Azure) + + - az_rg_name (change optional) + + - location (change optional) + + - az_asp_name (change optional) + +3. Initialize Terraform + + + `terraform init` + This command downloads the Azure provider and sets up your workspace. + +4. Create an Execution Plan + + Plan the Deployment: + + `terraform plan` + This command creates an execution plan, which lets you preview the changes that Terraform will make to your infrastructure. + +5. Apply the Execution Plan + + Deploy the Resources: + + `terraform apply` + Terraform will prompt you for confirmation before making any changes. Type yes and press Enter. + +6. Verify Deployment + + Check the Resources in Azure Portal: + Go to the Azure Portal and verify that the resources have been created. + + Check the deployed URL: + +`.azurewebsites.net` + +## AWS + +Wait for step 1 to be set as complete before starting step 2. The whole process will take up to 20 minutes. + +1. Launch the infrastructure deploy: + + - Give it a sugestive name since you'll be using it in the next step. + + [![launch-stack](https://s3.amazonaws.com/cloudformation-examples/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=itk-infra-stack&templateURL=https://s3.us-east-1.amazonaws.com/cf-templates-19n482mly1fba-us-east-1/2024-10-07T124926.165Z3xc-infrastructure.yaml) + +2. Launch the code deploy + - In VPC Configuration, you should select the resources created by the previous step: VPCId, PublicSubnetAId, PublicSubnetBId, PrivateSubnetAId, PrivateSubnetBId + + [![launch-stack](https://s3.amazonaws.com/cloudformation-examples/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=itk-code-stack&templateURL=https://s3.us-east-1.amazonaws.com/cf-templates-19n482mly1fba-us-east-1/2024-10-07T125858.730Zlsu-2-development.yaml) + + +Once step 2 it's complete, in the output tab, you'll see the deployed URL. + +**Note: This code doesn't have auth, so this URL will be open to the internet.** + +## Environment configuration + +`MODE: CLOUD` to hide Settings page so users can't change configuration that would affect other users experiences. + +`AUTH_ENABLED: TRUE` if you would like to limit access into the app by user and password defined in a .secrets.toml file inside .streamlit: + +``` +[passwords] +user_test = "user123" +... +``` + +Inserting secrets.toml file to the web app: + +Add a value of user=password separating each user by `;` to your web app environment. + +`USER_CREDENTIALS="user1=pass1;user2=pass2"` + + + + diff --git a/DEVELOPING.md b/DEVELOPING.md index f37f5d0a..69ef13fa 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -15,7 +15,6 @@ ## Running the app - ## GPT settings You can configure your OpenAI access when running the app via `Settings page`, or using environment variables. @@ -104,8 +103,8 @@ See the documentation and an example of how to run the code with your data to ob ##### Recommended configuration: -- *Minimum disk space*: 10GB -- *Minimum memory*: 8GB +- *Minimum disk space*: 8GB +- *Minimum memory*: 4GB Download, install and then open docker app: https://www.docker.com/products/docker-desktop/ @@ -122,56 +121,28 @@ Use `cd `+ the path to the folder. For example: `cd C:\Users\user01\projects\intelligence-toolkit` -Pull the latest built image: -` docker pull ghcr.io/microsoft/intelligence-toolkit:latest` -or build it with your own code: +Build it with your own code: `docker build . -t intelligence-toolkit` -Once the pull/build ir fininshed, run the docker container: - -- via shell: - - `docker run -d -p 80:80 intelligence-toolkit --name intelligence-toolkit` - -Open [localhost:80](http://localhost:80) - -## Deploying - -#### Recommended configuration: - -- *Minimum disk space*: 10GB +Or pull the latest built image: -- *Minimum memory*: 8GB - - If too many users using at the same time, it might need to be higher. - - -### Using AWS - -Wait for step 1 to be set as complete before starting step 2. The whole process will take up to 20 minutes. - -1. Launch the infrastructure deploy: - - - Give it a sugestive name since you'll be using it in the next step. - - [![launch-stack](https://s3.amazonaws.com/cloudformation-examples/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=itk-infra-stack&templateURL=https://s3.us-east-1.amazonaws.com/cf-templates-19n482mly1fba-us-east-1/2024-10-07T124926.165Z3xc-infrastructure.yaml) - -2. Launch the code deploy - - In VPC Configuration, you should select the resources created by the previous step: VPCId, PublicSubnetAId, PublicSubnetBId, PrivateSubnetAId, PrivateSubnetBId +` docker pull ghcr.io/microsoft/intelligence-toolkit:latest` - [![launch-stack](https://s3.amazonaws.com/cloudformation-examples/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=itk-code-stack&templateURL=https://s3.us-east-1.amazonaws.com/cf-templates-19n482mly1fba-us-east-1/2024-10-07T125858.730Zlsu-2-development.yaml) +Once the pull/build is fininshed, run the docker container: -Once step 2 it's complete, in the output tab, you'll see the deployed URL. +- via shell: -**Note: This code doesn't have auth, so this URL will be open to the internet.** + With `` being `intelligence-toolkit` if you used docker build or `ghcr.io/microsoft/intelligence-toolkit:latest` if you used docker pull. -### Using Azure + `docker run -d --name intelligence-toolkit -p 80:80 ` -In [this tutorial](https://dev.to/keneojiteli/deploy-a-docker-app-to-app-services-on-azure-5d3h), you can learn how to create the necessary services in azure. +Open [localhost:80](http://localhost:80) -From there, you can deploy it manually as described, or use [our YAML file](/.vsts-ci.yml) to automatically deploy to your environment. +## Deploying +See [instructions]('./DEPLOYING.md') # Lifecycle Scripts diff --git a/Dockerfile b/Dockerfile index a545754c..bf26a515 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,6 @@ RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor ENV PATH="/root/.local/bin:$PATH" COPY . . -RUN rm -rf .streamlit/app_secrets.toml RUN poetry install --only main # Run application diff --git a/app/Home.py b/app/Home.py index 665fe7e6..dc42e2ee 100644 --- a/app/Home.py +++ b/app/Home.py @@ -42,12 +42,11 @@ def main(): page_icon=f"{path}myapp.ico", page_title="Intelligence Toolkit | Home", ) + load_multipage_app() transparency_faq, mermaid_text = get_readme_and_mermaid() - st.markdown( - transparency_faq - ) + st.markdown(transparency_faq) mermaid.mermaid( code=mermaid_text, @@ -55,5 +54,6 @@ def main(): ) + if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/app/components/app_auth.py b/app/components/app_auth.py new file mode 100644 index 00000000..0f972823 --- /dev/null +++ b/app/components/app_auth.py @@ -0,0 +1,70 @@ +# Copyright (c) 2024 Microsoft Corporation. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project. +# + +import hmac +import os + +import streamlit as st +import toml + +credentials_string = os.getenv("USER_CREDENTIALS") +credentials_dict = {} + + +def check_password() -> bool: + """Returns `True` if the user had a correct password.""" + + def login_form(): + """Form with widgets to collect user information""" + with st.form("Credentials"): + st.text_input("Username", key="username") + st.text_input("Password", type="password", key="password") + st.form_submit_button("Log in", on_click=password_entered) + + def password_entered(): + """Checks whether a password entered by the user is correct.""" + if st.session_state["username"] in st.secrets[ + "passwords" + ] and hmac.compare_digest( + st.session_state["password"], + st.secrets.passwords[st.session_state["username"]], + ): + st.session_state["password_correct"] = True + del st.session_state["password"] # Don't store the password. + else: + st.session_state["password_correct"] = False + + # Return True if the username + password is validated. + if st.session_state.get("password_correct", False): + return True + + # Show inputs for username + password. + login_form() + if "password_correct" in st.session_state: + st.error("User not known or password incorrect") + st.stop() + return False + + +def load_passwords() -> None: + if credentials_string: + user_pairs = credentials_string.split(";") + for pair in user_pairs: + if ":" in pair: + user, password = pair.split(":") + credentials_dict[user] = password + + secrets_content = {"passwords": credentials_dict} + + secrets_file_path = os.path.join(".streamlit", "secrets.toml") + if not os.path.exists(secrets_file_path): + with open(secrets_file_path, "w") as secrets_file: + toml.dump(secrets_content, secrets_file) + else: + with open(secrets_file_path) as secrets_file: + current_secrets = toml.load(secrets_file) + if current_secrets != secrets_content: + with open(secrets_file_path, "w") as secrets_file: + toml.dump(secrets_content, secrets_file) + print("User auth info updated") diff --git a/app/components/app_loader.py b/app/components/app_loader.py index 61e47d89..b7d14633 100644 --- a/app/components/app_loader.py +++ b/app/components/app_loader.py @@ -1,14 +1,33 @@ # Copyright (c) 2024 Microsoft Corporation. All rights reserved. # Licensed under the MIT license. See LICENSE file in the project. # +import os + import streamlit as st +from streamlit.source_util import ( + _on_pages_changed, + get_pages, +) import app.components.app_mode as am import app.components.app_user as au +from app.components.app_auth import check_password, load_passwords from app.javascript.styles import add_styles def load_multipage_app(sv=None): + if os.getenv("AUTH_ENABLED") == "TRUE": + load_passwords() + check_password() + if os.getenv("MODE") == "CLOUD": + current_pages = get_pages("Home.py") + + for key, value in current_pages.items(): + if value["page_name"] == "Settings": + del current_pages[key] + break + _on_pages_changed.send() + # Load user if logged in user = au.AppUser() user.view_get_info() diff --git a/app/components/app_user.py b/app/components/app_user.py index d2fe9a29..43bd122b 100644 --- a/app/components/app_user.py +++ b/app/components/app_user.py @@ -2,6 +2,7 @@ # Licensed under the MIT license. See LICENSE file in the project. # import streamlit as st + from app.javascript.scripts import get_auth_user from app.util.enums import Mode from app.util.session_variables import SessionVariables @@ -24,6 +25,9 @@ def _set_user(self, username): self.sv.username.value = username def view_get_info(self): + if st.session_state.get("username"): + self._set_user(st.session_state["username"]) + if self.sv.username.value: st.sidebar.write(f"Logged in as {self.sv.username.value}") diff --git a/deploy/azure/main.tf b/deploy/azure/main.tf new file mode 100644 index 00000000..2f3ec4fa --- /dev/null +++ b/deploy/azure/main.tf @@ -0,0 +1,61 @@ +# This configuration has been generated by the Azure deployments handler which utilizes Generative AI which may result in unintended or inaccurate configuration code. A human must validate that this configuration accomplishes the desired goal before applying the configuration. + +provider "azurerm" { + features {} +} + +variable "az_webapp_name" { + type = string + default = "" +} + +variable "az_rg_name" { + type = string + default = "webapp-itk-rg" +} + +variable "location" { + type = string + default = "East US" +} + +variable "az_asp_name" { + type = string + default = "webapp-itk-appserviceplan" +} + +variable "ghcr_image" { + type = string + default = "microsoft/intelligence-toolkit:latest" +} + +resource "azurerm_resource_group" "az_rg" { + name = var.az_rg_name + location = var.location +} + +resource "azurerm_service_plan" "az_asp" { + os_type="Linux" + name = var.az_asp_name + location = azurerm_resource_group.az_rg.location + resource_group_name = azurerm_resource_group.az_rg.name + + sku_name = "B3" +} + +resource "azurerm_linux_web_app" "az_webapp" { + name = var.az_webapp_name + location = azurerm_resource_group.az_rg.location + resource_group_name = azurerm_resource_group.az_rg.name + service_plan_id = azurerm_service_plan.az_asp.id + https_only = true + + site_config { + application_stack { + docker_image_name = "${var.ghcr_image}" + docker_registry_url = "https://ghcr.io" + + } + } + +}