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

Subdirect #15

Merged
merged 23 commits into from
Nov 8, 2024
Merged
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
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.venv
26 changes: 17 additions & 9 deletions .github/workflows/check.yaml
Original file line number Diff line number Diff line change
@@ -1,33 +1,41 @@
jobs:
check:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: source includes.sh && ghas
- run: source includes.sh dcb
# Ideally this would come from default.settings_module()
- run: source includes.sh dcr web python -m pytest --ds=demodj.settings

check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: npm
node-version-file: .nvmrc
- run: npm install --frozen-lockfile

- uses: actions/setup-python@v5
with:
python-version-file: .python-version

- run: pip install --upgrade pip uv
env:
PIP_PROGRESS_BAR: 'off'
- run: |

Choose a reason for hiding this comment

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

I moved to setup-uv, if you are curious

      - uses: astral-sh/setup-uv@v3
        with:
          enable-cache: true
      - run: uv python pin ${{ matrix.python-version }}
      - run: uv sync --python-preference=only-managed

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've been seeing uv sync and uv.lock in the docs. I'll have to try them out.

pip install --disable-pip-version-check --progress-bar off --upgrade \
"$(grep ^uv requirements.txt)"

- run: uv venv
- run: source includes.sh && ups && a && pcam --color always --show-diff-on-failure
- run: .venv/bin/python -m build
- run: .venv/bin/twine check dist/*
- run: .venv/bin/python -m manage check
- run: .venv/bin/python -m manage makemigrations --check
- run: source includes.sh dcb
# Ideally this would come from default.settings_module()
- run: source includes.sh dcr web pytest --ds=demodj.settings

summarize:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: source includes.sh summarize

on: # yamllint disable-line rule:truthy
- pull_request
9 changes: 8 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,23 @@ repos:
- --write-changes
- id: dot-yaml
- id: gitignore-node-python
- id: hadolint
- id: includes-sh
- id: mailmap
- id: mypy
- id: prettier-write
exclude: ^package-lock\.json$
- id: ruff-check-fix
- id: ruff-format
- id: shellcheck
- id: uv-pip-compile
args:
- --all-extras
- --quiet
- pyproject.toml
files: pyproject.toml
- id: yamllint
exclude: |
(?x)^(
package-lock.json
package-lock\.json
)$
39 changes: 29 additions & 10 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,20 +1,39 @@
FROM python:3.12
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim

# TODO assert C.UTF8 and PYTHONUNBUFFERED are set correctly
# TODO assert C.UTF8 locale and PYTHONUNBUFFERED are set correctly
ENV PYTHONUNBUFFERED 1

WORKDIR /srv
RUN --mount=type=cache,target=/root/.cache pip install --upgrade pip-tools wheel
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache pip-sync

# TODO multi-stage for faster development builds?
RUN mkdir -p static
# hadolint ignore=DL3008,SC2046
RUN --mount=type=cache,target=/var/cache/apt \
--mount=type=bind,source=includes.sh,target=includes.sh \
rm /etc/apt/apt.conf.d/docker-clean \
darpham marked this conversation as resolved.
Show resolved Hide resolved
&& echo 'Binary::apt::APT::Keep-Downloaded-Packages "1";' > /etc/apt/apt.conf.d/99cache \
&& apt-get update \
&& apt-get install -qq --no-install-recommends --yes nodejs npm \
$(sed -En 's/"$//; s/^PACKAGES="//p' includes.sh) \
&& rm -rf /var/lib/apt/lists/*

# Bind mount caused "EROFS: read-only file system, open '/srv/package-lock.json'"
COPY package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
--mount=type=bind,source=package.json,target=package.json \
npm install --frozen-lockfile

COPY . .
# Not using /srv/.venv because it would make volume-mounting /srv harder
ENV UV_COMPILE_BYTECODE=1
ENV UV_LINK_MODE=copy
ENV UV_SYSTEM_PYTHON=1
# hadolint ignore=DL3013,DL3042
RUN --mount=type=cache,target=/root/.cache \
--mount=type=bind,source=requirements.txt,target=requirements.txt \
uv pip sync --quiet requirements.txt

COPY . ./

ARG STATIC_URL
ENV STATIC_URL ${STATIC_URL:-/static/}
RUN STATIC_URL=${STATIC_URL} python -m manage collectstatic --no-input
RUN mkdir -p static && STATIC_URL=${STATIC_URL} python -m manage collectstatic --no-input

EXPOSE 8001
ENV PYTHONUNBUFFERED 1
25 changes: 4 additions & 21 deletions allowedflare/__init__.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,6 @@
# TODO Avoid "AppRegistryNotReady: Apps aren't loaded yet."
# To avoid "AppRegistryNotReady: Apps aren't loaded yet." and similar problems, this file must
# only import from the likes of allowedflare.core, not the likes of allowedflare.django.

from allowedflare.allowedflare import clean_username
from allowedflare.core import authenticate, clean_username

# Django REST Framework authentication class
from allowedflare.django import Authentication

# Django Admin authentication backend
from allowedflare.django import Backend

# Django Admin login view
from allowedflare.django import LoginView

# SQL Explorer login view wrapper
from allowedflare.django import login_view_wrapper

__all__ = (
Authentication.__name__,
Backend.__name__,
LoginView.__name__,
clean_username.__name__,
login_view_wrapper.__name__,
)
__all__ = (authenticate.__name__, clean_username.__name__)
File renamed without changes.
2 changes: 1 addition & 1 deletion allowedflare/django.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from django.http.response import HttpResponseBase
from rest_framework.authentication import BaseAuthentication

from allowedflare.allowedflare import authenticate
from allowedflare import authenticate

logger = logging.getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion allowedflare/juptyerhub.py → allowedflare/jupyter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from jupyterhub.auth import Authenticator # type: ignore
from tornado.web import RequestHandler

from allowedflare.allowedflare import authenticate
from allowedflare import authenticate


class JupyterHub(Authenticator):
Expand Down
11 changes: 5 additions & 6 deletions allowedflare/test_allowedflare.py → allowedflare/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
from cryptography.hazmat.backends import default_backend
from datetime import UTC, datetime

from allowedflare import clean_username
from allowedflare.allowedflare import authenticate
from allowedflare import authenticate, clean_username


def test_clean_username_unmodified(monkeypatch):
Expand All @@ -18,7 +17,7 @@ def test_clean_username_unmodified(monkeypatch):


def test_clean_username_email_domain_removed(monkeypatch):
monkeypatch.setenv('ALLOWEDFLARE_EMAIL_REGEX', 'domain\.com')
monkeypatch.setenv('ALLOWEDFLARE_EMAIL_REGEX', r'domain\.com')
assert clean_username('user@domain.com') == 'user'


Expand Down Expand Up @@ -57,7 +56,7 @@ def test_authenticate_invalid_token(monkeypatch):


def test_authenticate_jwk_client_error(monkeypatch):
private_key = generate_private_key(65537, 512, default_backend())
private_key = generate_private_key(65537, 1024, default_backend())
monkeypatch.setenv('ALLOWEDFLARE_ACCESS_URL', 'https://demo.cloudflareaccess.com')
user, message, token = authenticate({'CF_Authorization': encode({}, private_key, 'RS256')})
assert user == ''
Expand All @@ -66,7 +65,7 @@ def test_authenticate_jwk_client_error(monkeypatch):


def test_authenticate_valid_token(mocker, monkeypatch):
private_key = generate_private_key(65537, 512, default_backend())
private_key = generate_private_key(65537, 1024, default_backend())
token = {
'aud': 'audience',
'email': 'firstname.lastname@domain.com',
Expand All @@ -76,7 +75,7 @@ def test_authenticate_valid_token(mocker, monkeypatch):
monkeypatch.setenv('ALLOWEDFLARE_ACCESS_URL', 'https://demo.cloudflareaccess.com')
monkeypatch.setenv('ALLOWEDFLARE_AUDIENCE', 'audience')
mock_get_signing_key_from_jwt = mocker.patch(
'allowedflare.allowedflare.PyJWKClient.get_signing_key_from_jwt', autospec=True
'allowedflare.core.PyJWKClient.get_signing_key_from_jwt', autospec=True
)
mock_get_signing_key_from_jwt.return_value.key = private_key.public_key()

Expand Down
2 changes: 1 addition & 1 deletion allowedflare/test_django.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.test import RequestFactory

from allowedflare import LoginView
from allowedflare.django import LoginView


def test_allowedflare_login_view(monkeypatch, rf: RequestFactory):
Expand Down
11 changes: 7 additions & 4 deletions demodj/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,13 @@
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend', 'allowedflare.Backend']
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'allowedflare.django.Backend',
]

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ('allowedflare.Authentication',),
'DEFAULT_AUTHENTICATION_CLASSES': ('allowedflare.django.Authentication',),
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
'PAGE_SIZE': 10,
Expand All @@ -73,7 +76,7 @@
TEMPLATES = [
{ # To avoid admin.E403, configure Django templates
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['django_allowedflare'],
'DIRS': ['allowedflare'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
Expand All @@ -95,7 +98,7 @@
DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3'}}
EXPLORER_CONNECTIONS = {'Demo DJ': 'default'}
EXPLORER_DEFAULT_CONNECTION = 'default'
EXPLORER_NO_PERMISSION_VIEW = 'allowedflare.login_view_wrapper'
EXPLORER_NO_PERMISSION_VIEW = 'allowedflare.django.login_view_wrapper'


# Password validation
Expand Down
2 changes: 1 addition & 1 deletion demodj/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from django.urls import include, path
from rest_framework import routers

from allowedflare import LoginView
from allowedflare.django import LoginView
from . import views

router = routers.DefaultRouter()
Expand Down
7 changes: 3 additions & 4 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ services:
proxy:
image: cloudflare/cloudflared:2024.2.1

lab:
build: .
command: python -m manage shell_plus --allow-root
hub:
command: PATH="node_modules/.bin:$PATH" .venv/bin/python -m jupyterhub
depends_on:
- postgres
environment:
Expand All @@ -29,7 +28,7 @@ services:

web:
build: .
command: python -m manage runserver
command: .venv/bin/python -m manage runserver
depends_on:
- postgres
environment:
Expand Down
4 changes: 2 additions & 2 deletions jupyterhub_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
# - null: jupyterhub.auth.NullAuthenticator
# - pam: jupyterhub.auth.PAMAuthenticator
# Default: 'jupyterhub.auth.PAMAuthenticator'
c.JupyterHub.authenticator_class = 'jupyterhub_allowedflare.JupyterHub'
c.JupyterHub.authenticator_class = 'allowedflare.jupyter.JupyterHub'

## The base URL of the entire application.
#
Expand Down Expand Up @@ -1150,7 +1150,7 @@
# Note that this does *not* prevent users from accessing files outside of this
# path! They can do so with many other means.
# Default: ''
# c.Spawner.notebook_dir = ''
c.Spawner.notebook_dir = '~/code/allowedflare'

## Allowed scopes for oauth tokens issued by this server's oauth client.
#
Expand Down
5 changes: 5 additions & 0 deletions overrides.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"@jupyterlab/apputils-extension:themes": {
"theme": "JupyterLab Dark"
}
}
27 changes: 26 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,33 @@ dynamic = ['version']
name = 'allowedflare'
readme = 'README.md'

[project.optional-dependencies]
precommit = [

Choose a reason for hiding this comment

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

Doesn't pre-commit handle its deps itself? Or is this to enable local usage of all pre-commit tools

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is for system/local installation of tools like codespell which pre-commit will use.

'codespell',
'django-stubs',
'djangorestframework-stubs[compatible-mypy]',
'hadolint-py @ git+https://github.com/AleksaC/hadolint-py.git',
'mypy',
'pre-commit',
'ruff',
'shellcheck-py',
'uv',
'yamllint',
]
demo = [
'dj-notebook',
'django',
'django-extensions',
'django-health-check',
'django-sql-explorer',
'djangorestframework',
'jupyterlab',
]
build = ['build', 'twine']
test = ['pytest', 'pytest-django', 'pytest-mock']

[project.urls]
Homepage = 'https://github.com/covracer/allowedflare'
Homepage = 'https://github.com/biobuddies/allowedflare'

[tool.codespell]
ignore-words-list = 'afterall'
Expand Down
29 changes: 0 additions & 29 deletions requirements.in

This file was deleted.

Loading