{"id":13775048,"url":"https://github.com/Azure-Samples/azure-sql-db-secure-data-access-api","last_synced_at":"2025-05-11T07:31:53.003Z","repository":{"id":41439028,"uuid":"249258971","full_name":"Azure-Samples/azure-sql-db-secure-data-access-api","owner":"Azure-Samples","description":"Creating API that can guarantee secured and controlled data access to your data","archived":false,"fork":false,"pushed_at":"2023-09-12T23:26:02.000Z","size":48,"stargazers_count":4,"open_issues_count":1,"forks_count":2,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-04-11T17:02:21.837Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TSQL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Azure-Samples.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-03-22T19:41:13.000Z","updated_at":"2023-01-11T15:08:37.000Z","dependencies_parsed_at":"2024-01-13T11:12:46.100Z","dependency_job_id":null,"html_url":"https://github.com/Azure-Samples/azure-sql-db-secure-data-access-api","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Azure-Samples%2Fazure-sql-db-secure-data-access-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Azure-Samples%2Fazure-sql-db-secure-data-access-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Azure-Samples%2Fazure-sql-db-secure-data-access-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Azure-Samples%2Fazure-sql-db-secure-data-access-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Azure-Samples","download_url":"https://codeload.github.com/Azure-Samples/azure-sql-db-secure-data-access-api/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225027329,"owners_count":17409412,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T17:01:33.197Z","updated_at":"2024-11-17T10:30:28.143Z","avatar_url":"https://github.com/Azure-Samples.png","language":"TSQL","funding_links":[],"categories":["Code Samples"],"sub_categories":["REST"],"readme":"---\r\npage_type: sample\r\nlanguages:\r\n- python\r\n- tsql\r\n- sql\r\n- json\r\nproducts:\r\n- azure\r\n- vs-code\r\n- azure-sql-database\r\n- azure-app-service\r\n- azure-app-service-web\r\ndescription: \"Creating API to securely access data using Row Level Security\"\r\nurlFragment: \"azure-sql-db-secure-data-access-api\"\r\n---\r\n\r\n# Creating API to securely access data using Azure SQL Row Level Security\r\n\r\n![License](https://img.shields.io/badge/license-MIT-green.svg)\r\n\r\n\u003c!-- \r\nGuidelines on README format: https://review.docs.microsoft.com/help/onboard/admin/samples/concepts/readme-template?branch=master\r\n\r\nGuidance on onboarding samples to docs.microsoft.com/samples: https://review.docs.microsoft.com/help/onboard/admin/samples/process/onboarding?branch=master\r\n\r\nTaxonomies for products and languages: https://review.docs.microsoft.com/new-hope/information-architecture/metadata/taxonomies?branch=master\r\n--\u003e\r\n\r\nAn API should allow its users to securely access the data in the database used by the API itself. At the same time it also must assure that data is protected and secured from those users who doesn't have enough authorization. This is even more important when creating multi-tenant applications.\r\n\r\nAzure SQL offers an amazing feature to secure data at the database level, so that all the burden of taking care of such important and critical effort is done automatically by the database engine, so that the API code can be cleaner and easier to maintain and evolve. Not to mention better performances and improved efficiency as data will not leave the database at all, if the user has not the correct permissions.\r\n\r\nThis repo guides you to the creation of a API solution, deployable in Azure, that take advantage of Azure SQL Row Level Security to create secure API using Python, Flask and JWT. The same approach could be used with .NET or any other language that allows you to connect to Azure SQL.\r\n\r\nA detailed video on how this sample work is available here:\r\n\r\nhttps://youtu.be/Qpv8ke8ZuQ8\r\n\r\nThe sample simulate an authenticated user by passing in the JWT token (that you'll generate using the `pyjwt` tool) the hashed User Id. From a security point of view you want to make sure that a user can access only to his own data (or to the data s/he has been authorized to). \r\n\r\n## Install Sample Database\r\n\r\nIn order to run this sample, you need a Azure SQL database to use. If you already have one that can be used as a developer playground you can used that. Make sure create all the needed objects by executing the script:\r\n\r\n`./sql/00-SetupRLS.sql`\r\n\r\nOtherwise you can restore the `rls_sample` database by using the \r\n\r\n`./sql/rls_sample.bacpac`. If you already know how to restore a database, great!, go on and once restore is done move on to next section. Otherwise, or if you want some scripts to help, use the following link:\r\n\r\n[How To Restore Database](https://github.com/yorek/azure-sql-db-samples#restore-wideworldimporters-database)\r\n\r\nIf you need any help in executing the SQL script, you can find a Quickstart here: [Quickstart: Use Azure Data Studio to connect and query Azure SQL database](https://docs.microsoft.com/en-us/sql/azure-data-studio/quickstart-sql-database)\r\n\r\n## Run sample locally\r\n\r\nMake sure you have Python 3.7 installed on your machine. Clone this repo in a directory on our computer and then create a [virtual environment](https://www.youtube.com/watch?v=_eczHOiFMZA\u0026list=PLlrxD0HtieHhS8VzuMCfQD4uJ9yne1mE6\u0026index=34). For example:\r\n\r\n```bash\r\nvirtualenv venv --python C:\\Python37\\\r\n```\r\n\r\nthen activate the created virtual environment. For example, on Windows:\r\n\r\n```powershell\r\n.\\venv\\Scripts\\activate\r\n```\r\n\r\nand then install all the required packages:\r\n\r\n```bash\r\npip install -r requirements\r\n```\r\n\r\nThe connections string is not saved in the python code for security reasons, so you need to assign it to an environment variable in order to run the sample successfully. You also want to enable [development environment](https://flask.palletsprojects.com/en/1.1.x/config/#environment-and-debug-features) for Flask:\r\n\r\nLinux:\r\n\r\n```bash\r\nexport FLASK_ENV=\"development\"\r\nexport SQLAZURECONNSTR_RLS=\"\u003cyour-connection-string\u003e\"\r\n```\r\n\r\nWindows:\r\n\r\n```powershell\r\n$Env:FLASK_ENV=\"development\"\r\n$Env:SQLAZURECONNSTR_RLS=\"\u003cyour-connection-string\u003e\"\r\n```\r\n\r\nYour connection string is something like:\r\n\r\n```\r\nDRIVER={ODBC Driver 17 for SQL Server};SERVER=\u003cyour-server-name\u003e.database.windows.net;DATABASE=\u003cyour-database-name\u003e;UID=MiddleTierUser;PWD=a987REALLY#$%TRONGpa44w0rd;\r\n```\r\n\r\nJust replace `\u003cyour-server-name\u003e` and `\u003cyour-database-name\u003e` with the correct values for your environment.\r\n\r\nTo run and test the Python REST API local, just run\r\n\r\n```bash\r\nflask run\r\n```\r\n\r\nPython will start the HTTP server and when everything is up and running you'll see something like\r\n\r\n```text\r\n * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\r\n```\r\n\r\nUsing a REST Client (like [Insomnia](https://insomnia.rest/), [Postman](https://www.getpostman.com/) or curl), you can now call your API. The API requires a Bearer Token that contains the Hashed User Id of the user you want to simulate:\r\n\r\n|User|Hashed Id|\r\n|---|---|\r\n|Jane Dean|6134311589|\r\n|John Doe|1225328053|\r\n\r\nthe definition of who can see what is stored in the `rls.SensitiveDataPermissions` table.\r\n\r\nTo generate the Bearer Token you can use the `pyjwt` that is automatically installed by the `pyjwt` python package. Use the key `mySUPERs3cr3t` to sign the JWT message.\r\n\r\nLinux:\r\n\r\n```bash\r\nexport token=`pyjwt --key=mySUPERs3cr3t encode iss=me exp=+600 user-hash-id=1225328053`\r\ncurl -s -H \"Authorization: Bearer ${token}\" -X GET http://localhost:5000/sensitive-data/more  | jq .\r\n```\r\n\r\nWindows:\r\n\r\n```powershell\r\n$token = pyjwt --key=mySUPERs3cr3t encode iss=me exp=+600 user-hash-id=1225328053\r\n(Invoke-WebRequest -Uri http://localhost:5000/sensitive-data/more -Method GET -Headers @{\"Authorization\"=\"Bearer $token\"}).Content\r\n```\r\n\r\nand you'll get info on Customer 123:\r\n\r\n```json\r\n[\r\n    {\r\n        \"Id\": 1,\r\n        \"FirstName\": \"Jane\",\r\n        \"LastName\": \"Dean\",\r\n        \"EvenMore\": [...]\r\n    },\r\n    {\r\n        \"Id\": 2,\r\n        \"FirstName\": \"John\",\r\n        \"LastName\": \"Doe\",\r\n        \"EvenMore\": [...]\r\n    }\r\n]\r\n```\r\n\r\nAs you can see, data for both users is returned, even if you are invoking the API using a specific User. This is because the Row Level Security feature is *disabled*.\r\n\r\n## Enable Row Level Security\r\n\r\nTo enable to Row Level Security Policy execute the following code in the sample database:\r\n\r\n```sql\r\nalter security policy rls.SensitiveDataPolicy with (state = on)\r\n```\r\n\r\nIf you try to access the same API again, you'll now see only the data for the user you are simulating:\r\n\r\n```json\r\n[    \r\n    {\r\n        \"Id\": 2,\r\n        \"FirstName\": \"John\",\r\n        \"LastName\": \"Doe\",\r\n        \"EvenMore\": [...]\r\n    }\r\n]\r\n```\r\n\r\n## Debug from Visual Studio Code\r\n\r\nDebugging from Visual Studio Code is fully supported. Make sure you create an `.env` file the look like the following one (making sure you add your connection string)\r\n\r\n```\r\nFLASK_ENV=\"development\"\r\nSQLAZURECONNSTR_RLS=\"\"\r\n```\r\n\r\nand you'll be good to go.\r\n\r\n## Deploy to Azure\r\n\r\nNow that your REST API solution is ready, it's time to deploy it on Azure so that anyone can take advantage of it. A detailed article on how you can that that is here:\r\n\r\n- [Deploying Python web apps to Azure App Services](https://medium.com/@GeekTrainer/deploying-python-web-apps-to-azure-app-services-413cc16d4d68)\r\n- [Quickstart: Create a Python app in Azure App Service on Linux](https://docs.microsoft.com/en-us/azure/app-service/containers/quickstart-python?tabs=bash)\r\n\r\nThe only thing you have do in addition to what explained in the above articles is to add the connection string to the Azure Web App configuration. Using AZ CLI, for example:\r\n\r\n```bash\r\nappName=\"azure-sql-db-secure-data-access-api\"\r\nresourceGroup=\"my-resource-group\"\r\n\r\naz webapp config connection-string set \\\r\n    -g $resourceGroup \\\r\n    -n $appName \\\r\n    --settings RLS=$SQLAZURECONNSTR_RLS \\\r\n    --connection-string-type=SQLAzure\r\n```\r\n\r\nJust make sure you correctly set `$appName` and `$resourceGroup` to match your environment and also that the variable `$SQLAZURECONNSTR_RLS` as also been set, as mentioned in section \"Run sample locally\". An example of a full script that deploy the REST API is available here: `azure-deploy.sh`.\r\n\r\nPlease note that connection string are accessible as environment variables from Python when running on Azure, *but they are prefixed* as documented here:\r\n\r\nhttps://docs.microsoft.com/en-us/azure/app-service/configure-common#connection-strings\r\n\r\nThat's why the Python code in the sample look for `SQLAZURECONNSTR_RLS` but the Shell script write the `RLS` connection string name.\r\n\r\n## Next Steps\r\n\r\nTo properly secure your API you need some kind of authentication. You can use Azure Active Directory:\r\n\r\n[Microsoft Authentication Library (MSAL) for Python](https://github.com/AzureAD/microsoft-authentication-library-for-python)\r\n\r\nor some third party service like Auth0:\r\n\r\n- [Auth0 Python Web App](https://github.com/auth0-samples/auth0-python-web-app)\r\n- [Auth0 Python API](https://github.com/auth0-samples/auth0-python-api-samples)\r\n\r\n## Learn more\r\n\r\nHere's some link if you want to learn more, or you are new to Python\r\n\r\n[REST API using Python, Flask and Azure SQL](https://github.com/Azure-Samples/azure-sql-db-python-rest-api)\r\n\r\nIf you want to do the same in .NET, this repo is a good start:\r\n\r\n[REST API using .Net, Dapper and Azure SQL](https://github.com/Azure-Samples/azure-sql-db-dotnet-rest-api)\r\n\r\n## Contributing\r\n\r\nThis project welcomes contributions and suggestions.  Most contributions require you to agree to a\r\nContributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\r\nthe rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.\r\n\r\nWhen you submit a pull request, a CLA bot will automatically determine whether you need to provide\r\na CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions\r\nprovided by the bot. You will only need to do this once across all repos using our CLA.\r\n\r\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\r\nFor more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\r\ncontact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAzure-Samples%2Fazure-sql-db-secure-data-access-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAzure-Samples%2Fazure-sql-db-secure-data-access-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAzure-Samples%2Fazure-sql-db-secure-data-access-api/lists"}