Skip to content

Commit b78bb82

Browse files
authored
Merge pull request #1 from WIPACrepo/initial
initial version
2 parents 283926e + effc2e7 commit b78bb82

File tree

11 files changed

+462
-0
lines changed

11 files changed

+462
-0
lines changed

.github/workflows/ci.yaml

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
name: wipac ci/cd
2+
3+
on:
4+
push:
5+
branches:
6+
- '**'
7+
tags-ignore:
8+
- '**'
9+
10+
env:
11+
py_version: '3.12'
12+
13+
jobs:
14+
15+
flake8:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
- uses: astral-sh/ruff-action@v3
20+
21+
py-versions:
22+
runs-on: ubuntu-latest
23+
outputs:
24+
matrix: ${{ steps.versions.outputs.matrix }}
25+
steps:
26+
- uses: actions/checkout@v4
27+
- id: versions
28+
uses: WIPACrepo/wipac-dev-py-versions-action@v2.5
29+
30+
pip-install:
31+
needs: [py-versions]
32+
runs-on: ubuntu-latest
33+
strategy:
34+
max-parallel: 4
35+
fail-fast: false
36+
matrix:
37+
version: ${{ fromJSON(needs.py-versions.outputs.matrix) }}
38+
steps:
39+
- uses: actions/checkout@v4
40+
- uses: actions/setup-python@v4
41+
with:
42+
python-version: ${{ matrix.version }}
43+
- run: |
44+
pip install --upgrade pip wheel setuptools
45+
pip install .
46+
47+
docker-build:
48+
name: "Docker Image"
49+
runs-on: ubuntu-latest
50+
steps:
51+
- name: Checkout Project
52+
uses: actions/checkout@v4
53+
- name: Build Docker Image
54+
uses: docker/build-push-action@v4
55+
with:
56+
context: .
57+
push: false
58+
59+
release:
60+
if: ${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' }}
61+
needs: [flake8, pip-install, docker-build]
62+
runs-on: ubuntu-latest
63+
concurrency: release
64+
65+
permissions:
66+
id-token: write
67+
contents: write
68+
packages: write
69+
70+
steps:
71+
# Note: we need to checkout the repository at the workflow sha in case during the workflow
72+
# the branch was updated. To keep PSR working with the configured release branches,
73+
# we force a checkout of the desired release branch but at the workflow sha HEAD.
74+
- name: Setup | Checkout Repository at workflow sha
75+
uses: actions/checkout@v4
76+
with:
77+
fetch-depth: 0
78+
ref: ${{ github.sha }}
79+
80+
- name: Setup | Force correct release branch on workflow sha
81+
run: |
82+
git checkout -B ${{ github.ref_name }} ${{ github.sha }}
83+
84+
- name: Action | Semantic Version Release
85+
id: release
86+
# Adjust tag with desired version if applicable.
87+
uses: python-semantic-release/python-semantic-release@v9.8.1
88+
with:
89+
github_token: ${{ secrets.GITHUB_TOKEN }}
90+
git_committer_name: "github-actions"
91+
git_committer_email: "actions@users.noreply.github.com"
92+
93+
- name: Publish | Upload to GitHub Release Assets
94+
uses: python-semantic-release/publish-action@v9.16.1
95+
if: steps.release.outputs.released == 'true'
96+
with:
97+
github_token: ${{ secrets.GITHUB_TOKEN }}
98+
tag: ${{ steps.release.outputs.tag }}
99+
100+
- name: Docker meta
101+
id: docker_meta
102+
if: steps.release.outputs.released == 'true'
103+
uses: docker/metadata-action@v5
104+
with:
105+
images: |
106+
ghcr.io/wipacrepo/oauth2-proxy
107+
tags: |
108+
type=semver,pattern={{major}},value=${{ steps.release.outputs.tag }}
109+
type=semver,pattern={{major}}.{{minor}},value=${{ steps.release.outputs.tag }}
110+
type=semver,pattern={{major}}.{{minor}}.{{patch}},value=${{ steps.release.outputs.tag }}
111+
- name: Login to GitHub Container Registry
112+
if: steps.release.outputs.released == 'true'
113+
uses: docker/login-action@v3
114+
with:
115+
registry: ghcr.io
116+
username: ${{ github.actor }}
117+
password: ${{ secrets.GITHUB_TOKEN }}
118+
- name: Push Docker Image
119+
if: steps.release.outputs.released == 'true'
120+
uses: docker/build-push-action@v6
121+
with:
122+
context: .
123+
tags: ${{ steps.docker_meta.outputs.tags }}
124+
labels: ${{ steps.docker_meta.outputs.labels }}
125+
push: true

Dockerfile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
FROM python:3.13
2+
3+
RUN groupadd -g 1000 app && useradd -m -g 1000 -u 1000 app
4+
5+
RUN mkdir /app
6+
WORKDIR /app
7+
8+
COPY src /app/src
9+
COPY pyproject.toml /app/pyproject.toml
10+
11+
RUN chown -R app:app /app
12+
13+
USER app
14+
15+
ENV VIRTUAL_ENV=/app/venv
16+
17+
RUN python3.13 -m venv $VIRTUAL_ENV
18+
19+
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
20+
21+
RUN --mount=source=.git,target=.git,type=bind pip install -e .
22+
23+
CMD ["python", "-m", "oauth2_proxy"]

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,31 @@
11
# oauth2-proxy
22
Python version of oauth2-proxy
3+
4+
A very limited version of [https://github.com/oauth2-proxy/oauth2-proxy](https://github.com/oauth2-proxy/oauth2-proxy)
5+
written in Python. Primarily meant for debugging, as it gives much more detailed logging.
6+
7+
## Basic Config
8+
9+
Primary env variables:
10+
11+
* OPENID_CLIENT_ID
12+
* OPENID_CLIENT_SECRET
13+
* OPENID_URL
14+
* OPENID_SCOPES
15+
* OPENID_AUDIENCE
16+
17+
Some host config using env variables:
18+
19+
* FULL_URL - external base url, for redirects to work
20+
* HOST - bind host, set to an empty string for all interfaces
21+
* PORT - bind port
22+
23+
## API Routes
24+
25+
Handles API routes (returning 401 instead of a login redirect) using env variables:
26+
27+
* API_ROUTES - string separated regexp patterns
28+
* API_TOKEN_LEEWAY - extra leeway for validating token expiration
29+
30+
`API_TOKEN_LEEWAY` is usually unnecessary, but may be useful in an environment
31+
with clock skew between the OpenID server and this server.

pyproject.toml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
[build-system]
2+
requires = ["setuptools>=64", "setuptools_scm[toml]>=3.4"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "oauth2-proxy"
7+
description = "WIPAC - oauth2 proxy"
8+
readme = "README.md"
9+
authors = [{name = "WIPAC Developers", email = "developers@icecube.wisc.edu"}]
10+
keywords = ["transfer"]
11+
license = {text = "MIT License"}
12+
classifiers = [
13+
"Development Status :: 5 - Production/Stable",
14+
"Environment :: Console",
15+
"License :: OSI Approved :: MIT License",
16+
"Operating System :: OS Independent",
17+
"Programming Language :: Python :: 3",
18+
"Topic :: Software Development :: Libraries :: Python Modules",
19+
]
20+
requires-python = ">=3.12"
21+
dependencies = [
22+
"wipac-dev-tools",
23+
"wipac-rest-tools",
24+
]
25+
dynamic = ["version"]
26+
27+
[project.optional-dependencies]
28+
tests = ["mypy", "pytest", "pytest-async"]
29+
30+
[project.urls]
31+
repository = "https://github.com/WIPACrepo/oauth2-proxy"
32+
33+
[tool.setuptools]
34+
include-package-data = true
35+
license-files = ["LICENSE"]
36+
37+
[tool.setuptools.packages.find]
38+
where = ["src"]
39+
include = ["oauth2*"]
40+
namespaces = true
41+
42+
[tool.setuptools_scm]
43+
write_to = "src/oauth2_proxy/version.py"
44+
45+
[tool.ruff]
46+
exclude = ["env", "*tests", "bin", "docs", "resources"]
47+
target-version = "py313"
48+
49+
[tool.ruff.lint]
50+
ignore = ["E203", "E226", "E228", "E231", "E501"]
51+
52+
[tool.pytest.ini_options]
53+
asyncio_mode = "auto"
54+
55+
[tool.semantic_release]
56+
tag_format = "{version}"
57+
commit_parser = "emoji"
58+
59+
[tool.semantic_release.commit_parser_options]
60+
major_tags = ["[major]"]
61+
minor_tags = ["[minor]", "[feature]"]
62+
patch_tags = ["[patch]", "[fix]", " ", "!", "#", "$", "%", "&", "'", "(", ")", "*", "+", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~"]

setup.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env python3
2+
from setuptools import setup
3+
setup()

setupenv.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/sh
2+
unset PYTHONPATH
3+
python3 -m virtualenv -p python3 env
4+
echo "unset PYTHONPATH" >> env/bin/activate
5+
. env/bin/activate
6+
pip install -e .[tests]

src/oauth2_proxy/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
try:
2+
from .version import __version__
3+
except ImportError:
4+
# package is not installed
5+
__version__ = None

src/oauth2_proxy/__main__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import asyncio
2+
3+
from .config import config_logging
4+
from .server import Server
5+
6+
7+
config_logging()
8+
9+
10+
# start server
11+
async def main():
12+
s = Server()
13+
await s.start()
14+
try:
15+
await asyncio.Event().wait()
16+
finally:
17+
await s.stop()
18+
19+
asyncio.run(main())

src/oauth2_proxy/config.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import dataclasses as dc
2+
import logging
3+
4+
from wipac_dev_tools import from_environment_as_dataclass
5+
6+
@dc.dataclass(frozen=True)
7+
class EnvConfig:
8+
OPENID_CLIENT_ID: str
9+
OPENID_CLIENT_SECRET: str = ''
10+
OPENID_URL: str = 'https://keycloak.icecube.wisc.edu/auth/realms/IceCube'
11+
OPENID_SCOPES: str = ''
12+
OPENID_AUDIENCE: str = ''
13+
14+
API_ROUTES: str = ''
15+
API_TOKEN_LEEWAY: int = 60
16+
17+
HOST: str = 'localhost'
18+
PORT: int = 8080
19+
FULL_URL: str = 'http://localhost:8080'
20+
COOKIE_SECRET: str = '00000000'
21+
DEBUG: bool = False
22+
23+
CI_TEST: bool = False
24+
LOG_LEVEL: str = 'INFO'
25+
26+
def __post_init__(self) -> None:
27+
object.__setattr__(self, 'LOG_LEVEL', self.LOG_LEVEL.upper()) # b/c frozen
28+
29+
ENV = from_environment_as_dataclass(EnvConfig, collection_sep=',')
30+
31+
32+
def config_logging():
33+
# handle logging
34+
setlevel = {
35+
'CRITICAL': logging.CRITICAL, # execution cannot continue
36+
'FATAL': logging.CRITICAL,
37+
'ERROR': logging.ERROR, # something is wrong, but try to continue
38+
'WARNING': logging.WARNING, # non-ideal behavior, important event
39+
'WARN': logging.WARNING,
40+
'INFO': logging.INFO, # initial debug information
41+
'DEBUG': logging.DEBUG # the things no one wants to see
42+
}
43+
44+
if ENV.LOG_LEVEL not in setlevel:
45+
raise Exception('LOG_LEVEL is not a proper log level')
46+
logformat = '%(asctime)s %(levelname)s %(name)s %(module)s:%(lineno)s - %(message)s'
47+
logging.basicConfig(format=logformat, level=setlevel[ENV.LOG_LEVEL])

0 commit comments

Comments
 (0)