aboutsummaryrefslogtreecommitdiffstats
path: root/src/api
diff options
context:
space:
mode:
authorJustin Choquette <jchoquette@iol.unh.edu>2023-08-07 14:10:19 -0400
committerJustin Choquette <jchoquette@iol.unh.edu>2023-08-07 14:16:04 -0400
commita6168306c08e8d5b207b9acc48869180d194ff01 (patch)
tree51ffcafac4ae0b5fd4da363d9bf839e8ad3fc286 /src/api
parenta09db9f287a02873c0226759f8ea444bb304cd59 (diff)
User subsystem
Change-Id: Ibef4ede9b2d6a3ea465f79a9b5cbcc821afbccae Signed-off-by: Justin Choquette <jchoquette@iol.unh.edu>
Diffstat (limited to 'src/api')
-rw-r--r--src/api/urls.py10
-rw-r--r--src/api/utils.py122
-rw-r--r--src/api/views.py118
3 files changed, 237 insertions, 13 deletions
diff --git a/src/api/urls.py b/src/api/urls.py
index b009aeb..755b61f 100644
--- a/src/api/urls.py
+++ b/src/api/urls.py
@@ -42,6 +42,11 @@ from api.views import (
list_labs,
all_users,
booking_details,
+ ipa_create_account,
+ ipa_confirm_account,
+ ipa_set_company_from_workflow,
+ ipa_add_ssh_from_workflow,
+ ipa_conflict_account
)
urlpatterns = [
@@ -60,5 +65,10 @@ urlpatterns = [
path('users', all_users),
path('labs', list_labs),
+ path('ipa/create', ipa_create_account),
+ path('ipa/confirm', ipa_confirm_account),
+ path('ipa/conflict', ipa_conflict_account),
+ path('ipa/workflow-company', ipa_set_company_from_workflow),
+ path('ipa/workflow-ssh', ipa_add_ssh_from_workflow),
url(r'^token$', GenerateTokenView.as_view(), name='generate_token'),
]
diff --git a/src/api/utils.py b/src/api/utils.py
new file mode 100644
index 0000000..c32205e
--- /dev/null
+++ b/src/api/utils.py
@@ -0,0 +1,122 @@
+# These functions are called from views and perform the actual request to LibLaaS
+
+import json
+from django.http.response import JsonResponse, HttpResponse
+import requests
+import os
+
+from dashboard.forms import *
+liblaas_base_url = os.environ.get("LIBLAAS_BASE_URL")
+
+# IPA Stuff
+def ipa_query_user(ipa_username):
+ url = liblaas_base_url + "user/" + ipa_username
+ print("Getting ipa user for", ipa_username, url)
+ try:
+ response = requests.get(url)
+ data = response.json()
+ print("ipa user is", data)
+ return data # Expects a dict
+ except Exception as e:
+ print(e)
+ return None
+
+# Queries for an IPA user using dashboard username
+# Returns a result
+def get_ipa_migration_form(user, profile):
+ # ipa_user = ipa_query_user(str(dashboard_user))
+ # if (ipa_user and ipa_user.mail is )
+ # pass
+ dashboard_username = str(user)
+ dashboard_email = profile.email_addr
+ first_name = user.first_name
+ last_name = user.last_name
+
+ ipa_user = ipa_query_user(dashboard_username)
+ print("Attempting auto migration with", dashboard_username, dashboard_email, ipa_user)
+ if (ipa_user):
+ if (dashboard_email == ipa_user["mail"]):
+ # User is found and email match
+ print("User is found and email match")
+ return {
+ "form": ReadOnlyIPAAccountForm(initial={'ipa_username': ipa_user['uid'],'first_name': ipa_user["givenname"], 'last_name': ipa_user["sn"], 'email': ipa_user["mail"], 'company': ipa_user["ou"]}),
+ "message": "We have located the following IPA account matching your username and email. Please confirm to link your account. You may change these details at any time.",
+ "action": "api/ipa/confirm",
+ "button": "Link"
+ }
+
+ else:
+ # User is found and emails don't match
+ print("User is found and emails don't match")
+ return {
+ "form": ConflictIPAAcountForm(initial={'first_name': first_name, 'last_name': last_name, 'email': dashboard_email}),
+ "message": "Our records indicate that you do not currently have an account in our IPA system, or your emails do not match. Please enter the following details to enroll your account.",
+ "action": "/",
+ "button": "Submit"
+ }
+ else:
+ # User is not found
+ print("User is not found")
+ return {
+ "form": NewIPAAccountForm(initial={'first_name': first_name, 'last_name': last_name, 'email': dashboard_email}),
+ "message": "Our records indicate that you do not currently have an account in our IPA system, or your usernames do not match. Please enter the following details to enroll your account.",
+ "action": "api/ipa/create",
+ "button": "Submit"
+ }
+
+# Take a list of strings, sends it to liblaas, replacing the IPA keys with the new keys
+def ipa_set_ssh(user_profile, ssh_key_list):
+ url = liblaas_base_url + "user/" + user_profile.ipa_username + "/ssh"
+ print(ssh_key_list)
+ print("Setting SSH keys with URL", url)
+ try:
+ requests.post(url, data=json.dumps(ssh_key_list), headers={'Content-Type': 'application/json'})
+ return HttpResponse(status=200)
+ except Exception as e:
+ print(e)
+ return HttpResponse(status=500)
+
+def ipa_set_company(user_profile, company_name):
+ url = liblaas_base_url + "user/" + user_profile.ipa_username + "/company"
+ print("Setting company with URL", url)
+ try:
+ requests.post(url, data=json.dumps(company_name), headers={'Content-Type': 'application/json'})
+ return HttpResponse(status=200)
+ except Exception as e:
+ print(e)
+ return HttpResponse(status=500)
+
+def get_booking_prereqs_validator(user_profile):
+ ipa_user = None
+ if (user_profile.ipa_username != None and user_profile.ipa_username != ""):
+ ipa_user = ipa_query_user(user_profile.ipa_username)
+
+ if ipa_user == None:
+ print("No user")
+ return {
+ "form": None,
+ "exists": "false",
+ "action": "no user"
+ }
+
+ if (not "ou" in ipa_user) or (ipa_user["ou"] == ""):
+ print("Missing company")
+ return {
+ "form": SetCompanyForm(),
+ "exists": "true",
+ "action": "/api/ipa/workflow-company"
+ }
+
+ if (not "ipasshpubkey" in ipa_user) or (ipa_user["ipasshpubkey"] == []):
+ print("Missing SSH key")
+ return {
+ "form": SetSSHForm(),
+ "exists": "true",
+ "action": "/api/ipa/workflow-ssh"
+ }
+
+ return {
+ "form": None,
+ "exists": "false",
+ "action": ""
+ } \ No newline at end of file
diff --git a/src/api/views.py b/src/api/views.py
index ea36a6d..98dd3dc 100644
--- a/src/api/views.py
+++ b/src/api/views.py
@@ -23,7 +23,7 @@ from django.views import View
from django.http import HttpResponseNotFound
from django.http.response import JsonResponse, HttpResponse
import requests
-from rest_framework import viewsets
+from api.utils import ipa_query_user, ipa_set_ssh, ipa_set_company
from rest_framework.authtoken.models import Token
from django.views.decorators.csrf import csrf_exempt
from django.core.exceptions import ObjectDoesNotExist
@@ -51,7 +51,7 @@ Most functions let you GET or POST to the same endpoint, and
the correct thing will happen
"""
-
+liblaas_base_url = os.environ.get('LIBLAAS_BASE_URL')
@method_decorator(login_required, name='dispatch')
class GenerateTokenView(View):
def get(self, request, *args, **kwargs):
@@ -249,20 +249,31 @@ def make_booking(request):
data = json.loads(request.body)
print("incoming data is ", data)
- allowed_users = list(data["allowed_users"])
- allowed_users.append(str(request.user))
+ # todo - test this
+ ipa_users = list(UserProfile.objects.get(user=request.user).ipa_username) # add owner's ipa username to list of allowed users to be sent to liblaas
+
+ for user in list(data["allowed_users"]):
+ collab_profile = UserProfile.objects.get(user=User.objects.get(username=user))
+ if (collab_profile.ipa_username == "" or collab_profile.ipa_username == None):
+ return JsonResponse(
+ data={},
+ status=406, # Not good practice but a quick solution until blob validation is fully supported within django instead of the frontend
+ safe=False
+ )
+ else:
+ ipa_users.append(collab_profile.ipa_username)
bookingBlob = {
"template_id": data["template_id"],
- "allowed_users": allowed_users,
+ "allowed_users": ipa_users,
"global_cifile": data["global_cifile"],
"metadata": {
"booking_id": None, # fill in after creating django object
- "owner": str(request.user),
+ "owner": UserProfile.objects.get(user=request.user).ipa_username,
"lab": "UNH_IOL",
"purpose": data["metadata"]["purpose"],
"project": data["metadata"]["project"],
- "length": data["metadata"]["length"]
+ "length": int(data["metadata"]["length"])
}
}
@@ -279,9 +290,8 @@ def make_booking(request):
print("successfully created booking object with id ", booking.id)
# Now add collabs
- for c in bookingBlob["allowed_users"]:
- if c != bookingBlob["metadata"]["owner"]: # Don't add self as a collab
- booking.collaborators.add(User.objects.get(username=c))
+ for c in list(data["allowed_users"]):
+ booking.collaborators.add(User.objects.get(username=c))
print("successfully added collabs")
# Now create it in liblaas
@@ -479,7 +489,7 @@ def liblaas_request(request) -> JsonResponse:
liblaas_endpoint = post_data["endpoint"]
payload = post_data["workflow_data"]
# Fill in actual username
- liblaas_endpoint = liblaas_endpoint.replace("[username]", str(request.user))
+ liblaas_endpoint = liblaas_endpoint.replace("[username]", UserProfile.objects.get(user=request.user).ipa_username)
liblaas_endpoint = liblaas_base_url + liblaas_endpoint
print("processed endpoint is ", liblaas_endpoint)
@@ -513,7 +523,7 @@ def liblaas_request(request) -> JsonResponse:
)
def liblaas_templates(request):
- liblaas_url = os.environ.get("LIBLAAS_BASE_URL") + "template/list/" + str(request.user)
+ liblaas_url = os.environ.get("LIBLAAS_BASE_URL") + "template/list/" + UserProfile.objects.get(user=request.user).ipa_username
print("api call to " + liblaas_url)
return requests.get(liblaas_url)
@@ -553,4 +563,86 @@ def liblaas_end_booking(aggregateId):
return response
except:
print("failed to end booking")
- return HttpResponse(status=500) \ No newline at end of file
+ return HttpResponse(status=500)
+
+def ipa_create_account(request):
+ # Called when no username was found
+ # Only allow if user does not have a linked ipa account
+ profile = UserProfile.objects.get(user=request.user)
+ if (profile.ipa_username):
+ return HttpResponse(status=401)
+
+ post_data = request.POST
+ user = {
+ 'uid': str(request.user),
+ 'givenname': post_data['first_name'],
+ 'sn': post_data['last_name'],
+ 'cn': post_data['first_name'] + " " + post_data['last_name'],
+ 'mail': post_data['email'],
+ 'ou': post_data['company'],
+ 'random': True
+ }
+
+ try:
+ response = requests.post(liblaas_base_url + "user/create", data=json.dumps(user), headers={'Content-Type': 'application/json'})
+ profile.ipa_username = user['uid']
+ print("Saving ipa username", profile.ipa_username)
+ profile.save()
+ return redirect("dashboard:index")
+ except Exception as e:
+ print(e)
+ return redirect("dashboard:index")
+
+def ipa_confirm_account(request):
+ # Called when username was found and email matches
+ profile = UserProfile.objects.get(user=request.user)
+ if (profile.ipa_username):
+ return HttpResponse(status=401)
+
+ profile.ipa_username = str(request.user)
+ print("Saving ipa username", profile.ipa_username)
+ profile.save()
+ return redirect("dashboard:index")
+
+def ipa_conflict_account(request):
+ # Called when username was found but emails do not match
+ # Need to ask user to input alternate username
+ # To verify username is not taken, call query_username and accept if returns None
+ print("ipa conflict account")
+ profile = UserProfile.objects.get(user=request.user)
+ print("profile is", profile)
+ if (profile.ipa_username):
+ return HttpResponse(status=401)
+
+ post_data = request.POST
+ user = {
+ 'uid': post_data['ipa_username'],
+ 'givenname': post_data['first_name'],
+ 'sn': post_data['last_name'],
+ 'cn': post_data['first_name'] + " " + post_data['last_name'],
+ 'mail': post_data['email'],
+ 'ou': post_data['company'],
+ 'random': True,
+ }
+
+ try:
+ response = requests.post(liblaas_base_url + "user/create", data=json.dumps(user), headers={'Content-Type': 'application/json'})
+ profile.ipa_username = user['uid']
+ print("Saving ipa username", profile.ipa_username)
+ profile.save()
+ return redirect("dashboard:index")
+ except Exception as e:
+ print(e)
+ return redirect("dashboard:index")
+
+def ipa_set_company_from_workflow(request):
+ profile = UserProfile.objects.get(user=request.user)
+ ipa_set_company(profile, request.POST["company"])
+ return redirect("workflow:book_a_pod")
+
+def ipa_add_ssh_from_workflow(request):
+ profile = UserProfile.objects.get(user=request.user)
+ key_as_list = []
+ key_as_list.append(request.POST["ssh_public_key"])
+ ipa_set_ssh(profile, key_as_list)
+ return redirect("workflow:book_a_pod")