Skip to content
This repository has been archived by the owner on Aug 28, 2024. It is now read-only.

🛂 Add EntraID Authentication #4

Merged
merged 5 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CLIENT_ID=<your_client_id>
CLIENT_SECRET=<your_client_secret>
AZURE_TENANT_ID=<your_azure_tenant_id>
REDIRECT_URI=<your_redirect_uri>

# when running locally, you can use
REDIRECT_URI="https://127.0.0.1:8000/azure_auth/callback"
51 changes: 49 additions & 2 deletions ollamate/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,30 @@

from pathlib import Path

import os
import environ

# Initialize environment variables
env = environ.Env()

# Set the project base directory
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Read the .env file
env_file = os.path.join(BASE_DIR, '.env')
if os.path.isfile(env_file):
print(f"Loading environment variables from {env_file}")
environ.Env.read_env(env_file)
else:
print(f"{env_file} not found")
print("CLIENT_ID:", env("CLIENT_ID"))
print("CLIENT_SECRET:", env("CLIENT_SECRET"))
print("TENANT_ID:", env("AZURE_TENANT_ID"))
print("REDIRECT_URI:", env("REDIRECT_URI"))

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/

Expand All @@ -37,7 +57,8 @@
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'streamingapp'
'streamingapp',
'azure_auth'
]

MIDDLEWARE = [
Expand Down Expand Up @@ -122,3 +143,29 @@
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

# Azure authentication settings

AZURE_AUTH = {
"CLIENT_ID": env("CLIENT_ID"),
"CLIENT_SECRET": env("CLIENT_SECRET"),
"REDIRECT_URI": env("REDIRECT_URI"),
"SCOPES": ["User.Read"],
"AUTHORITY": "https://login.microsoftonline.com/{}".format(env("AZURE_TENANT_ID")), # Or https://login.microsoftonline.com/common if multi-tenant
# # "LOGOUT_URI": "https://<domain>/logout", # Optional
# # "PUBLIC_URLS": ["<public:view_name>",], # Optional, public views accessible by non-authenticated users
# # "PUBLIC_PATHS": ['/go/',], # Optional, public paths accessible by non-authenticated users
# # "ROLES": {
# # "95170e67-2bbf-4e3e-a4d7-e7e5829fe7a7": "GroupName1",
# # "3dc6539e-0589-4663-b782-fef100d839aa": "GroupName2"
# # }, # Optional, will add user to django group if user is in EntraID group
"USERNAME_ATTRIBUTE": "mail", # The AAD attribute or ID token claim you want to use as the value for the user model `USERNAME_FIELD`
# # "EXTRA_FIELDS": [], # Optional, extra AAD user profile attributes you want to make available in the user mapping function
# "USER_MAPPING_FN": "azure_auth.tests.misc.user_mapping_fn", # Optional, path to the function used to map the AAD to Django attributes
}
LOGIN_URL = "/azure_auth/login"
LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = '/'

AUTHENTICATION_BACKENDS = ("azure_auth.backends.AzureBackend",)

5 changes: 4 additions & 1 deletion ollamate/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@
"""
from django.contrib import admin
from django.urls import path, include
from streamingapp.views import redirect_to_ollama

urlpatterns = [
path('admin/', admin.site.urls),
path('stream/', include('streamingapp.urls'))
path("azure_auth/", include("azure_auth.urls"),),
path('stream/', include('streamingapp.urls')),
path('', redirect_to_ollama)
]
20 changes: 12 additions & 8 deletions streamingapp/templates/streamingapp/input_form.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
Expand Down Expand Up @@ -43,25 +42,30 @@
<div class="w-full bg-yellow-400 text-center p-2">
<p class="text-black font-bold">A Proof of Concept by the <a href="https://github.com/ministryofjustice/analytical-platform" class="text-blue-700 hover:underline">Analytical Platform</a></p>
</div>
</div>
</div>
<div class="container mx-auto p-4">
<h1 class="text-3xl font-bold mb-4 text-center">Ollamate 🦙🍵</h1>
<h2 class="text-xl font-bold mb-4 text-center">A Simple Conversational AI.</h2>
<p class="mb-4">Enter text below to interact with Ollamate:</p>
<div id="conversation-log" class="bg-white p-4 rounded shadow-lg hidden text-base">
<p class="mb-4 text-center">Logged in as: <strong>{{ user.username }}</strong></p>
<div class="mt-4 text-center">
{% if user.is_authenticated %}
<a href="{% url 'azure_auth:logout' %}" class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">Logout</a>
{% else %}
<a href="{% url 'azure_auth:login' %}" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded">Login</a>
{% endif %}
</div>
<p class="mb-4">Enter text below to interact with Ollamate:</p>
<div id="conversation-log" class="bg-white p-4 rounded shadow-lg hidden text-base"></div>
<form id="ollamaForm" class="mt-4">
<div class="flex items-center">
<textarea id="userInput" name="userInput" required class="border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline resize-none"></textarea>
<button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white py-2 px-4 rounded ml-2">Send</button>
<button type="submit" class="bg-green-500 hover:bg-green-700 text-white py-2 px-4 rounded ml-2">Send</button>
</div>
</form>
</div>

<footer class="bg-gray-800 text-white py-4 mt-8">
<div class="container mx-auto text-center">
<p>📝 <a href="https://github.com/ministryofjustice/analytical-platform" class="text-blue-500 hover:underline">GitHub Repository</a></p>
<p>📝 <a href="https://github.com/ministryofjustice/analytical-platform-ollamate" class="text-blue-500 hover:underline">GitHub Repository</a></p>
</div>
</footer>

Expand Down Expand Up @@ -129,4 +133,4 @@ <h2 class="text-xl font-bold mb-4 text-center">A Simple Conversational AI.</h2>
});
</script>
</body>
</html>
</html>
20 changes: 17 additions & 3 deletions streamingapp/views.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,50 @@
from django.shortcuts import render
from django.shortcuts import render, redirect
from django.http import JsonResponse
from azure_auth.decorators import azure_auth_required
import requests
import logging
import json

# Configure logging
logging.basicConfig(level=logging.DEBUG)

@azure_auth_required
def call_ollama(request):
if request.method == 'POST':
user_input = request.POST.get('userInput', '')
conversation_history = json.loads(user_input)
try:
conversation_history = json.loads(user_input)
except json.JSONDecodeError as e:
logging.error("JSONDecodeError: %s", e)
return JsonResponse({"error": "Invalid input format"}, status=400)

url = 'http://localhost:11434/api/chat'
headers = {'Content-Type': 'application/json'}
data = {
"model": "llama3",
"messages": conversation_history,
"stream": False
}

logging.debug("Sending data to Ollama API: %s", json.dumps(data, indent=2))

try:
response = requests.post(url, json=data, headers=headers)
response.raise_for_status()
response_data = response.json()
logging.debug("Response data: %s", json.dumps(response_data, indent=2))
# Extract the actual response message

ollama_response = response_data.get("message", {}).get("content", "")
if not ollama_response:
logging.error("Empty response from Ollama API")
return JsonResponse({"error": "Empty response from Ollama API"}, status=500)

return JsonResponse({"response": ollama_response})
except requests.RequestException as e:
logging.error("RequestException: %s", e)
return JsonResponse({"error": str(e)}, status=500)
else:
return render(request, 'streamingapp/input_form.html')

def redirect_to_ollama(request):
return redirect('/stream/call-ollama')