Skip to content
This repository has been archived by the owner on Jan 4, 2023. It is now read-only.

Add JWT auth #3

Merged
merged 30 commits into from
Dec 15, 2020
Merged

Add JWT auth #3

merged 30 commits into from
Dec 15, 2020

Conversation

tevariou
Copy link
Contributor

@tevariou tevariou commented Nov 27, 2020

Fixes

Fixes #2 by @tevariou

Description

  • Add a login page
  • Replace basic authentication with JWT auth
  • Fix yarn timeout
  • Shared volume between api and nginx is now limited to django static files and the socket file
  • Change env variables management
  • The uwsgi server is run by a dedicated non root user

Technical details

  • The Patient model still has the code attribute that has a unique constraint. An UUID is autogenerated if the field is not filled by the client. It can be used as a study code to avoid subject duplicates.

@tevariou tevariou changed the title Tr/feat/token auth Add JWT auth Nov 27, 2020
api/.env Outdated
Comment on lines 1 to 6
DJANGO_SUPERUSER_PASSWORD=${DJANGO_SUPERUSER_PASSWORD:-password}
DJANGO_SUPERUSER_USERNAME=${DJANGO_SUPERUSER_USERNAME:-admin}
DJANGO_SUPERUSER_EMAIL=${DJANGO_SUPERUSER_EMAIL:[email protected]}
DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY:-secret}
DJANGO_DEBUG=${DJANGO_DEBUG:-1}
DJANGO_ALLOWED_HOSTS="${DJANGO_ALLOWED_HOSTS:-localhost 127.0.0.1 [::1]}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be in the docker-compose file, and the .env should not be comitted

postgres/.env Outdated
Comment on lines 1 to 5
POSTGRES_DB=${POSTGRES_DB:-avc}
POSTGRES_USER=${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
POSTGRES_HOST=${POSTGRES_HOST:-db}
POSTGRES_PORT=${POSTGRES_PORT:-5432}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be in the docker-compose file, and the .env should not be comitted

Copy link
Contributor Author

@tevariou tevariou Dec 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

un .env (pas commit parce qu'il peut contenir des secrets) qui vient override les defaults du docker-compose

The variables in the docker-compose file take precedence on the .env files https://docs.docker.com/compose/environment-variables/#the-env-file docker-compose > shell > .env > dockerfile. What is usually done is to have multiple docker-compose files: https://docs.docker.com/compose/extends/ && https://docs.docker.com/compose/production/

One base docker-compose.yml, one docker-compose.override.yml for local dev with one or multiple .env.local files added in .gitignore and one docker-compose.prod.yml. The .env and .env.local are merged and the variables in the local file will take precedence on the base in case of duplicate.

Here two issues I think this solves:

  • Make front development easy. No need to build the front app which takes time. Use of .env files is also idiomatic in React. To override the variables to connect with a remote API, simply add variables in a .env.local file (no edit of the base configuration needed and there are gitignored).
  • Running the app the easiest way possible with minimal edit.

So i've made some changes, tell me what you think!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

works for me 👌

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I advise you not to do this way.

  • There is now 6 files to manage configuration.
  • Having a single .env in the key=value format (no fancy yaml stuff) allows you to use python-dotenv in load config in the same manner than docker. This makes it very easy to switch from the local development environment to a containerized application. Furthermore, true dotenv files (key=value format) are supported by IDEs (idea, vscode), which allows you to reuse them for debugging.
  • In your proposed setup, casually changing configuration while developping would have to be done by modifying the settings.py configuration file, which would show up in you diff.
  • Putting separate production compose files is interesting for documentation, but it has little value for developpment.

@@ -0,0 +1,2 @@
NGINX_PORT=${NGINX_PORT:-8080}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

app/Dockerfile Outdated
Comment on lines 2 to 9
#WORKDIR /app
#ENV PATH /app/node_modules/.bin:$PATH
#COPY ./package.json .
#RUN yarn
#COPY . .
#RUN yarn build
#
#FROM busybox
#WORKDIR /app
#COPY --from=builder /app/build .

FROM busybox
FROM node:15.3.0
WORKDIR /app
COPY ./build .
ENV PATH /app/node_modules/.bin:$PATH
COPY ./package.json .
COPY ./.yarnrc .
COPY ./yarn.lock .
RUN yarn
COPY . .
RUN yarn build
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this produces a very large image for just static files in the end, why not using 2-stage build ?

app/.env Outdated
@@ -0,0 +1 @@
REACT_APP_API_URL=${API_URL:-http://localhost:8080/api}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

@tevariou tevariou requested a review from simonvadee December 5, 2020 12:09
app/package.json Outdated
@@ -44,7 +44,7 @@
"@types/uuid": "^8.3.0"
},
"scripts": {
"start": "react-scripts start",
"start": "env $(cat .env) react-scripts start",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't the .env automatically understood by react ?

Comment on lines 13 to 21
// const ME = gql`
// query me {
// me {
// id
// name
// email
// }
// }
// `;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't you want to remove these comments ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied the component from cohort and it used graphQL to handle user logged status. I juste kept some code commented in case I need it later because for now authentication persistance is not wired yet. (if you refresh the page, authentication credentials are lost)

name: "password",
password: true,
label: "Password",
// validationRules: { required: "Field required" },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is it commented out ?

<FormBuilder<LoginData>
properties={properties}
formId="login-form"
defaultValues={{ username: "toto", password: "" }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want to keep these ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No we don't

Comment on lines 5 to 9
// userName: string
// displayName: string
// firstName: string
// lastName: string
// deidentified: boolean
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

@@ -0,0 +1,20 @@
import { AUTH_API_URL } from "../constants";

const API_URL = `${process.env.REACT_APP_AUTH_API_URL}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not using AUTH_API_URL ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in next commit ;)

// method: "POST",
// body: JSON.stringify({ username, password }),
// });
const loginData = await fetch(`http://localhost:8080/api/token/`, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AUTH_API_URL ?

Comment on lines 8 to 9
volumes:
- app_data:/app
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this volume is necessary

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This volume is actually shared with nginx. That's where the react static files are stored.

Comment on lines 5 to 6
# env_file:
# - ./postgres/.env.local
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it work if I only uncomment these lines but not the ones below ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as the file exist, yes! I need to comment those lines since docker will return an error if the files doesn't exist and will not run (instead of ignoring them which would have been nicer).

postgres/.env Outdated
Comment on lines 1 to 5
POSTGRES_DB=${POSTGRES_DB:-avc}
POSTGRES_USER=${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
POSTGRES_HOST=${POSTGRES_HOST:-db}
POSTGRES_PORT=${POSTGRES_PORT:-5432}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

works for me 👌

@@ -6,7 +6,7 @@ import { useDispatch } from "react-redux";
// import { gql } from 'apollo-boost'

import { ACCES_TOKEN } from "../constants";
import { login } from "state/user";
// import { login } from "state/user";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove

RUN groupadd -r api && useradd --create-home --no-log-init -r -g api api
USER api:api
WORKDIR /home/api
ENV PYTHONPATH=/home/api
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already the case if the cwd is /home/api

Copy link
Contributor Author

@tevariou tevariou Dec 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that it's not. uwsgi crash returning a ModuleNotFoundError: No module named 'avc_forms' if I don't specify the python path. PYTHONPATH is undefined in the container if I don't define it manually here.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you problem. You can set module=avc_forms.wsgi in the uwsgi.ini, which a pretty standard way.

Note: regarding the uwsgi.ini file, there's a lot of stuff in there. Did you find that everything was required?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The module and the mount attributes seem to be mutually exclusive. The others are either required (chdir, mount, manage-script-name, socket, chmod-socket, uid, guid) or standard server configuration. I did a bit of cleaning.

USER api:api
WORKDIR /home/api
ENV PYTHONPATH=/home/api
ENV PATH /home/api/.local/bin:${PATH}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this required ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning supression such as Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. during python package install and it doesn't find uwsgi when it's time to run the server.

api/Dockerfile Outdated
WORKDIR /home/api
ENV PYTHONPATH=/home/api
ENV PATH /home/api/.local/bin:${PATH}
ENV DJANGO_SETTINGS_MODULE=avc_forms.settings
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting this here is not required (use a default in the uwsgi.py).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

django-admin returns No Django settings specified if I don't specify this (in docker-entrypoint.sh I'm using django-admin to create a superuser). It seems the default is correctly set in uwsgi.py though.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use python manage.py createsuperuser to create a superuser.

As a rule of thumb, use the manage.py over django-admin when the codebase is available.

api/avc_forms/settings.py Show resolved Hide resolved
api/docker-entrypoint.sh Outdated Show resolved Hide resolved
django-filter==2.4.0
djangorestframework==3.12.2
djangorestframework-simplejwt==4.6.0
Markdown==3.3.3
psycopg2==2.8.6
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

psycopg2 is always builded, hence it requires OS packages (at least for bionic). This could be documented in a CONTRIBUTING.md.

app/Dockerfile Outdated
COPY ./yarn.lock .
RUN yarn
COPY . .
RUN yarn build

FROM busybox
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why did you use busybox ? (btw you should set a version)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to use scratch since I only want to put the build directory in the app-data volume but It crash if I don't specify a CMD to run something... so I use busybox here since it's the smallest image I could find and it shutdowns gracefully.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I see.

In terms of engineering, I think it would be best to try to reuse the same image at the company (namely python:3.x-slim), to reduce the cognitive load (same files, same tricks).

But it is your call.

@@ -1,5 +1,5 @@
upstream django {
server unix:/api/avc_forms/avc_forms.sock;
server unix:/api/avc_forms.sock;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@ -11,8 +11,11 @@
"""

from pathlib import Path
# from dotenv import load_dotenv, find_dotenv
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

catch them all !

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unexpected pokemon ?

BPierrick and others added 4 commits December 9, 2020 18:14
- Login/Logout
- Patient creation
- Patient imports from CSV are sent to the API
- Patient deletion is wired with the API

- Note: Patient edit is not wired yet
@tevariou tevariou requested a review from vmttn December 9, 2020 22:30
tevariou and others added 2 commits December 11, 2020 12:31
- Handles entries edit (patient editing)
- Added notifications system
- Some improvements in token managment
@tevariou tevariou merged commit 9f69b10 into master Dec 15, 2020
@tevariou tevariou deleted the tr/feat/token_auth branch December 15, 2020 10:17
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Yarn timeout
4 participants