diff options
author | Justin Choquette <jchoquette@iol.unh.edu> | 2023-08-07 14:10:19 -0400 |
---|---|---|
committer | Justin Choquette <jchoquette@iol.unh.edu> | 2023-08-07 14:16:04 -0400 |
commit | a6168306c08e8d5b207b9acc48869180d194ff01 (patch) | |
tree | 51ffcafac4ae0b5fd4da363d9bf839e8ad3fc286 /src/api | |
parent | a09db9f287a02873c0226759f8ea444bb304cd59 (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.py | 10 | ||||
-rw-r--r-- | src/api/utils.py | 122 | ||||
-rw-r--r-- | src/api/views.py | 118 |
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") |