From cdc197458cb3de5d3c472a818e56b7ae294c70ab Mon Sep 17 00:00:00 2001 From: Timothy Miller Date: Sat, 2 Apr 2022 13:01:55 -0400 Subject: [PATCH] reapply session auth changes --- api/requirements/dev.txt | 9 + api/requirements/prod.txt | 9 + api/woeip/apps/core/forms.py | 2 + api/woeip/settings.py | 33 +- api/woeip/urls.py | 5 +- nginx-proxy/Dockerfile | 3 +- web/package-lock.json | 116 ++++++- web/package.json | 4 +- web/src/App.js | 27 +- web/src/App.test.js | 42 +++ web/src/api.util.js | 25 ++ web/src/api.util.test.js | 6 + web/src/components/auth/boundary.js | 27 ++ web/src/components/auth/boundary.test.js | 50 +++ web/src/components/auth/login.css | 7 + web/src/components/auth/login.js | 65 ++++ web/src/components/auth/login.test.js | 135 ++++++++ web/src/components/auth/logout.js | 34 ++ web/src/components/auth/logout.test.js | 102 ++++++ web/src/components/auth/register.css | 7 + web/src/components/auth/register.js | 87 +++++ web/src/components/auth/register.test.js | 159 +++++++++ web/src/components/auth/tokenContext.js | 7 + web/src/components/auth/utils.js | 89 ++++++ web/src/components/auth/utils.test.js | 132 ++++++++ web/src/components/home/index.js | 9 +- web/src/components/home/index.test.js | 20 +- web/src/components/home/welcome.css | 3 + web/src/components/home/welcome.js | 15 + web/src/components/map/box.js | 3 +- web/src/components/nav/bar.js | 19 +- web/src/components/nav/bar.test.js | 31 +- web/src/components/nav/index.test.js | 43 ++- web/src/components/nav/switch.js | 34 +- web/src/components/ui/index.js | 1 + web/src/components/upload/confirm.js | 38 +-- web/src/components/upload/drop.js | 2 +- web/src/components/upload/index.js | 57 ++-- web/src/components/upload/utils.js | 217 ++++++++++++- web/src/components/upload/utils.test.js | 390 ++++++++++++++++++++++- 40 files changed, 1965 insertions(+), 99 deletions(-) create mode 100644 api/woeip/apps/core/forms.py create mode 100644 web/src/components/auth/boundary.js create mode 100644 web/src/components/auth/boundary.test.js create mode 100644 web/src/components/auth/login.css create mode 100644 web/src/components/auth/login.js create mode 100644 web/src/components/auth/login.test.js create mode 100644 web/src/components/auth/logout.js create mode 100644 web/src/components/auth/logout.test.js create mode 100644 web/src/components/auth/register.css create mode 100644 web/src/components/auth/register.js create mode 100644 web/src/components/auth/register.test.js create mode 100644 web/src/components/auth/tokenContext.js create mode 100644 web/src/components/auth/utils.js create mode 100644 web/src/components/auth/utils.test.js create mode 100644 web/src/components/home/welcome.css create mode 100644 web/src/components/home/welcome.js diff --git a/api/requirements/dev.txt b/api/requirements/dev.txt index b7b9ec3d..ca12dccc 100644 --- a/api/requirements/dev.txt +++ b/api/requirements/dev.txt @@ -10,8 +10,13 @@ charset-normalizer==2.0.12 coreapi==2.3.3 coreschema==0.0.4 coverage==6.3.2 +cryptography==36.0.1 +defusedxml==0.7.1 +dj-rest-auth==2.2.3 Django==3.2.12 +django-allauth==0.49.0 django-boto==0.3.12 +django-cors-headers==3.11.0 django-debug-toolbar==3.2.4 django-environ==0.8.1 django-extensions==3.1.5 @@ -34,19 +39,23 @@ MarkupSafe==2.1.0 mypy==0.931 mypy-extensions==0.4.3 numpy==1.22.2 +oauthlib==3.2.0 packaging==21.3 pandas==1.4.1 pluggy==1.0.0 psycopg2-binary==2.9.3 py==1.11.0 pycparser==2.21 +PyJWT==2.3.0 pynmea2==1.18.0 pyparsing==3.0.7 pytest==7.0.1 pytest-django==4.5.2 python-dateutil==2.8.2 +python3-openid==3.2.0 pytz==2021.3 requests==2.27.1 +requests-oauthlib==1.3.1 ruamel.yaml==0.17.21 ruamel.yaml.clib==0.2.6 six==1.16.0 diff --git a/api/requirements/prod.txt b/api/requirements/prod.txt index 626609db..bd061c89 100644 --- a/api/requirements/prod.txt +++ b/api/requirements/prod.txt @@ -7,8 +7,13 @@ cffi==1.15.0 charset-normalizer==2.0.12 coreapi==2.3.3 coreschema==0.0.4 +cryptography==36.0.1 +defusedxml==0.7.1 +dj-rest-auth==2.2.3 Django==3.2.12 +django-allauth==0.49.0 django-boto==0.3.12 +django-cors-headers==3.11.0 django-environ==0.8.1 django-extensions==3.1.5 django-model-utils==4.2.0 @@ -24,15 +29,19 @@ itypes==1.2.0 Jinja2==3.0.3 MarkupSafe==2.1.0 numpy==1.22.2 +oauthlib==3.2.0 packaging==21.3 pandas==1.4.1 psycopg2-binary==2.9.3 pycparser==2.21 +PyJWT==2.3.0 pynmea2==1.18.0 pyparsing==3.0.7 python-dateutil==2.8.2 +python3-openid==3.2.0 pytz==2021.3 requests==2.27.1 +requests-oauthlib==1.3.1 ruamel.yaml==0.17.21 ruamel.yaml.clib==0.2.6 six==1.16.0 diff --git a/api/woeip/apps/core/forms.py b/api/woeip/apps/core/forms.py new file mode 100644 index 00000000..0c9508b4 --- /dev/null +++ b/api/woeip/apps/core/forms.py @@ -0,0 +1,2 @@ +from django import forms +from django.contrib.auth.forms import UserChangeForm, UserCreationForm diff --git a/api/woeip/settings.py b/api/woeip/settings.py index 73cda9a1..5bdb66dd 100644 --- a/api/woeip/settings.py +++ b/api/woeip/settings.py @@ -30,7 +30,7 @@ "django.contrib.gis", ] -THIRD_PARTY_APPS = ["django_extensions", "rest_framework", "storages", "drf_yasg"] +THIRD_PARTY_APPS = ["corsheaders", "django_extensions", "rest_framework", 'rest_framework.authtoken', "dj_rest_auth", "allauth", 'allauth.account', 'allauth.socialaccount', 'dj_rest_auth.registration', "storages", "drf_yasg"] LOCAL_APPS = ["woeip.apps.core", "woeip.apps.air_quality"] @@ -57,6 +57,7 @@ MIDDLEWARE = [ + "corsheaders.middleware.CorsMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", @@ -67,6 +68,33 @@ "django.contrib.flatpages.middleware.FlatpageFallbackMiddleware", ] +REST_FRAMEWORK = { + 'DATETIME_FORMAT': "%m/%d/%Y %I:%M%P", + 'DEFAULT_AUTHENTICATION_CLASSES': [ + 'rest_framework.authentication.TokenAuthentication', + ], + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.IsAuthenticatedOrReadOnly', + ] +} + +AUTHENTICATION_BACKENDS = ( + "django.contrib.auth.backends.ModelBackend", + "allauth.account.auth_backends.AuthenticationBackend", +) + +ACCOUNT_EMAIL_REQUIRED = True +ACCOUNT_UNIQUE_EMAIL = True +ACCOUNT_USERNAME_REQUIRED = False +ACCOUNT_AUTHENTICATION_METHOD = 'email' + +CORS_ALLOWED_ORIGINS = [ + "http://lvh.me", + "https://woaq.org", + "http://localhost:3000", + "http://127.0.0.1:3000", +] + ROOT_URLCONF = "woeip.urls" TEMPLATES = [ @@ -192,8 +220,5 @@ def generate_file_handler(filename): }, } -LOGIN_REDIRECT_URL = "upload" -LOGOUT_REDIRECT_URL = "login" - EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend" EMAIL_FILE_PATH = str(project_root.path("sent_emails")) diff --git a/api/woeip/urls.py b/api/woeip/urls.py index b1643cd3..1aab468e 100644 --- a/api/woeip/urls.py +++ b/api/woeip/urls.py @@ -18,13 +18,14 @@ router.register(r"pollutant_values", views.PollutantValueViewSet) router.register(r"sensors", views.SensorViewSet) router.register(r"timegeo", views.TimeGeoViewSet) -router.register(r"users", core_views.UserViewSet) +# router.register(r"users", core_views.UserViewSet) # Disable to prevent personal information leaking urlpatterns = [ path("", include(router.urls)), path("admin/", admin.site.urls), - path("api-auth/", include("rest_framework.urls", namespace="rest_framework")), + path("auth/", include("dj_rest_auth.urls")), + path("auth/register/", include('dj_rest_auth.registration.urls')), ] urlpatterns += swagger_urlpatterns diff --git a/nginx-proxy/Dockerfile b/nginx-proxy/Dockerfile index dbc2d10a..ef879baa 100644 --- a/nginx-proxy/Dockerfile +++ b/nginx-proxy/Dockerfile @@ -1,4 +1,3 @@ FROM jwilder/nginx-proxy ARG API_DOMAIN -COPY cors.conf /etc/nginx/vhost.d/$API_DOMAIN -COPY api_static_assets_routing /etc/nginx/vhost.d/${API_DOMAIN}_location \ No newline at end of file +COPY api_static_assets_routing /etc/nginx/vhost.d/${API_DOMAIN}_location diff --git a/web/package-lock.json b/web/package-lock.json index beeb8a66..22e2433d 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -26,7 +26,9 @@ "devDependencies": { "@testing-library/jest-dom": "^5.11.9", "@testing-library/react": "^11.2.5", - "@testing-library/user-event": "^12.7.1", + "@testing-library/user-event": "^12.8.3", + "@types/react": "^17.0.39", + "@types/react-router-dom": "^5.3.3", "@typescript-eslint/parser": "^4.15.1", "eslint-config-prettier": "^7.2.0", "eslint-plugin-prettier": "^3.3.1", @@ -3101,6 +3103,12 @@ "@types/node": "*" } }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "dev": true + }, "node_modules/@types/html-minifier-terser": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz", @@ -3196,11 +3204,49 @@ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==" }, + "node_modules/@types/prop-types": { + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", + "dev": true + }, "node_modules/@types/q": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" }, + "node_modules/@types/react": { + "version": "17.0.39", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz", + "integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-router": { + "version": "5.1.18", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.18.tgz", + "integrity": "sha512-YYknwy0D0iOwKQgz9v8nOzt2J6l4gouBmDnWqUUznltOTaon+r8US8ky8HvN0tXvc38U9m6z/t2RsVsnd1zM0g==", + "dev": true, + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "dev": true, + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, "node_modules/@types/resolve": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", @@ -3209,6 +3255,12 @@ "@types/node": "*" } }, + "node_modules/@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, "node_modules/@types/set-cookie-parser": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.2.tgz", @@ -6244,6 +6296,12 @@ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" }, + "node_modules/csstype": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", + "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==", + "dev": true + }, "node_modules/cyclist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", @@ -24086,6 +24144,12 @@ "@types/node": "*" } }, + "@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "dev": true + }, "@types/html-minifier-terser": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz", @@ -24181,11 +24245,49 @@ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.4.tgz", "integrity": "sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA==" }, + "@types/prop-types": { + "version": "15.7.4", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz", + "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==", + "dev": true + }, "@types/q": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" }, + "@types/react": { + "version": "17.0.39", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz", + "integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-router": { + "version": "5.1.18", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.18.tgz", + "integrity": "sha512-YYknwy0D0iOwKQgz9v8nOzt2J6l4gouBmDnWqUUznltOTaon+r8US8ky8HvN0tXvc38U9m6z/t2RsVsnd1zM0g==", + "dev": true, + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "dev": true, + "requires": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, "@types/resolve": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", @@ -24194,6 +24296,12 @@ "@types/node": "*" } }, + "@types/scheduler": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true + }, "@types/set-cookie-parser": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.2.tgz", @@ -26576,6 +26684,12 @@ } } }, + "csstype": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", + "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==", + "dev": true + }, "cyclist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", diff --git a/web/package.json b/web/package.json index 3c48c36e..3a3b6fcf 100644 --- a/web/package.json +++ b/web/package.json @@ -55,7 +55,9 @@ "devDependencies": { "@testing-library/jest-dom": "^5.11.9", "@testing-library/react": "^11.2.5", - "@testing-library/user-event": "^12.7.1", + "@testing-library/user-event": "^12.8.3", + "@types/react": "^17.0.39", + "@types/react-router-dom": "^5.3.3", "@typescript-eslint/parser": "^4.15.1", "eslint-config-prettier": "^7.2.0", "eslint-plugin-prettier": "^3.3.1", diff --git a/web/src/App.js b/web/src/App.js index 45b769ab..a4493216 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -1,3 +1,6 @@ +import { useState, useEffect } from "react"; +import { AuthTokenContext } from "./components/auth/tokenContext"; +import { getAuthTokenItem } from "./components/auth/utils"; import { Navigation } from "./components/nav"; import { Footer } from "./components/footer"; import { Container } from "./components/ui"; @@ -5,11 +8,25 @@ import "./App.css"; // All components for the application are organized here. export const App = () => { + const [authToken, setAuthToken] = useState(""); + const [tokenLoading, setTokenLoading] = useState(true); + /** + * Check local storage for auth token on initial application load. + */ + useEffect(() => { + setAuthToken(getAuthTokenItem()); + setTokenLoading(false); + }, []); + return ( - - {/* Components that can be mounted via the Router are nested in Navigation */} - -