diff --git a/back/Pipfile b/back/Pipfile index b2fa8cc..54943ec 100644 --- a/back/Pipfile +++ b/back/Pipfile @@ -9,8 +9,8 @@ verify_ssl = true django = "==2.1.5" django-cors-headers = "==2.4.0" djangorestframework = "==3.8.2" - gunicorn = "==19.6.0" +django-rest-auth = "==0.9.3" [requires] python_version = "3.5" diff --git a/back/Pipfile.lock b/back/Pipfile.lock index 4527d5b..e493330 100644 --- a/back/Pipfile.lock +++ b/back/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "b5222b4256c8f09a9b1b1d380285fa65c443f84d28dc03450684fca84b38a26b" + "sha256": "d3bf402a934e168cbdc04022effcdb9ff8d4fde5b83d79bb388ad2a4c547894a" }, "pipfile-spec": 6, "requires": { @@ -32,6 +32,13 @@ "index": "pypi", "version": "==2.4.0" }, + "django-rest-auth": { + "hashes": [ + "sha256:ad155a0ed1061b32e3e46c9b25686e397644fd6acfd35d5c03bc6b9d2fc6c82a" + ], + "index": "pypi", + "version": "==0.9.3" + }, "djangorestframework": { "hashes": [ "sha256:b6714c3e4b0f8d524f193c91ecf5f5450092c2145439ac2769711f7eba89a9d9", @@ -54,6 +61,13 @@ "sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c" ], "version": "==2018.9" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" } }, "develop": {} diff --git a/back/backend/__init__.py b/back/backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/backend/urls.py b/back/backend/urls.py index e0b18a6..83f7279 100644 --- a/back/backend/urls.py +++ b/back/backend/urls.py @@ -9,9 +9,6 @@ urlpatterns = [ path('reports', views.reports), path('report/', views.report_detail), path('report//section/', views.section), - path('account', views.account), - path('account/login', views.account_login), - path('account/logout', views.account_logout), ] -urlpatterns = format_suffix_patterns(urlpatterns) \ No newline at end of file +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/back/backend/views.py b/back/backend/views.py index 06ba04e..4d1689b 100644 --- a/back/backend/views.py +++ b/back/backend/views.py @@ -174,24 +174,3 @@ def section(request, report_pk, section_pk): } return JsonResponse(data) - -@api_view(['POST']) -def account(request): - ''' - Create a new user account - ''' - return JsonResponse({"message": "Account creation successful."}) - -@api_view(['POST']) -def account_login(request): - ''' - Log in to a user account - ''' - return JsonResponse({"message": "Successfully logged in."}) - -@api_view(['DELETE']) -def account_logout(request): - ''' - Log out from a user account - ''' - return JsonResponse({"message": "User logged out."}) diff --git a/back/reimbursinator/custom_auth.py b/back/reimbursinator/custom_auth.py new file mode 100644 index 0000000..9097e26 --- /dev/null +++ b/back/reimbursinator/custom_auth.py @@ -0,0 +1,9 @@ +from rest_framework.authentication import TokenAuthentication + +class BearerAuthentication(TokenAuthentication): + """ + This class simply changes the expected token keyword to 'Bearer' + from the Django rest authentication default 'Token'. This allows + applications like Postman to work with token authentication. + """ + keyword = "Bearer" diff --git a/back/reimbursinator/settings.py b/back/reimbursinator/settings.py index e274dc5..6fb5556 100644 --- a/back/reimbursinator/settings.py +++ b/back/reimbursinator/settings.py @@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/2.1/ref/settings/ """ import os +#from reimbursinator.custom_auth import BearerAuthentication # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -40,6 +41,8 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', # 3rd party 'rest_framework', + 'rest_framework.authtoken', + 'rest_auth', 'corsheaders', # local 'users', @@ -48,8 +51,12 @@ INSTALLED_APPS = [ REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ - 'rest_framework.permissions.AllowAny', - ] + 'rest_framework.permissions.IsAuthenticated', + ], + 'DEFAULT_AUTHENTICATION_CLASSES': [ + 'reimbursinator.custom_auth.BearerAuthentication', + 'rest_framework.authentication.SessionAuthentication', + ], } MIDDLEWARE = [ diff --git a/back/reimbursinator/urls.py b/back/reimbursinator/urls.py index 53beb58..62397e8 100644 --- a/back/reimbursinator/urls.py +++ b/back/reimbursinator/urls.py @@ -12,4 +12,6 @@ from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('api/v1/', include("backend.urls")), -] \ No newline at end of file + path('api/v1/account/', include('rest_auth.urls')), + path('api-auth/', include('rest_framework.urls')), +] diff --git a/back/users/__init__.py b/back/users/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/front/static/edit_report.html b/front/static/edit_report.html index 88004b4..80d39df 100644 --- a/front/static/edit_report.html +++ b/front/static/edit_report.html @@ -62,6 +62,25 @@ + diff --git a/front/static/js/viewHistory.js b/front/static/js/viewHistory.js index c9b2483..7e7a88c 100644 --- a/front/static/js/viewHistory.js +++ b/front/static/js/viewHistory.js @@ -1,30 +1,6 @@ -// Hack to change endpoint url for each OS +// Hack to change endpoint url function getEndpointDomain() { - let OSName; - let domain; - - if (navigator.appVersion.indexOf("Win") !== -1) - OSName = "Windows"; - else if (navigator.appVersion.indexOf("Mac") !== -1) - OSName = "MacOS"; - else if (navigator.appVersion.indexOf("X11") !== -1) - OSName = "UNIX"; - else if (navigator.appVersion.indexOf("Linux") !== -1) - OSName = "Linux"; - else - OSName = "Unknown OS"; - - console.log("Detected operating system: " + OSName); - - if (OSName === "Windows") { - domain = "https://192.168.99.100:8444/"; - } else if (OSName === "MacOS" && navigator.userAgent.includes("Firefox")) { - domain = "https://192.168.99.100:8444/"; // That's Shuaiyi - } else { - domain = "https://localhost:8444/"; // Jack, Preston - } - - return domain; + return "https://" + window.location.hostname + ":8444/"; } // Make a GET request to url and pass response to callback function @@ -145,13 +121,25 @@ function createCollapsibleCard(key, sectionTitle) { function createCollapsibleCardBody(key, form, sectionDescription, sectionCompleted) { // Create wrapper div const div = document.createElement("div"); - sectionCompleted ? div.classList.add("collapse") : div.classList.add("collapse", "show"); + const sectionAlert = document.createElement("div"); + + if (sectionCompleted) { + div.classList.add("collapse"); + sectionAlert.classList.add("alert", "alert-success"); + sectionAlert.innerHTML = "This section is complete"; + } else { + div.classList.add("collapse", "show"); + sectionAlert.classList.add("alert", "alert-danger"); + sectionAlert.innerHTML = "This section is not complete"; + } + div.setAttribute("data-parent", "#editReportAccordion"); div.id = "collapse" + key; // Create card body. Append form to body, body to wrapper div const cardBody = document.createElement("div"); cardBody.classList.add("card-body"); + cardBody.appendChild(sectionAlert); cardBody.insertAdjacentHTML("beforeend", sectionDescription); cardBody.appendChild(form); div.appendChild(cardBody); @@ -160,20 +148,17 @@ function createCollapsibleCardBody(key, form, sectionDescription, sectionComplet } function createEditReportForm(parsedData) { - const col = document.querySelector(".col-sm-8"); - const fragment = document.createDocumentFragment(); + const modalBody = document.querySelector(".modal-body"); + const modalLabel = document.querySelector("#editReportModalLabel"); - while (col.firstChild) { - col.removeChild(col.firstChild) + while (modalBody.firstChild) { + modalBody.removeChild(modalBody.firstChild); } // Add report title and date const reportTitle = parsedData.title; const dateCreated = new Date(parsedData.date_created).toLocaleDateString("en-US"); - const h3 = document.createElement("h3"); - h3.innerHTML = reportTitle + " " + dateCreated; - h3.classList.add("text-center"); - fragment.appendChild(h3); + modalLabel.innerHTML = reportTitle + " " + dateCreated; // Create accordion const accordion = document.createElement("div"); @@ -218,15 +203,7 @@ function createEditReportForm(parsedData) { accordion.appendChild(collapsibleCard); } - // Add submit button to accordion - let submitButton = document.createElement("button"); - submitButton.innerHTML = "Submit Report"; - submitButton.type = "submit"; - submitButton.classList.add("btn", "btn-primary", "btn-lg", "btn-block"); // TODO: add eventListener - accordion.appendChild(submitButton); - - fragment.appendChild(accordion) - col.appendChild(fragment); + modalBody.appendChild(accordion); } function displayListOfReports(parsedData) { @@ -268,7 +245,8 @@ function displayListOfReports(parsedData) { dateSubmitted = "TBD"; actionButton.classList.add("btn-primary", "edit-report-button"); // Add event listener class actionButton.innerHTML = "Edit"; - //actionButton.addEventListener("click", openEditReportForm); + actionButton.setAttribute("data-toggle", "modal"); + actionButton.setAttribute("data-target", "#editReportModal"); } else { // View button dateSubmitted = new Date(reports[i].date_submitted).toLocaleDateString("en-US");