Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Argon Conversion #421

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ GRAPH_DB=police_data
GRAPH_URI=db
GRAPH_NM_URI=db:7687
GRAPH_PORT=5432
NPDI_API_PORT=5000
NPDI_API_PORT=5001
MIXPANEL_TOKEN=your_mixpanel_token
MAIL_SERVER=mail.yourdomain.com
MAIL_PORT=465
Expand Down
18 changes: 9 additions & 9 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3-slim-bookworm AS base
FROM --platform=linux/arm64 python:3.11-slim-bookworm AS base

# Install dependencies
RUN apt-get update -y && apt-get install -y \
Expand All @@ -7,30 +7,30 @@ RUN apt-get update -y && apt-get install -y \
libpq-dev \
gcc \
python3-dev \
libffi-dev
libffi-dev \
build-essential \
&& rm -rf /var/lib/apt/lists/*

# Install Rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"


# Add docker-compose-wait (if still needed)
ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.12.1/wait /wait
RUN chmod +x /wait


FROM base
WORKDIR /app/

ARG NPDI_API_PORT=5000

RUN pip3 install --upgrade pip

COPY requirements/ requirements/

RUN pip3 install -r requirements/dev_unix.txt

COPY . .

ENV PORT=$NPDI_API_PORT
ARG NPDI_API_PORT=5001
ENV NPDI_API_PORT=$NPDI_API_PORT
EXPOSE $NPDI_API_PORT

CMD /wait && ./run_dev.sh

20 changes: 5 additions & 15 deletions backend/Dockerfile.cloud
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Stage 1: Build Stage
FROM python:3-slim-bookworm AS build
FROM --platform=linux/arm64 python:3-slim-bookworm AS build

# Install required packages
RUN apt-get update -y && apt-get install -y \
gcc \
g++ \
Expand All @@ -13,27 +12,19 @@ RUN apt-get update -y && apt-get install -y \
&& rm -rf /var/lib/apt/lists/*

# Install Rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
&& . "$HOME/.cargo/env"
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"

WORKDIR /app/

# Instead of manually downloading a wheel, rely on pip:
COPY requirements/prod.txt .

SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN arch=$(arch) && \
file=pandas-2.2.2-cp312-cp312-manylinux_2_17_${arch}.manylinux2014_${arch}.whl && \
url="https://pypi.debian.net/pandas/${file}" && \
wget ${url} && \
sed -i "s/pandas==2.2.2/${file}/" prod.txt

RUN pip install --no-cache-dir --user -r prod.txt

COPY . .

# Stage 2: Production Image
FROM python:3-slim-bookworm
FROM --platform=linux/arm64 python:3-slim-bookworm

WORKDIR /app/

Expand All @@ -42,12 +33,11 @@ ARG NPDI_API_PORT=5000
# Copy installed packages from the build stage
COPY --from=build /root/.local /root/.local

# Update PATH to include pip-installed packages
ENV PATH=/root/.local/bin:${PATH}

COPY . .

EXPOSE $NPDI_API_PORT
ENV NPDI_API_PORT=$NPDI_API_PORT

ENTRYPOINT [ "./run_cloud.sh" ]
ENTRYPOINT ["./run_cloud.sh"]
11 changes: 7 additions & 4 deletions backend/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from flask import Flask
from flask_mail import Mail
from flask_cors import CORS
from argon2 import PasswordHasher
from backend.config import get_config_from_env
from backend.auth import jwt, refresh_token
from backend.schemas import spec
Expand Down Expand Up @@ -46,7 +47,7 @@ def register_extensions(app: Flask):
# Neo4j setup
# Driver setup
db_driver = GraphDatabase.driver(
f"bolt://{app.config["GRAPH_NM_URI"]}",
f'bolt://{app.config["GRAPH_NM_URI"]}',
auth=(
app.config["GRAPH_USER"],
app.config["GRAPH_PASSWORD"]
Expand All @@ -70,10 +71,12 @@ def register_extensions(app: Flask):
neo_config.DATABASE_URL = neo_url

spec.register(app)
# login_manager.init_app(app)
# TODO: Add the correct route info
# login_manager.login_view = 'auth.login'

# Authentication
ph = PasswordHasher()
app.config['PASSWORD_HASHER'] = ph
jwt.init_app(app)

Mail(app)
CORS(app, resources={r"/api/*": {"origins": "*"}})

Expand Down
25 changes: 20 additions & 5 deletions backend/database/models/user.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Define the SQL classes for Users."""

from werkzeug.security import generate_password_hash, check_password_hash
from flask import current_app
from argon2 import exceptions as argon2_exceptions
from backend.schemas import JsonSerializable, PropertyEnum
from neomodel import (
Relationship, StructuredNode,
Expand Down Expand Up @@ -68,6 +69,14 @@ class User(StructuredNode, JsonSerializable):
'backend.database.models.source.StagedInvitation',
"EXTENDED")

def _get_password_hasher(self):
"""
Get the password hasher.
Returns:
PasswordHasher: The password hasher.
"""
return current_app.config['PASSWORD_HASHER']

def verify_password(self, pw: str) -> bool:
"""
Verify the user's password using bcrypt.
Expand All @@ -77,16 +86,22 @@ def verify_password(self, pw: str) -> bool:
Returns:
bool: True if the password is correct, False otherwise.
"""
# return bcrypt.checkpw(pw.encode("utf8"), self.password.encode("utf8"))
return check_password_hash(self.password_hash, pw)
try:
return self._get_password_hasher().verify(self.password_hash, pw)
except argon2_exceptions.VerifyMismatchError:
return False
except argon2_exceptions.VerificationError:
return False
except argon2_exceptions.InvalidHash:
return False

def set_password(self, pw: str):
"""
Set the user's password.
Args:
pw (str): The password to set.
"""
self.password_hash = User.hash_password(pw)
self.password_hash = self.hash_password(pw)

def send_email_verification(self):
"""
Expand Down Expand Up @@ -119,7 +134,7 @@ def hash_password(cls, pw: str) -> str:
Returns:
str: The hashed password.
"""
return generate_password_hash(pw)
return current_app.config['PASSWORD_HASHER'].hash(pw)

@classmethod
def get_by_email(cls, email: str) -> "User":
Expand Down
1 change: 1 addition & 0 deletions backend/routes/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def register():
"""
logger = logging.getLogger("user_register")
body: RegisterUserDTO = request.validated_body
logger.info(f"Registering user with email {body.email}.")

# Check to see if user already exists
user = User.nodes.first_or_none(email=body.email)
Expand Down
29 changes: 20 additions & 9 deletions backend/tests/test_auth.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import pytest
from backend.database.models.user import User, UserRole
from argon2 import PasswordHasher

ph = PasswordHasher()

mock_user = {
"email": "[email protected]",
"password_hash": User.hash_password("my_password"),
"password_hash": ph.hash("my_password"),
"first_name": "John",
"last_name": "Doe",
"phone_number": "1234567890",
Expand All @@ -20,8 +23,12 @@ def existing_user():

@pytest.mark.parametrize(
(
"email", "password", "firstname", "lastname",
"phone_number", "expected_status_code"
"email",
"password",
"firstname",
"lastname",
"phone_number",
"expected_status_code",
),
[
("[email protected]", "my_password", "John", "Doe", "1234567890", 200),
Expand All @@ -31,9 +38,14 @@ def existing_user():
],
)
def test_register(
client, existing_user, email, password,
firstname, lastname, phone_number,
expected_status_code
client,
existing_user,
email,
password,
firstname,
lastname,
phone_number,
expected_status_code,
):
res = client.post(
"api/v1/auth/register",
Expand All @@ -43,7 +55,7 @@ def test_register(
"firstname": firstname,
"lastname": lastname,
"phone_number": phone_number,
}
},
)

assert res.status_code == expected_status_code
Expand All @@ -68,8 +80,7 @@ def test_register(
[("my_password", 200), ("bad_password", 401), (None, 422)],
)
def test_login(
db_session,
client, example_user, password, expected_status_code
db_session, client, example_user, password, expected_status_code
):
res = client.post(
"api/v1/auth/login",
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ services:
context: .
dockerfile: ./backend/Dockerfile
args:
NPDI_API_PORT: ${NPDI_API_PORT:-5000}
NPDI_API_PORT: ${NPDI_API_PORT:-5001}
volumes:
- .:/app
depends_on:
Expand All @@ -57,7 +57,7 @@ services:
MIXPANEL_TOKEN: ${MIXPANEL_TOKEN:-notset}
WAIT_HOSTS: db:7687
ports:
- ${NPDI_API_PORT:-5000}:${NPDI_API_PORT:-5000}
- ${NPDI_API_PORT:-5001}:${NPDI_API_PORT:-5001}
volumes:
neo4j: {}
neo4j_logs: {}
Expand Down
33 changes: 20 additions & 13 deletions requirements/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
# psycopg requires postgres development files in order to compile the
# requirements, so this image starts with the same image as the database
# containers and installs the same version of python as the api containers

FROM postgres:17 as base
FROM debian:bookworm-slim

# Install packages required to build Python and extensions
RUN apt-get update && apt-get install -y \
make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev \
libsqlite3-dev wget curl llvm libncursesw5-dev xz-utils tk-dev \
libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev git
curl wget git make build-essential libssl-dev zlib1g-dev libbz2-dev \
libreadline-dev libsqlite3-dev llvm libncursesw5-dev xz-utils tk-dev \
libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev ca-certificates \
libopenblas-dev gfortran \
&& rm -rf /var/lib/apt/lists/*

FROM base
# Install Rust and Cargo for packages requiring Rust
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"

SHELL ["bash", "-lc"]
# Install pyenv
RUN curl https://pyenv.run | bash && \
echo 'export PATH="$HOME/.pyenv/shims:$HOME/.pyenv/bin:$PATH"' >> ~/.bashrc

ENV PYTHON_VERSION=3.13.0
ENV PYTHON_VERSION=3.11
SHELL ["bash", "-lc"]

# Install the specified Python version via pyenv
RUN pyenv install ${PYTHON_VERSION} && pyenv global ${PYTHON_VERSION}
RUN pip install -U pip-tools

# Upgrade pip and install pip-tools
RUN pip install --no-cache-dir -U pip pip-tools

# Copy requirements directory and run the update script
COPY . requirements/

CMD python requirements/update.py
CMD python requirements/update.py
3 changes: 2 additions & 1 deletion requirements/_core.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
argon2-cffi
bcrypt
black
boto3
Expand Down Expand Up @@ -28,9 +29,9 @@ openpyxl
xlsxwriter
numpy
spectree
jupyter
mixpanel
ua-parser
ujson
neo4j
neomodel==5.3.3
cffi==1.17.1
Loading
Loading