def create_app(request): if request.method != "POST": response = PrettyJsonResponse({ "success": False, "error": "Request is not of method POST" }) response.status_code = 400 return response try: name = request.POST["name"] user_id = request.session["user_id"] except (KeyError, AttributeError): response = PrettyJsonResponse({ "success": False, "message": "Request does not have name or user." }) response.status_code = 400 return response user = get_user_by_id(user_id) new_app = App(name=name, user=user) new_app.save() keen_add_event.delay("App created", { "appid": new_app.id, "name": new_app.name, "userid": user.id }) s = Scopes() return PrettyJsonResponse({ "success": True, "message": "App sucessfully created", "app": { "name": new_app.name, "id": new_app.id, "token": new_app.api_token, "created": new_app.created, "updated": new_app.last_updated, "oauth": { "client_id": new_app.client_id, "client_secret": new_app.client_secret, "callback_url": new_app.callback_url, "scopes": s.get_all_scopes() }, "webhook": { "verification_secret": new_app.webhook.verification_secret, } } })
def regenerate_app_token(request): if request.method != "POST": response = PrettyJsonResponse({ "success": False, "error": "Request is not of method POST" }) response.status_code = 400 return response try: app_id = request.POST["app_id"] user_id = request.session["user_id"] except (KeyError, AttributeError): response = PrettyJsonResponse({ "success": False, "message": "Request does not have an app_id." }) response.status_code = 400 return response user = get_user_by_id(user_id) apps = App.objects.filter(id=app_id, user=user) if len(apps) == 0: response = PrettyJsonResponse({ "success": False, "message": "App does not exist." }) response.status_code = 400 return response else: app = apps[0] app.regenerate_token() new_api_token = app.api_token keen_add_event.delay("App token regenerated", { "appid": app.id, "userid": user.id }) return PrettyJsonResponse({ "success": True, "message": "App token sucessfully regenerated.", "app": { "id": app.id, "token": new_api_token, "date": app.last_updated } })
def rename_app(request): if request.method != "POST": response = PrettyJsonResponse({ "success": False, "error": "Request is not of method POST" }) response.status_code = 400 return response try: app_id = request.POST["app_id"] new_name = request.POST["new_name"] user_id = request.session["user_id"] except (KeyError, AttributeError): response = PrettyJsonResponse({ "success": False, "message": "Request does not have app_id/new_name" }) response.status_code = 400 return response user = get_user_by_id(user_id) apps = App.objects.filter(id=app_id, user=user, deleted=False) if len(apps) == 0: response = PrettyJsonResponse({ "success": False, "message": "App does not exist." }) response.status_code = 400 return response else: app = apps[0] app.name = new_name app.save() keen_add_event.delay("App renamed", { "appid": app.id, "new_name": app.name, "userid": user.id }) return PrettyJsonResponse({ "success": True, "message": "App sucessfully renamed.", "date": app.last_updated })
def myapps_shibboleth_callback(request): # should auth user login or signup # then redirect to my apps homepage eppn = request.META['HTTP_EPPN'] groups = request.META['HTTP_UCLINTRANETGROUPS'] cn = request.META['HTTP_CN'] department = request.META['HTTP_DEPARTMENT'] given_name = request.META['HTTP_GIVENNAME'] display_name = request.META['HTTP_DISPLAYNAME'] employee_id = request.META['HTTP_EMPLOYEEID'] try: user = User.objects.get(email=eppn) except ObjectDoesNotExist: # create a new user new_user = User(email=eppn, full_name=display_name, given_name=given_name, department=department, cn=cn, raw_intranet_groups=groups, employee_id=employee_id) new_user.save() add_user_to_mailing_list_task.delay(new_user.email, new_user.full_name) request.session["user_id"] = new_user.id keen_add_event.delay("signup", { "id": new_user.id, "email": eppn, "name": display_name }) else: # user exists already, update values request.session["user_id"] = user.id user.full_name = display_name user.given_name = given_name user.department = department user.raw_intranet_groups = groups user.employee_id = employee_id user.save() keen_add_event.delay("User data updated", { "id": user.id, "email": eppn, "name": display_name }) return redirect("/oauth/myapps")
def delete_app(request): if request.method != "POST": response = PrettyJsonResponse({ "success": False, "error": "Request is not of method POST" }) response.status_code = 400 return response try: app_id = request.POST["app_id"] user_id = request.session["user_id"] except KeyError: response = PrettyJsonResponse({ "success": False, "message": "Request does not have app_id." }) response.status_code = 400 return response user = get_user_by_id(user_id) apps = App.objects.filter(id=app_id, user=user) if len(apps) == 0: response = PrettyJsonResponse({ "success": False, "message": "App does not exist." }) response.status_code = 400 return response else: app = apps[0] app.deleted = True app.save() keen_add_event.delay("App deleted", { "appid": app_id, "userid": user.id }) return PrettyJsonResponse({ "success": True, "message": "App sucessfully deleted.", })
def log_api_call(request, token, token_type): service = request.path.split("/")[1] method = request.path.split("/")[2] headers = request.META version_headers = {} regex = re.compile("^HTTP_UCLAPI_.*_VERSION$") for header in headers: if regex.match(header): version_headers[header] = headers[header] queryparams = dict(request.GET) if token_type in {"general", "oauth"}: parameters = { "userid": token.user.id, "email": token.user.email, "name": token.user.given_name, "service": service, "method": method, "version-headers": version_headers, "queryparams": queryparams, "temp_token": False, "token_type": token_type } elif token_type == "general-temp": parameters = { "service": service, "method": method, "version-headers": version_headers, "queryparams": queryparams, "temp_token": True, "token_type": token_type } keen_add_event.delay("apicall", parameters)
def update_scopes(request): if request.method != "POST": response = PrettyJsonResponse({ "success": False, "error": "Request is not of method POST" }) response.status_code = 400 return response try: app_id = request.POST["app_id"] except KeyError: response = PrettyJsonResponse({ "success": False, "message": "Request does not have an app_id." }) response.status_code = 400 return response try: user_id = request.session["user_id"] except (KeyError, AttributeError): response = PrettyJsonResponse({ "success": False, "message": "User ID not set in session. Please log in again." }) response.status_code = 400 return response try: scopes_json = request.POST["scopes"] except KeyError: response = PrettyJsonResponse({ "success": False, "message": "No scopes data attached." }) response.status_code = 400 return response try: scopes = json.loads(scopes_json) except ValueError: response = PrettyJsonResponse({ "success": False, "message": "Invalid scope data that could not be parsed." }) response.status_code = 400 return response user = get_user_by_id(user_id) apps = App.objects.filter(id=app_id, user=user) if len(apps) == 0: response = PrettyJsonResponse({ "success": False, "message": "App does not exist." }) response.status_code = 400 return response else: app = apps[0] current = app.scope.scope_number s = Scopes() try: for scope in scopes: if "checked" in scope and scope["checked"]: current = s.add_scope(current, scope["name"]) else: current = s.remove_scope(current, scope["name"]) app.scope.scope_number = current app.scope.save() app.save() except (KeyError, ValueError, TypeError): response = PrettyJsonResponse({ "success": False, "message": "Invalid scope data that could not be iterated." }) response.status_code = 400 return response keen_add_event.delay("App scopes changed", { "appid": app_id, "userid": user.id, "scopes": scopes }) return PrettyJsonResponse({ "success": True, "message": "Scope successfully changed.", })
def set_callback_url(request): if request.method != "POST": response = PrettyJsonResponse({ "success": False, "error": "Request is not of method POST" }) response.status_code = 400 return response try: app_id = request.POST["app_id"] except KeyError: response = PrettyJsonResponse({ "success": False, "message": "Request does not have an app_id." }) response.status_code = 400 return response try: user_id = request.session["user_id"] except (KeyError, AttributeError): response = PrettyJsonResponse({ "success": False, "message": "User ID not set in session. Please log in again." }) response.status_code = 400 return response try: new_callback_url = request.POST["callback_url"] except KeyError: response = PrettyJsonResponse({ "success": False, "message": "Request does not have a Callback URL." }) response.status_code = 400 return response url_not_safe_saved = is_url_unsafe(new_callback_url) if url_not_safe_saved: if url_not_safe_saved == NOT_HTTPS: message = "The requested callback URL does not "\ "start with 'https://'." elif url_not_safe_saved == NOT_VALID: message = "The requested callback URL is not valid." elif url_not_safe_saved == URL_BLACKLISTED: message = "The requested callback URL is forbidden." elif url_not_safe_saved == NOT_PUBLIC: message = "The requested callback URL is not publicly available." response = PrettyJsonResponse({"success": False, "message": message}) response.status_code = 400 return response user = get_user_by_id(user_id) apps = App.objects.filter(id=app_id, user=user) if len(apps) == 0: response = PrettyJsonResponse({ "success": False, "message": "App does not exist." }) response.status_code = 400 return response app = apps[0] app.callback_url = new_callback_url app.save() keen_add_event.delay("App callback URL changed", { "appid": app_id, "userid": user.id, "newcallbackurl": new_callback_url }) return PrettyJsonResponse({ "success": True, "message": "Callback URL successfully changed.", })
def shibcallback(request): # Callback from Shib login. Get ALL the meta! appdata_signed = request.GET.get("appdata", None) if not appdata_signed: response = PrettyJsonResponse({ "ok": False, "error": ("No signed app data returned from Shibboleth." " Please use the authorise endpoint.") }) response.status_code = 400 return response signer = TimestampSigner() try: # Expire our signed tokens after five minutes for added security appdata = signer.unsign(appdata_signed, max_age=300) except signing.SignatureExpired: response = PrettyJsonResponse({ "ok": False, "error": ("Login data has expired. Please attempt to log in " "again. If the issues persist please contact the " "UCL API Team to rectify this.") }) response.status_code = 400 return response except signing.BadSignature: response = PrettyJsonResponse({ "ok": False, "error": ("Bad signature. Please attempt to log in again. " "If the issues persist please contact the UCL API " "Team to rectify this.") }) response.status_code = 400 return response client_id = appdata[:33] state = appdata[33:] # We can trust this value because it was extracted from the signed data # string sent via Shibboleth app = App.objects.get(client_id=client_id) eppn = request.META['HTTP_EPPN'] groups = request.META['HTTP_UCLINTRANETGROUPS'] cn = request.META['HTTP_CN'] department = request.META['HTTP_DEPARTMENT'] given_name = request.META['HTTP_GIVENNAME'] display_name = request.META['HTTP_DISPLAYNAME'] employee_id = request.META['HTTP_EMPLOYEEID'] # If a user has never used the API before then we need to sign them up try: user = User.objects.get(email=eppn) except User.DoesNotExist: # create a new user user = User(email=eppn, full_name=display_name, given_name=given_name, department=department, cn=cn, raw_intranet_groups=groups, employee_id=employee_id) user.save() keen_add_event.delay("signup", { "id": user.id, "email": eppn, "name": display_name }) else: # User exists already, so update the values user = User.objects.get(email=eppn) user.full_name = display_name user.given_name = given_name user.department = department user.raw_intranet_groups = groups user.employee_id = employee_id user.save() keen_add_event.delay("User data updated", { "id": user.id, "email": eppn, "name": display_name }) # Log the user into the system using their User ID request.session["user_id"] = user.id signer = TimestampSigner() response_data = { "client_id": app.client_id, "state": state, "user_upi": user.employee_id } response_data_str = json.dumps(response_data, cls=DjangoJSONEncoder) response_data_signed = signer.sign(response_data_str) s = Scopes() page_data = { "app_name": app.name, "creator": app.user.full_name, "client_id": app.client_id, "state": state, "scopes": s.scope_dict(app.scope.scope_number), "user": { "full_name": user.full_name, "cn": user.cn, "email": user.email, "department": user.department, "upi": user.employee_id }, "signed_data": response_data_signed } initial_data = json.dumps(page_data, cls=DjangoJSONEncoder) return render(request, 'permissions.html', {'initial_data': initial_data})
def edit_webhook(request): if request.method != "POST": response = PrettyJsonResponse({ "ok": False, "message": ("Request is not of method POST") }) response.status_code = 400 return response try: app_id = request.POST["app_id"] url = request.POST["url"] siteid = request.POST["siteid"] roomid = request.POST["roomid"] contact = request.POST["contact"] user_id = request.session["user_id"] except KeyError: response = PrettyJsonResponse({ "ok": False, "message": ("Request is missing parameters. Should have app_id" ", url, siteid, roomid, contact" " as well as a sessionid cookie") }) response.status_code = 400 return response if not user_owns_app(user_id, app_id): response = PrettyJsonResponse({ "ok": False, "message": ("App does not exist or user is lacking permission.") }) response.status_code = 400 return response app = App.objects.get(id=app_id) webhook = app.webhook if url != webhook.url: if is_url_unsafe(url): response = PrettyJsonResponse({ "ok": False, "message": ("Invalid URL") }) response.status_code = 400 return response if not verify_ownership(url, generate_secret(), webhook.verification_secret): response = PrettyJsonResponse({ "ok": False, "message": ("Ownership of webhook can't be verified." "Make sure to follow the documentation: " "https://uclapi.com/docs#webhook/challenge-event") }) response.status_code = 400 return response webhook.url = url webhook.save() webhook.siteid = siteid webhook.roomid = roomid webhook.contact = contact webhook.enabled = True webhook.save() keen_add_event.delay( "Webhook edited", { "appid": app.id, "userid": user_id, "url": url, "siteid": siteid, "roomid": roomid, "contact": contact, }) return PrettyJsonResponse({ "ok": True, "message": "Webhook sucessfully changed.", "url": webhook.url, "roomid": webhook.roomid, "siteid": webhook.siteid, "contact": webhook.contact })
def set_callback_url(request): if request.method != "POST": response = PrettyJsonResponse({ "success": False, "error": "Request is not of method POST" }) response.status_code = 400 return response try: app_id = request.POST["app_id"] except KeyError: response = PrettyJsonResponse({ "success": False, "message": "Request does not have an app_id." }) response.status_code = 400 return response try: user_id = request.session["user_id"] except (KeyError, AttributeError): response = PrettyJsonResponse({ "success": False, "message": "User ID not set in session. Please log in again." }) response.status_code = 400 return response try: new_callback_url = request.POST["callback_url"] except KeyError: response = PrettyJsonResponse({ "success": False, "message": "Request does not have a Callback URL." }) response.status_code = 400 return response if not is_url_safe(new_callback_url): response = PrettyJsonResponse({ "success": False, "message": ("The requested callback URL" " is not valid.") }) response.status_code = 400 return response user = get_user_by_id(user_id) apps = App.objects.filter(id=app_id, user=user) if len(apps) == 0: response = PrettyJsonResponse({ "success": False, "message": "App does not exist." }) response.status_code = 400 return response app = apps[0] app.callback_url = new_callback_url app.save() keen_add_event.delay("App callback URL changed", { "appid": app_id, "userid": user.id, "newcallbackurl": new_callback_url }) return PrettyJsonResponse({ "success": True, "message": "Callback URL successfully changed.", })