Skip to content

Commit

Permalink
Release 2023.04.2 (#1375)
Browse files Browse the repository at this point in the history
  • Loading branch information
thekaveman committed Apr 21, 2023
2 parents 8740b19 + ee42f91 commit ddbc3a5
Show file tree
Hide file tree
Showing 49 changed files with 638 additions and 443 deletions.
7 changes: 2 additions & 5 deletions .devcontainer/Dockerfile
@@ -1,15 +1,12 @@
FROM benefits_client:latest

# install devcontainer requirements
COPY .devcontainer/requirements.txt .devcontainer/requirements.txt
RUN pip install -r .devcontainer/requirements.txt
RUN pip install -e .[dev,test]

# docs requirements are in a separate file for the GitHub Action
COPY docs/requirements.txt docs/requirements.txt
RUN pip install -r docs/requirements.txt

COPY tests/pytest/requirements.txt tests/pytest/requirements.txt
RUN pip install -r tests/pytest/requirements.txt

# install pre-commit environments in throwaway Git repository
# https://stackoverflow.com/a/68758943
COPY .pre-commit-config.yaml .
Expand Down
1 change: 1 addition & 0 deletions .devcontainer/devcontainer.json
Expand Up @@ -20,6 +20,7 @@
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"bungcip.better-toml",
"batisteo.vscode-django",
"bpruitt-goddard.mermaid-markdown-syntax-highlighting",
"eamodio.gitlens",
Expand Down
4 changes: 0 additions & 4 deletions .devcontainer/requirements.txt

This file was deleted.

1 change: 1 addition & 0 deletions .dockerignore
Expand Up @@ -5,3 +5,4 @@
.flake8
.*ignore
*.db
*.egg-info
2 changes: 1 addition & 1 deletion .github/dependabot.yml
Expand Up @@ -6,7 +6,7 @@
version: 2
updates:
- package-ecosystem: "pip"
directory: "/appcontainer" # main requirements.txt
directory: "/" # pyproject.toml
schedule:
interval: "daily"
commit-message:
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/labeler-deploy-dev.yml
Expand Up @@ -5,11 +5,11 @@ on:
branches: [dev]
types: [opened]
paths:
- '.github/workflows/deploy-*.yml'
- 'benefits/**'
- 'bin/**'
- ".github/workflows/deploy-*.yml"
- "benefits/**"
- "bin/**"
- Dockerfile
- requirements.txt
- pyproject.toml

jobs:
label-deployment-dev:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests-pytest.yml
Expand Up @@ -18,10 +18,10 @@ jobs:
with:
python-version-file: .github/workflows/.python-version
cache: pip
cache-dependency-path: "**/requirements.txt"
cache-dependency-path: "**/pyproject.toml"

- name: Install Python dependencies
run: pip install -r appcontainer/requirements.txt -r tests/pytest/requirements.txt
run: pip install -e .[test]

- name: Run setup
run: ./bin/init.sh
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Expand Up @@ -34,7 +34,7 @@ repos:
args: ["--maxkb=1500"]

- repo: https://github.com/psf/black
rev: 23.1.0
rev: 23.3.0
hooks:
- id: black
types:
Expand All @@ -61,6 +61,6 @@ repos:
types_or: [javascript, css]

- repo: https://github.com/Riverside-Healthcare/djLint
rev: v1.19.16
rev: v1.23.0
hooks:
- id: djlint-django
18 changes: 11 additions & 7 deletions appcontainer/Dockerfile
@@ -1,15 +1,19 @@
FROM ghcr.io/cal-itp/docker-python-web:main

# install python dependencies
COPY appcontainer/requirements.txt requirements.txt
RUN pip install -r requirements.txt
# upgrade pip
RUN python -m pip install --upgrade pip

# copy Django utility script
COPY manage.py manage.py
# overwrite default nginx.conf
COPY appcontainer/nginx.conf /etc/nginx/nginx.conf
COPY appcontainer/proxy.conf /home/calitp/run/proxy.conf

# copy source files
COPY bin/ bin/
COPY benefits/ benefits/
COPY manage.py manage.py
COPY bin bin
COPY benefits benefits
COPY pyproject.toml pyproject.toml

RUN pip install -e .

# ensure $USER can compile messages in the locale directories
USER root
Expand Down
92 changes: 92 additions & 0 deletions appcontainer/nginx.conf
@@ -0,0 +1,92 @@
worker_processes auto;
error_log stderr warn;
pid /var/run/nginx.pid;

events {
worker_connections 1024;
accept_mutex on;
}

http {
include mime.types;
default_type application/octet-stream;
sendfile on;
gzip on;
keepalive_timeout 5;

log_format main '[$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /dev/stdout main;

upstream app_server {
# fail_timeout=0 means we always retry an upstream even if it failed
# to return a good HTTP response
server unix:/home/calitp/run/gunicorn.sock fail_timeout=0;
}

# maps $binary_ip_address to $limit variable if request is of type POST
map $request_method $limit {
default "";
POST $binary_remote_addr;
}

# define a zone with 10mb memory, rate limit to 12 requests/min (~= 1 request/5 seconds) on applied locations
# $limit will eval to $binary_remote_addr for POST requests using the above map
# requests with an empty key value (e.g. GET) are not affected
# http://nginx.org/en/docs/http/ngx_http_limit_req_module.html#limit_req_zone
limit_req_zone $limit zone=rate_limit:10m rate=12r/m;

server {
listen 8000;

keepalive_timeout 65;

# 404 known scraping path targets
# case-insensitive regex matches the given path fragment anywhere in the request path
location ~* /(\.?git|api|app|assets|ats|bootstrap|bower|cgi|content|credentials|docker|doc|env|example|swagger|web) {
access_log off;
log_not_found off;
return 404;
}

# 404 known scraping file targets
# case-insensitive regex matches the given file extension anywhere in the request path
location ~* /.*\.(asp|axd|cgi|com|env|json|php|xml|ya?ml) {
access_log off;
log_not_found off;
return 404;
}

location /favicon.ico {
access_log off;
log_not_found off;
expires 1y;
add_header Cache-Control public;
}

# path for static files
location /static/ {
alias /home/calitp/app/static/;
expires 1y;
add_header Cache-Control public;
}

location / {
# checks for static file, if not found proxy to app
try_files $uri @proxy_to_app;
}

# apply rate limit to these paths
# case-insensitive regex matches path
location ~* ^/(eligibility/confirm)$ {
limit_req zone=rate_limit;
include /home/calitp/run/proxy.conf;
}

# app path
location @proxy_to_app {
include /home/calitp/run/proxy.conf;
}
}
}
8 changes: 8 additions & 0 deletions appcontainer/proxy.conf
@@ -0,0 +1,8 @@
# the core app proxy directives
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
# we don't want nginx trying to do something clever with
# redirects, we set the Host: header above already.
proxy_redirect off;
proxy_pass http://app_server;
7 changes: 0 additions & 7 deletions appcontainer/requirements.txt

This file was deleted.

2 changes: 1 addition & 1 deletion benefits/__init__.py
@@ -1,3 +1,3 @@
__version__ = "2023.04.1"
__version__ = "2023.04.2"

VERSION = __version__
41 changes: 1 addition & 40 deletions benefits/core/middleware.py
Expand Up @@ -2,12 +2,10 @@
The core application: middleware definitions for request/response cycle.
"""
import logging
import time

from django.conf import settings
from django.http import HttpResponse, HttpResponseBadRequest
from django.http import HttpResponse
from django.shortcuts import redirect
from django.template import loader
from django.template.response import TemplateResponse
from django.utils.decorators import decorator_from_middleware
from django.utils.deprecation import MiddlewareMixin
Expand Down Expand Up @@ -40,43 +38,6 @@ def process_request(self, request):
return user_error(request)


class RateLimit(MiddlewareMixin):
"""Middleware checks settings and session to ensure rate limit is respected."""

def process_request(self, request):
if not settings.RATE_LIMIT_ENABLED:
logger.debug("Rate Limiting is not configured")
return None

if request.method in settings.RATE_LIMIT_METHODS:
session.increment_rate_limit_counter(request)
else:
# bail early if the request method doesn't match
return None

counter = session.rate_limit_counter(request)
reset_time = session.rate_limit_time(request)
now = int(time.time())

if counter > settings.RATE_LIMIT:
if reset_time > now:
logger.warning("Rate limit exceeded")
home = viewmodels.Button.home(request)
page = viewmodels.ErrorPage.server_error(
title="Rate limit error",
headline="Rate limit error",
paragraphs=["You have reached the rate limit. Please try again."],
button=home,
)
t = loader.get_template("400.html")
return HttpResponseBadRequest(t.render(page.context_dict()))
else:
# enough time has passed, reset the rate limit
session.reset_rate_limit(request)

return None


class EligibleSessionRequired(MiddlewareMixin):
"""Middleware raises an exception for sessions lacking confirmed eligibility."""

Expand Down

0 comments on commit ddbc3a5

Please sign in to comment.