Skip to content

Commit

Permalink
Merge pull request #172 from CogStack/CU-862j2cj0p-password-reset
Browse files Browse the repository at this point in the history
CU862j2cj0p Password Reset via Email
  • Loading branch information
tomolopolis authored Jan 29, 2024
2 parents 115b6a9 + da55c53 commit 74fe4e9
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 4 deletions.
14 changes: 14 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ On MAC: https://docs.docker.com/docker-for-mac/#memory

On Windows: https://docs.docker.com/docker-for-windows/#resources

### (Optional) SMTP Setup

For password resets and other emailing services email environment variables are required to be set up.

Personal email accounts can be set up by users to do this, or you can contact someone in CogStack for a cogstack no email credentials.

The environment variables required are listed in [Environment Variables.](#(optional)-environment-variables)

Environment Variables are located in envs/env or envs/env-prod, when those are set webapp/frontend/.env must change "VITE_APP_EMAIL" to 1.

### (Optional) Environment Variables
Environment variables are used to configure the app:

Expand All @@ -48,6 +58,10 @@ Environment variables are used to configure the app:
|MEDCAT_CONFIG_FILE|MedCAT config file as described [here](https://github.com/CogStack/MedCAT/blob/master/medcat/config.py)|
|BEHIND_RP| If you're running MedCATtrainer, use 1, otherwise this defaults to 0 i.e. False|
|MCTRAINER_PORT|The port to run the trainer app on|
|EMAIL_USER|Email address which will be used to send users emails regarding password resets|
|EMAIL_PASS|The password or authentication key which will be used with the email address|
|EMAIL_HOST|The hostname of the SMTP server which will be used to send email (default: mail.cogstack.org)|
|EMAIL_PORT|The port that the SMTP server is listening to, common numbers are 25, 465, 587 (default: 465)|

Set these and re-run the docker-compose file.

Expand Down
6 changes: 6 additions & 0 deletions envs/env
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,9 @@ RESUBMIT_ALL_ON_STARTUP=1

# Front end env vars
LOAD_NUM_DOC_PAGES=10

# SMTP email settings - when settings are configured go to webapp/frontend/.env and set VITE_APP_EMAIL to 1
[email protected]
EMAIL_PASS="to be changed"
EMAIL_HOST=mail.cogstack.org
EMAIL_PORT=465
5 changes: 5 additions & 0 deletions envs/env-prod
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,8 @@ RESUBMIT_ALL_ON_STARTUP=1
# Front end env vars
LOAD_NUM_DOC_PAGES=10

# SMTP EMAIL SETTINGS
[email protected]
EMAIL_PASS="to be changed"
EMAIL_HOST=mail.cogstack.org
EMAIL_PORT=465
13 changes: 13 additions & 0 deletions webapp/api/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
from django.shortcuts import render
from django.utils import timezone
from django_filters import rest_framework as drf
from django.contrib.auth.views import PasswordResetView
from medcat.cdb import CDB
from medcat.utils.helpers import tkns_from_doc
from rest_framework import viewsets
from rest_framework.decorators import api_view
from rest_framework.response import Response
from smtplib import SMTPException

from core.settings import MEDIA_ROOT
from .admin import download_projects_with_text, download_projects_without_text, \
Expand Down Expand Up @@ -213,6 +215,17 @@ class OPCSCodeViewSet(viewsets.ModelViewSet):
filterset_class = OPCSCodeFilter
filterset_fields = ['code', 'id']

class ResetPasswordView(PasswordResetView):
email_template_name = 'password_reset_email.html'
subject_template_name = 'password_reset_subject.txt'
def post(self, request, *args, **kwargs):
try:
return super().post(request, *args, **kwargs)
except SMTPException:
return HttpResponseServerError('''SMTP settings are not configured correctly. <br>
Please visit https://medcattrainer.readthedocs.io for more information to resolve this. <br>
You can also ask a question at: https://discourse.cogstack.org/c/medcat/5''')


@api_view(http_method_names=['GET'])
def get_anno_tool_conf(_):
Expand Down
11 changes: 11 additions & 0 deletions webapp/api/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, "..", "frontend", "dist"),
os.path.join(BASE_DIR, "..", "templates", "registration")
],
'APP_DIRS': True,
'OPTIONS': {
Expand Down Expand Up @@ -192,3 +193,13 @@
SOLR_PORT = os.environ.get('CONCEPT_SEARCH_SERVICE_PORT', '8983')

SILENCED_SYSTEM_CHECKS = ['admin.E130']

# For testing only
# EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = os.environ.get('EMAIL_HOST')
EMAIL_PORT = os.environ.get('EMAIL_PORT')
EMAIL_HOST_USER = os.environ.get('EMAIL_USER')
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_PASS')
EMAIL_USE_TLS = True
6 changes: 5 additions & 1 deletion webapp/api/core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.contrib.auth import views as pw_views
from django.urls import path, include, re_path
from rest_framework.authtoken import views as auth_views
from rest_framework import routers
import api.views


router = routers.DefaultRouter()
router.register(r'users', api.views.UserViewSet)
router.register(r'entities', api.views.EntityViewSet)
Expand Down Expand Up @@ -70,5 +70,9 @@
path('api/concept-path/', api.views.cdb_concept_path),
path('api/generate-concept-filter-json/', api.views.generate_concept_filter_flat_json),
path('api/generate-concept-filter/', api.views.generate_concept_filter),
path('reset_password/', api.views.ResetPasswordView.as_view(), name ='reset_password'),
path('reset_password_sent/', pw_views.PasswordResetDoneView.as_view(), name ='password_reset_done'),
path('reset/<uidb64>/<token>', pw_views.PasswordResetConfirmView.as_view(), name ='password_reset_confirm'),
path('reset_password_complete/', pw_views.PasswordResetCompleteView.as_view(), name ='password_reset_complete'),
re_path('^.*$', api.views.index, name='index'), # Match everything else to home
]
1 change: 1 addition & 0 deletions webapp/frontend/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_APP_EMAIL=0 # Set to 1 when email settings are correctly configured
6 changes: 5 additions & 1 deletion webapp/frontend/src/components/common/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<input v-model="uname" class="form-control" id="uname">
<label>Password:</label>
<input v-model="password" class="form-control" type="password" id="password">
<a v-if="reset_pw" href="/reset_password/">Forgotten Password</a>
</form>
<span v-if="failed" class="text-danger">Username and/or password incorrect</span>
<span v-if="failedAdminStatusCheck" class="text-danger">Cannot determine admin status of username</span>
Expand Down Expand Up @@ -40,7 +41,8 @@ export default {
uname: '',
password: '',
failed: false,
failedAdminStatusCheck: false
failedAdminStatusCheck: false,
reset_pw: import.meta.env.VITE_APP_EMAIL === '1',
}
},
methods: {
Expand Down Expand Up @@ -102,4 +104,6 @@ export default {
width: 400px;
}
}
</style>
10 changes: 8 additions & 2 deletions webapp/frontend/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { createVuePlugin as vue } from "vite-plugin-vue2";
import { defineConfig } from 'vite'
import { defineConfig, loadEnv } from 'vite'

// https://vitejs.dev/config/
const path = require("path");
export default defineConfig({
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '')
return {
define: {
__APP_ENV__: JSON.stringify(env.APP_ENV),
},
plugins: [vue()],
resolve: {
alias: {
Expand Down Expand Up @@ -39,4 +44,5 @@ export default defineConfig({
}
}
}
}
})
17 changes: 17 additions & 0 deletions webapp/templates/registration/password_reset_email.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{% autoescape off %}
You’re receiving this email because you requested a password reset for your user account
with MedCATTrainer at {{ site_name }}.

Please go to the following page and choose a new password:

{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}

Your username, in case you've forgotten: {{ user.username }}.

If clicking the link above doesn't work, please copy and paste the URL in a new browser
window instead.

Sincerely,

The Cogstack Team
{% endautoescape %}
1 change: 1 addition & 0 deletions webapp/templates/registration/password_reset_subject.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
MedCATtrainer: Password Reset

0 comments on commit 74fe4e9

Please sign in to comment.