mirror of
https://github.com/goauthentik/authentik
synced 2026-05-06 07:02:51 +02:00
Compare commits
59 Commits
rust-proxy
...
version-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e095e9f694 | ||
|
|
10e311534f | ||
|
|
46fdb45273 | ||
|
|
6d4125cb90 | ||
|
|
bc83176962 | ||
|
|
0fa8432b72 | ||
|
|
bb9a524b53 | ||
|
|
d31c05625b | ||
|
|
399223b770 | ||
|
|
19197d3f9b | ||
|
|
1cd000dfe2 | ||
|
|
00ae97944a | ||
|
|
9f3ccfb7c7 | ||
|
|
9ed9c39ac8 | ||
|
|
30b6eeee9f | ||
|
|
afe2621783 | ||
|
|
8b12c6a01a | ||
|
|
f63adfed96 | ||
|
|
9c8fec21cf | ||
|
|
4776d2bcc5 | ||
|
|
a15a040362 | ||
|
|
fcd6dc1d60 | ||
|
|
acc3b59869 | ||
|
|
d9d5ac10e6 | ||
|
|
750669dcab | ||
|
|
88a3eed67e | ||
|
|
6c214fffc4 | ||
|
|
70100fc105 | ||
|
|
3c1163fabd | ||
|
|
539e8242ff | ||
|
|
2648333590 | ||
|
|
fe828ef993 | ||
|
|
29a6530742 | ||
|
|
a6b9274c4f | ||
|
|
a2a67161ac | ||
|
|
2e8263a99b | ||
|
|
6b9afed21f | ||
|
|
1eb1f4e0b8 | ||
|
|
7c3d60ec3a | ||
|
|
a494c6b6e8 | ||
|
|
6604d3577f | ||
|
|
f8bfa7e16a | ||
|
|
ea6cf6eabf | ||
|
|
769ce3ce7b | ||
|
|
3891fb3fa8 | ||
|
|
41eb965350 | ||
|
|
8d95612287 | ||
|
|
82b5274b15 | ||
|
|
af56ce3d78 | ||
|
|
f5c6e7aeb0 | ||
|
|
3809400e93 | ||
|
|
1def9865cf | ||
|
|
3716298639 | ||
|
|
c16317d7cf | ||
|
|
bbb8fa8269 | ||
|
|
e4c251a178 | ||
|
|
0fefd5f522 | ||
|
|
88057db0b0 | ||
|
|
91cb6c9beb |
20
.bumpversion.cfg
Normal file
20
.bumpversion.cfg
Normal file
@@ -0,0 +1,20 @@
|
||||
[bumpversion]
|
||||
current_version = 2023.10.7
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
|
||||
serialize = {major}.{minor}.{patch}
|
||||
message = release: {new_version}
|
||||
tag_name = version/{new_version}
|
||||
|
||||
[bumpversion:file:pyproject.toml]
|
||||
|
||||
[bumpversion:file:docker-compose.yml]
|
||||
|
||||
[bumpversion:file:schema.yml]
|
||||
|
||||
[bumpversion:file:authentik/__init__.py]
|
||||
|
||||
[bumpversion:file:internal/constants/constants.go]
|
||||
|
||||
[bumpversion:file:web/src/common/constants.ts]
|
||||
@@ -1,5 +0,0 @@
|
||||
[alias]
|
||||
t = ["nextest", "run", "--workspace"]
|
||||
|
||||
[build]
|
||||
rustflags = ["--cfg", "tokio_unstable"]
|
||||
@@ -1,47 +0,0 @@
|
||||
[licenses]
|
||||
allow = [
|
||||
"Apache-2.0 WITH LLVM-exception",
|
||||
"Apache-2.0",
|
||||
"BSD-3-Clause",
|
||||
"CC0-1.0",
|
||||
"CDLA-Permissive-2.0",
|
||||
"ISC",
|
||||
"MIT",
|
||||
"MPL-2.0",
|
||||
"OpenSSL",
|
||||
"Unicode-3.0",
|
||||
"Zlib",
|
||||
]
|
||||
|
||||
[licenses.private]
|
||||
ignore = true
|
||||
|
||||
[bans]
|
||||
multiple-versions = "allow"
|
||||
wildcards = "deny"
|
||||
[bans.workspace-dependencies]
|
||||
duplicates = "deny"
|
||||
include-path-dependencies = true
|
||||
unused = "deny"
|
||||
|
||||
# No non-FIPS compliant dependencies
|
||||
[[bans.deny]]
|
||||
name = "native-tls"
|
||||
[[bans.deny]]
|
||||
name = "openssl"
|
||||
[[bans.deny]]
|
||||
name = "openssl-sys"
|
||||
[[bans.deny]]
|
||||
name = "ring"
|
||||
[[bans.features]]
|
||||
allow = [
|
||||
"alloc",
|
||||
"aws-lc-sys",
|
||||
"default",
|
||||
"fips",
|
||||
"prebuilt-nasm",
|
||||
"ring-io",
|
||||
"ring-sig-verify",
|
||||
]
|
||||
name = "aws-lc-rs"
|
||||
exact = true
|
||||
@@ -1,15 +0,0 @@
|
||||
comment_width = 100
|
||||
format_code_in_doc_comments = true
|
||||
format_strings = true
|
||||
group_imports = "StdExternalCrate"
|
||||
hex_literal_case = "Lower"
|
||||
imports_granularity = "Crate"
|
||||
max_width = 100
|
||||
newline_style = "Unix"
|
||||
normalize_comments = true
|
||||
normalize_doc_attributes = true
|
||||
reorder_impl_items = true
|
||||
style_edition = "2024"
|
||||
use_field_init_shorthand = true
|
||||
use_try_shorthand = true
|
||||
wrap_comments = true
|
||||
@@ -1,13 +1,11 @@
|
||||
htmlcov
|
||||
*.env.yml
|
||||
node_modules
|
||||
**/node_modules
|
||||
dist/**
|
||||
build/**
|
||||
build_docs/**
|
||||
*Dockerfile
|
||||
**/*Dockerfile
|
||||
blueprints/local
|
||||
.git
|
||||
.venv
|
||||
target
|
||||
!gen-ts-api/node_modules
|
||||
!gen-ts-api/dist/**
|
||||
|
||||
@@ -7,9 +7,6 @@ charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.toml]
|
||||
indent_size = 2
|
||||
|
||||
[*.html]
|
||||
indent_size = 2
|
||||
|
||||
|
||||
9
.gitattributes
vendored
9
.gitattributes
vendored
@@ -1,9 +0,0 @@
|
||||
packages/client-*/** linguist-generated
|
||||
web/packages/lex/* linguist-vendored
|
||||
web/packages/node-domexception/* linguist-vendored
|
||||
web/packages/formdata-polyfill/* linguist-vendored
|
||||
web/packages/sfe/vendored/* linguist-vendored
|
||||
website/vendored/* linguist-vendored
|
||||
website/docs/** linguist-documentation
|
||||
website/integrations/** linguist-documentation
|
||||
website/api/** linguist-documentation
|
||||
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -1 +1 @@
|
||||
custom: https://goauthentik.io/pricing/
|
||||
github: [BeryJu]
|
||||
|
||||
81
.github/ISSUE_TEMPLATE/1-bug-report.yml
vendored
81
.github/ISSUE_TEMPLATE/1-bug-report.yml
vendored
@@ -1,81 +0,0 @@
|
||||
name: Bug report
|
||||
description: Create a report to help us improve
|
||||
labels: ["bug", "triage"]
|
||||
type: bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to fill out this bug report!
|
||||
- type: textarea
|
||||
id: describe-the-bug
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: "A clear and concise description of what the bug is."
|
||||
placeholder: "Describe the issue"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: how-to-reproduce
|
||||
attributes:
|
||||
label: How to reproduce
|
||||
description: "Steps to reproduce the behavior."
|
||||
placeholder: |
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected-behavior
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: "A clear and concise description of what you expected to happen."
|
||||
placeholder: "The behavior that I expect to see is [...]"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: screenshots
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: "If applicable, add screenshots to help explain your problem."
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: "Add any other context about the problem here."
|
||||
placeholder: "Also note that [...]"
|
||||
validations:
|
||||
required: false
|
||||
- type: dropdown
|
||||
id: deployment-method
|
||||
attributes:
|
||||
label: Deployment Method
|
||||
description: "What deployment method are you using for authentik? Only Docker, Kubernetes and AWS CloudFormation are supported."
|
||||
options:
|
||||
- Docker
|
||||
- Kubernetes
|
||||
- AWS CloudFormation
|
||||
- Other (please specify)
|
||||
default: 0
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: "What version of authentik are you using?"
|
||||
placeholder: "[e.g. 2025.10.1]"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: "Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks."
|
||||
render: shell
|
||||
validations:
|
||||
required: false
|
||||
49
.github/ISSUE_TEMPLATE/2-docs-issue.yml
vendored
49
.github/ISSUE_TEMPLATE/2-docs-issue.yml
vendored
@@ -1,49 +0,0 @@
|
||||
name: Documentation suggestion/problem
|
||||
description: Suggest an improvement or report a problem in our docs
|
||||
labels: ["area: docs", "triage"]
|
||||
type: task
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to fill out this documentation issue!
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
|
||||
**Consider opening a PR!**
|
||||
If the issue is one that you can fix, or even make a good pass at, we'd appreciate a PR.
|
||||
|
||||
For more information about making a contribution to the docs, and using our Style Guide and our templates, refer to ["Writing documentation"](https://docs.goauthentik.io/docs/developer-docs/docs/writing-documentation).
|
||||
- type: textarea
|
||||
id: issue
|
||||
attributes:
|
||||
label: Do you see an area that can be clarified or expanded, a technical inaccuracy, or a broken link?
|
||||
description: "A clear and concise description of what the problem is, or where the document can be improved."
|
||||
placeholder: "I believe we need more details about [...]"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: link
|
||||
attributes:
|
||||
label: Link
|
||||
description: "Provide the URL or link to the exact page in the documentation to which you are referring."
|
||||
placeholder: "If there are multiple pages, list them all"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: Solution
|
||||
description: "A clear and concise description of what you suggest as a solution"
|
||||
placeholder: "This issue could be resolved by [...]"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: "Add any other context or screenshots about the documentation issue here."
|
||||
placeholder: "Also note that [...]"
|
||||
validations:
|
||||
required: false
|
||||
41
.github/ISSUE_TEMPLATE/3-feature-request.yml
vendored
41
.github/ISSUE_TEMPLATE/3-feature-request.yml
vendored
@@ -1,41 +0,0 @@
|
||||
name: Feature request
|
||||
description: Suggest an idea for a feature
|
||||
labels: ["enhancement", "triage"]
|
||||
type: feature
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to fill out this feature request!
|
||||
- type: textarea
|
||||
id: related-to-problem
|
||||
attributes:
|
||||
label: Is your feature request related to a problem?
|
||||
description: "A clear and concise description of what the problem is."
|
||||
placeholder: "I'm always frustrated when [...]"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: feature
|
||||
attributes:
|
||||
label: Describe the solution you'd like
|
||||
description: A clear and concise description of what you want to happen.
|
||||
placeholder: "I'd like authentik to have [...]"
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Describe alternatives that you've considered
|
||||
description: "A clear and concise description of any alternative solutions or features you've considered."
|
||||
placeholder: "I've tried this but [...]"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: "Add any other context or screenshots about the feature request here."
|
||||
placeholder: "Also note that [...]"
|
||||
validations:
|
||||
required: false
|
||||
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ""
|
||||
labels: bug
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Logs**
|
||||
Output of docker-compose logs or kubectl logs respectively
|
||||
|
||||
**Version and Deployment (please complete the following information):**
|
||||
|
||||
- authentik version: [e.g. 2021.8.5]
|
||||
- Deployment: [e.g. docker-compose, helm]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Question
|
||||
url: https://github.com/goauthentik/authentik/discussions
|
||||
about: Please ask questions via GitHub Discussions rather than creating issues.
|
||||
- name: authentik Discord
|
||||
url: https://discord.com/invite/jg33eMhnj6
|
||||
about: For community support, visit our Discord server.
|
||||
19
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
19
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ""
|
||||
labels: enhancement
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
17
.github/ISSUE_TEMPLATE/hackathon_idea.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/hackathon_idea.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Hackathon Idea
|
||||
about: Propose an idea for the hackathon
|
||||
title: ""
|
||||
labels: hackathon
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
**Describe the idea**
|
||||
|
||||
A clear concise description of the idea you want to implement
|
||||
|
||||
You're also free to work on existing GitHub issues, whether they be feature requests or bugs, just link the existing GitHub issue here.
|
||||
|
||||
<!-- Don't modify below here -->
|
||||
|
||||
If you want to help working on this idea or want to contribute in any other way, react to this issue with a :rocket:
|
||||
7
.github/ISSUE_TEMPLATE/issue_template.md
vendored
7
.github/ISSUE_TEMPLATE/issue_template.md
vendored
@@ -1,7 +0,0 @@
|
||||
---
|
||||
name: Blank issue
|
||||
about: This issue type is only for internal use
|
||||
title:
|
||||
labels:
|
||||
assignees:
|
||||
---
|
||||
27
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask a question about a feature or specific configuration
|
||||
title: ""
|
||||
labels: question
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
**Describe your question/**
|
||||
A clear and concise description of what you're trying to do.
|
||||
|
||||
**Relevant infos**
|
||||
i.e. Version of other software you're using, specifics of your setup
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Logs**
|
||||
Output of docker-compose logs or kubectl logs respectively
|
||||
|
||||
**Version and Deployment (please complete the following information):**
|
||||
|
||||
- authentik version: [e.g. 2021.8.5]
|
||||
- Deployment: [e.g. docker-compose, helm]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
266
.github/actions/cherry-pick/action.yml
vendored
266
.github/actions/cherry-pick/action.yml
vendored
@@ -1,266 +0,0 @@
|
||||
name: "Cherry-picker"
|
||||
description: "Cherry-pick PRs based on their labels"
|
||||
|
||||
inputs:
|
||||
token:
|
||||
description: "GitHub Token"
|
||||
required: true
|
||||
git_user:
|
||||
description: "Git user for pushing the cherry-pick PR"
|
||||
required: true
|
||||
git_user_email:
|
||||
description: "Git user email for pushing the cherry-pick PR"
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Check if workflow should run
|
||||
id: should_run
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ inputs.token }}
|
||||
run: |
|
||||
set -e -o pipefail
|
||||
# For issues events, check if it's actually a PR
|
||||
if [ "${{ github.event_name }}" = "issues" ]; then
|
||||
# Check if this issue is actually a PR
|
||||
PR_DATA=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }} 2>/dev/null || echo "null")
|
||||
if [ "$PR_DATA" = "null" ]; then
|
||||
echo "should_run=false" >> $GITHUB_OUTPUT
|
||||
echo "reason=not_a_pr" >> $GITHUB_OUTPUT
|
||||
echo "This is an issue, not a PR. Skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get PR data
|
||||
PR_MERGED=$(echo "$PR_DATA" | jq -r '.merged')
|
||||
PR_NUMBER="${{ github.event.issue.number }}"
|
||||
MERGE_COMMIT_SHA=$(echo "$PR_DATA" | jq -r '.merge_commit_sha')
|
||||
|
||||
# Check if it's a backport label
|
||||
LABEL_NAME="${{ github.event.label.name }}"
|
||||
if [[ "$LABEL_NAME" =~ ^backport/(.+)$ ]]; then
|
||||
if [ "$PR_MERGED" = "true" ]; then
|
||||
echo "should_run=true" >> $GITHUB_OUTPUT
|
||||
echo "reason=label_added_to_merged_pr" >> $GITHUB_OUTPUT
|
||||
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
|
||||
echo "merge_commit_sha=$MERGE_COMMIT_SHA" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
else
|
||||
echo "should_run=false" >> $GITHUB_OUTPUT
|
||||
echo "reason=label_added_to_open_pr" >> $GITHUB_OUTPUT
|
||||
echo "Backport label added to open PR. Will run after PR is merged."
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
echo "should_run=false" >> $GITHUB_OUTPUT
|
||||
echo "reason=non_backport_label" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# For pull_request and pull_request_target events
|
||||
PR_NUMBER="${{ github.event.pull_request.number }}"
|
||||
MERGE_COMMIT_SHA="${{ github.event.pull_request.merge_commit_sha }}"
|
||||
|
||||
# Case 1: PR was just merged (closed + merged = true)
|
||||
if [ "${{ github.event.action }}" = "closed" ] && [ "${{ github.event.pull_request.merged }}" = "true" ]; then
|
||||
echo "should_run=true" >> $GITHUB_OUTPUT
|
||||
echo "reason=pr_merged" >> $GITHUB_OUTPUT
|
||||
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
|
||||
echo "merge_commit_sha=$MERGE_COMMIT_SHA" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Case 2: Label was added
|
||||
if [ "${{ github.event.action }}" = "labeled" ]; then
|
||||
LABEL_NAME="${{ github.event.label.name }}"
|
||||
# Check if it's a backport label
|
||||
if [[ "$LABEL_NAME" =~ ^backport/(.+)$ ]]; then
|
||||
# Check if PR is already merged
|
||||
if [ "${{ github.event.pull_request.merged }}" = "true" ]; then
|
||||
echo "should_run=true" >> $GITHUB_OUTPUT
|
||||
echo "reason=label_added_to_merged_pr" >> $GITHUB_OUTPUT
|
||||
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
|
||||
echo "merge_commit_sha=$MERGE_COMMIT_SHA" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
else
|
||||
echo "should_run=false" >> $GITHUB_OUTPUT
|
||||
echo "reason=label_added_to_open_pr" >> $GITHUB_OUTPUT
|
||||
echo "Backport label added to open PR. Will run after PR is merged."
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
echo "should_run=false" >> $GITHUB_OUTPUT
|
||||
echo "reason=non_backport_label" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "should_run=false" >> $GITHUB_OUTPUT
|
||||
echo "reason=unknown" >> $GITHUB_OUTPUT
|
||||
- name: Configure Git
|
||||
if: steps.should_run.outputs.should_run == 'true'
|
||||
shell: bash
|
||||
env:
|
||||
user: ${{ inputs.git_user }}
|
||||
email: ${{ inputs.git_user_email }}
|
||||
run: |
|
||||
git config --global user.name "${user}"
|
||||
git config --global user.email "${email}"
|
||||
- name: Get PR details and extract backport labels
|
||||
if: steps.should_run.outputs.should_run == 'true'
|
||||
id: pr_details
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ inputs.token }}
|
||||
PR_NUMBER: ${{ steps.should_run.outputs.pr_number }}
|
||||
REASON: ${{ steps.should_run.outputs.reason }}
|
||||
run: |
|
||||
set -e -o pipefail
|
||||
|
||||
# Determine which labels to process
|
||||
if [ "${REASON}" = "label_added_to_merged_pr" ]; then
|
||||
# Only process the specific label that was just added
|
||||
if [ "${{ github.event_name }}" = "issues" ]; then
|
||||
LABEL_NAME="${{ github.event.label.name }}"
|
||||
else
|
||||
LABEL_NAME="${{ github.event.label.name }}"
|
||||
fi
|
||||
|
||||
if [[ "$LABEL_NAME" =~ ^backport/(.+)$ ]]; then
|
||||
echo "labels=$LABEL_NAME" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Label $LABEL_NAME does not match backport pattern"
|
||||
echo "labels=" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
# PR was just merged, process all backport labels
|
||||
LABELS=$(gh pr view $PR_NUMBER --json labels --jq '.labels[].name' | grep '^backport/' | tr '\n' ' ' || true)
|
||||
echo "labels=$LABELS" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Cherry-pick to target branches
|
||||
if: steps.should_run.outputs.should_run == 'true' && steps.pr_details.outputs.labels != ''
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ inputs.token }}
|
||||
PR_NUMBER: '${{ steps.should_run.outputs.pr_number }}'
|
||||
COMMIT_SHA: '${{ steps.should_run.outputs.merge_commit_sha }}'
|
||||
PR_TITLE: ${{ github.event.pull_request.title }}
|
||||
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
|
||||
LABELS: '${{ steps.pr_details.outputs.labels }}'
|
||||
run: |
|
||||
set -e -o pipefail
|
||||
|
||||
echo "Processing PR #$PR_NUMBER (reason: ${{ steps.should_run.outputs.reason }})"
|
||||
echo "Found backport labels: $LABELS"
|
||||
|
||||
# Process each backport label
|
||||
for label in $LABELS; do
|
||||
if [[ "$label" =~ ^backport/(.+)$ ]]; then
|
||||
TARGET_BRANCH="${BASH_REMATCH[1]}"
|
||||
echo "Processing backport to branch: $TARGET_BRANCH"
|
||||
|
||||
# Check if target branch exists
|
||||
if ! git ls-remote --heads origin "$TARGET_BRANCH" | grep -q "$TARGET_BRANCH"; then
|
||||
echo "❌ Target branch $TARGET_BRANCH does not exist, skipping"
|
||||
|
||||
# Comment on the original PR about the missing branch
|
||||
gh pr comment $PR_NUMBER --body "⚠️ Cannot backport to \`$TARGET_BRANCH\`: branch does not exist."
|
||||
continue
|
||||
fi
|
||||
|
||||
# Create a unique branch name for the cherry-pick
|
||||
CHERRY_PICK_BRANCH="cherry-pick/${PR_NUMBER}-to-${TARGET_BRANCH}"
|
||||
|
||||
# Check if a cherry-pick PR already exists
|
||||
EXISTING_PR=$(gh pr list --head "$CHERRY_PICK_BRANCH" --json number --jq '.[0].number' 2>/dev/null || echo "")
|
||||
if [ -n "$EXISTING_PR" ]; then
|
||||
echo "⚠️ Cherry-pick PR already exists: #$EXISTING_PR"
|
||||
gh pr comment $PR_NUMBER --body "Cherry-pick to \`$TARGET_BRANCH\` already exists: #$EXISTING_PR"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Fetch and checkout target branch
|
||||
git fetch origin "$TARGET_BRANCH"
|
||||
git checkout -b "$CHERRY_PICK_BRANCH" "origin/$TARGET_BRANCH"
|
||||
|
||||
# Attempt cherry-pick
|
||||
if git cherry-pick "$COMMIT_SHA"; then
|
||||
echo "✅ Cherry-pick successful for $TARGET_BRANCH"
|
||||
|
||||
# Push the cherry-pick branch
|
||||
git push origin "$CHERRY_PICK_BRANCH"
|
||||
|
||||
# Create PR for the cherry-pick
|
||||
CHERRY_PICK_TITLE="$PR_TITLE (cherry-pick #$PR_NUMBER to $TARGET_BRANCH)"
|
||||
CHERRY_PICK_BODY="Cherry-pick of #$PR_NUMBER to \`$TARGET_BRANCH\` branch.
|
||||
|
||||
**Original PR:** #$PR_NUMBER
|
||||
**Original Author:** @$PR_AUTHOR
|
||||
**Cherry-picked commit:** $COMMIT_SHA"
|
||||
|
||||
NEW_PR=$(gh pr create \
|
||||
--title "$CHERRY_PICK_TITLE" \
|
||||
--body "$CHERRY_PICK_BODY" \
|
||||
--base "$TARGET_BRANCH" \
|
||||
--head "$CHERRY_PICK_BRANCH" \
|
||||
--label "cherry-pick")
|
||||
|
||||
# Assign the PR to the original author
|
||||
gh pr edit "$NEW_PR" --add-assignee "$PR_AUTHOR" || true
|
||||
|
||||
echo "✅ Created cherry-pick PR $NEW_PR for $TARGET_BRANCH"
|
||||
|
||||
# Comment on original PR
|
||||
gh pr comment $PR_NUMBER --body "🍒 Cherry-pick to \`$TARGET_BRANCH\` created: $NEW_PR"
|
||||
|
||||
else
|
||||
echo "⚠️ Cherry-pick failed for $TARGET_BRANCH, creating conflict resolution PR"
|
||||
|
||||
# Add conflicted files and commit
|
||||
git add .
|
||||
git commit -m "Cherry-pick #$PR_NUMBER to $TARGET_BRANCH (with conflicts)
|
||||
|
||||
This cherry-pick has conflicts that need manual resolution.
|
||||
|
||||
Original PR: #$PR_NUMBER
|
||||
Original commit: $COMMIT_SHA"
|
||||
|
||||
# Push the branch with conflicts
|
||||
git push origin "$CHERRY_PICK_BRANCH"
|
||||
|
||||
# Create PR with conflict notice
|
||||
CONFLICT_TITLE="$PR_TITLE (cherry-pick #$PR_NUMBER to $TARGET_BRANCH)"
|
||||
CONFLICT_BODY="⚠️ **This cherry-pick has conflicts that require manual resolution.**
|
||||
|
||||
Cherry-pick of #$PR_NUMBER to \`$TARGET_BRANCH\` branch.
|
||||
|
||||
**Original PR:** #$PR_NUMBER
|
||||
**Original Author:** @$PR_AUTHOR
|
||||
**Cherry-picked commit:** $COMMIT_SHA
|
||||
|
||||
**Please resolve the conflicts in this PR before merging.**"
|
||||
|
||||
NEW_PR=$(gh pr create \
|
||||
--title "$CONFLICT_TITLE" \
|
||||
--body "$CONFLICT_BODY" \
|
||||
--base "$TARGET_BRANCH" \
|
||||
--head "$CHERRY_PICK_BRANCH" \
|
||||
--label "cherry-pick")
|
||||
|
||||
# Assign the PR to the original author
|
||||
gh pr edit "$NEW_PR" --add-assignee "$PR_AUTHOR" || true
|
||||
|
||||
echo "⚠️ Created conflict resolution PR $NEW_PR for $TARGET_BRANCH"
|
||||
|
||||
# Comment on original PR
|
||||
gh pr comment $PR_NUMBER --body "⚠️ Cherry-pick to \`$TARGET_BRANCH\` has conflicts: $NEW_PR"
|
||||
fi
|
||||
|
||||
# Clean up - go back to main branch
|
||||
git checkout main
|
||||
git branch -D "$CHERRY_PICK_BRANCH" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
@@ -9,15 +9,18 @@ inputs:
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Generate config
|
||||
id: ev
|
||||
uses: ./.github/actions/docker-push-variables
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v2
|
||||
uses: peter-evans/find-comment@v2
|
||||
id: fc
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: "github-actions[bot]"
|
||||
body-includes: authentik PR Installation instructions
|
||||
- name: Create or update comment
|
||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v2
|
||||
uses: peter-evans/create-or-update-comment@v2
|
||||
with:
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
@@ -35,6 +38,14 @@ runs:
|
||||
AUTHENTIK_OUTPOSTS__CONTAINER_IMAGE_BASE=ghcr.io/goauthentik/dev-%(type)s:gh-%(build_hash)s
|
||||
```
|
||||
|
||||
For arm64, use these values:
|
||||
|
||||
```shell
|
||||
AUTHENTIK_IMAGE=ghcr.io/goauthentik/dev-server
|
||||
AUTHENTIK_TAG=${{ inputs.tag }}-arm64
|
||||
AUTHENTIK_OUTPOSTS__CONTAINER_IMAGE_BASE=ghcr.io/goauthentik/dev-%(type)s:gh-%(build_hash)s
|
||||
```
|
||||
|
||||
Afterwards, run the upgrade commands from the latest release notes.
|
||||
</details>
|
||||
<details>
|
||||
@@ -46,10 +57,20 @@ runs:
|
||||
authentik:
|
||||
outposts:
|
||||
container_image_base: ghcr.io/goauthentik/dev-%(type)s:gh-%(build_hash)s
|
||||
global:
|
||||
image:
|
||||
repository: ghcr.io/goauthentik/dev-server
|
||||
tag: ${{ inputs.tag }}
|
||||
image:
|
||||
repository: ghcr.io/goauthentik/dev-server
|
||||
tag: ${{ inputs.tag }}
|
||||
```
|
||||
|
||||
For arm64, use these values:
|
||||
|
||||
```yaml
|
||||
authentik:
|
||||
outposts:
|
||||
container_image_base: ghcr.io/goauthentik/dev-%(type)s:gh-%(build_hash)s
|
||||
image:
|
||||
repository: ghcr.io/goauthentik/dev-server
|
||||
tag: ${{ inputs.tag }}-arm64
|
||||
```
|
||||
|
||||
Afterwards, run the upgrade commands from the latest release notes.
|
||||
|
||||
99
.github/actions/docker-push-variables/action.yml
vendored
99
.github/actions/docker-push-variables/action.yml
vendored
@@ -1,67 +1,64 @@
|
||||
---
|
||||
name: "Prepare docker environment variables"
|
||||
description: "Prepare docker environment variables"
|
||||
|
||||
inputs:
|
||||
image-name:
|
||||
required: true
|
||||
description: "Docker image prefix"
|
||||
image-arch:
|
||||
required: false
|
||||
description: "Docker image arch"
|
||||
release:
|
||||
required: true
|
||||
description: "True if this is a release build, false if this is a dev/PR build"
|
||||
|
||||
outputs:
|
||||
shouldPush:
|
||||
description: "Whether to push the image or not"
|
||||
value: ${{ steps.ev.outputs.shouldPush }}
|
||||
|
||||
shouldBuild:
|
||||
description: "Whether to build image or not"
|
||||
value: ${{ steps.ev.outputs.shouldBuild }}
|
||||
branchName:
|
||||
description: "Branch name"
|
||||
value: ${{ steps.ev.outputs.branchName }}
|
||||
branchNameContainer:
|
||||
description: "Branch name (for containers)"
|
||||
value: ${{ steps.ev.outputs.branchNameContainer }}
|
||||
timestamp:
|
||||
description: "Timestamp"
|
||||
value: ${{ steps.ev.outputs.timestamp }}
|
||||
sha:
|
||||
description: "sha"
|
||||
value: ${{ steps.ev.outputs.sha }}
|
||||
|
||||
shortHash:
|
||||
description: "shortHash"
|
||||
value: ${{ steps.ev.outputs.shortHash }}
|
||||
version:
|
||||
description: "Version"
|
||||
description: "version"
|
||||
value: ${{ steps.ev.outputs.version }}
|
||||
prerelease:
|
||||
description: "Prerelease"
|
||||
value: ${{ steps.ev.outputs.prerelease }}
|
||||
|
||||
imageTags:
|
||||
description: "Docker image tags"
|
||||
value: ${{ steps.ev.outputs.imageTags }}
|
||||
imageTagsJSON:
|
||||
description: "Docker image tags, as a JSON array"
|
||||
value: ${{ steps.ev.outputs.imageTagsJSON }}
|
||||
attestImageNames:
|
||||
description: "Docker image names used for attestation"
|
||||
value: ${{ steps.ev.outputs.attestImageNames }}
|
||||
cacheTo:
|
||||
description: "cache-to value for the docker build step"
|
||||
value: ${{ steps.ev.outputs.cacheTo }}
|
||||
imageMainTag:
|
||||
description: "Docker image main tag"
|
||||
value: ${{ steps.ev.outputs.imageMainTag }}
|
||||
imageMainName:
|
||||
description: "Docker image main name"
|
||||
value: ${{ steps.ev.outputs.imageMainName }}
|
||||
imageBuildArgs:
|
||||
description: "Docker image build args"
|
||||
value: ${{ steps.ev.outputs.imageBuildArgs }}
|
||||
versionFamily:
|
||||
description: "versionFamily"
|
||||
value: ${{ steps.ev.outputs.versionFamily }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Generate config
|
||||
id: ev
|
||||
shell: bash
|
||||
env:
|
||||
IMAGE_NAME: ${{ inputs.image-name }}
|
||||
IMAGE_ARCH: ${{ inputs.image-arch }}
|
||||
RELEASE: ${{ inputs.release }}
|
||||
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
REF: ${{ github.ref }}
|
||||
shell: python
|
||||
run: |
|
||||
python3 ${{ github.action_path }}/push_vars.py
|
||||
"""Helper script to get the actual branch name, docker safe"""
|
||||
import configparser
|
||||
import os
|
||||
from time import time
|
||||
|
||||
parser = configparser.ConfigParser()
|
||||
parser.read(".bumpversion.cfg")
|
||||
|
||||
branch_name = os.environ["GITHUB_REF"]
|
||||
if os.environ.get("GITHUB_HEAD_REF", "") != "":
|
||||
branch_name = os.environ["GITHUB_HEAD_REF"]
|
||||
|
||||
should_build = str(os.environ.get("DOCKER_USERNAME", "") != "").lower()
|
||||
version = parser.get("bumpversion", "current_version")
|
||||
version_family = ".".join(version.split(".")[:-1])
|
||||
safe_branch_name = branch_name.replace("refs/heads/", "").replace("/", "-")
|
||||
|
||||
sha = os.environ["GITHUB_SHA"] if not "${{ github.event.pull_request.head.sha }}" else "${{ github.event.pull_request.head.sha }}"
|
||||
|
||||
with open(os.environ["GITHUB_OUTPUT"], "a+", encoding="utf-8") as _output:
|
||||
print("branchName=%s" % branch_name, file=_output)
|
||||
print("branchNameContainer=%s" % safe_branch_name, file=_output)
|
||||
print("timestamp=%s" % int(time()), file=_output)
|
||||
print("sha=%s" % sha, file=_output)
|
||||
print("shortHash=%s" % sha[:7], file=_output)
|
||||
print("shouldBuild=%s" % should_build, file=_output)
|
||||
print("version=%s" % version, file=_output)
|
||||
print("versionFamily=%s" % version_family, file=_output)
|
||||
|
||||
122
.github/actions/docker-push-variables/push_vars.py
vendored
122
.github/actions/docker-push-variables/push_vars.py
vendored
@@ -1,122 +0,0 @@
|
||||
"""Helper script to get the actual branch name, docker safe"""
|
||||
|
||||
import os
|
||||
from json import dumps
|
||||
from pathlib import Path
|
||||
from sys import exit as sysexit
|
||||
from time import time
|
||||
from typing import Any
|
||||
|
||||
|
||||
def authentik_version() -> str:
|
||||
init = Path(__file__).parent.parent.parent.parent / "authentik" / "__init__.py"
|
||||
with open(init) as f:
|
||||
content = f.read()
|
||||
locals: dict[str, Any] = {}
|
||||
exec(content, None, locals) # nosec
|
||||
return str(locals["VERSION"])
|
||||
|
||||
|
||||
def must_or_fail(input: str | None, error: str) -> str:
|
||||
if not input:
|
||||
print(f"::error::{error}")
|
||||
sysexit(1)
|
||||
return input
|
||||
|
||||
|
||||
# Decide if we should push the image or not
|
||||
should_push = True
|
||||
if len(os.environ.get("DOCKER_USERNAME", "")) < 1:
|
||||
# Don't push if we don't have DOCKER_USERNAME, i.e. no secrets are available
|
||||
should_push = False
|
||||
if (
|
||||
must_or_fail(os.environ.get("GITHUB_REPOSITORY"), "Repo required").lower()
|
||||
== "goauthentik/authentik-internal"
|
||||
):
|
||||
# Don't push on the internal repo
|
||||
should_push = False
|
||||
|
||||
branch_name = os.environ["GITHUB_REF"]
|
||||
if os.environ.get("GITHUB_HEAD_REF", "") != "":
|
||||
branch_name = os.environ["GITHUB_HEAD_REF"]
|
||||
safe_branch_name = branch_name.replace("refs/heads/", "").replace("/", "-").replace("'", "-")
|
||||
|
||||
image_names = must_or_fail(os.getenv("IMAGE_NAME"), "Image name required").split(",")
|
||||
image_arch = os.getenv("IMAGE_ARCH") or None
|
||||
|
||||
is_pull_request = bool(os.getenv("PR_HEAD_SHA"))
|
||||
is_release = "dev" not in image_names[0]
|
||||
|
||||
sha = must_or_fail(
|
||||
os.environ["GITHUB_SHA"] if not is_pull_request else os.getenv("PR_HEAD_SHA"),
|
||||
"could not determine SHA",
|
||||
)
|
||||
|
||||
# 2042.1.0 or 2042.1.0-rc1
|
||||
version = authentik_version()
|
||||
# 2042.1
|
||||
version_family = ".".join(version.split("-", 1)[0].split(".")[:-1])
|
||||
prerelease = "-" in version
|
||||
|
||||
image_tags = []
|
||||
if is_release:
|
||||
for name in image_names:
|
||||
image_tags += [
|
||||
f"{name}:{version}",
|
||||
]
|
||||
if not prerelease:
|
||||
image_tags += [
|
||||
f"{name}:{version_family}",
|
||||
]
|
||||
else:
|
||||
suffix = ""
|
||||
if image_arch:
|
||||
suffix = f"-{image_arch}"
|
||||
for name in image_names:
|
||||
image_tags += [
|
||||
f"{name}:gh-{sha}{suffix}", # Used for ArgoCD and PR comments
|
||||
f"{name}:gh-{safe_branch_name}{suffix}", # For convenience
|
||||
f"{name}:gh-{safe_branch_name}-{int(time())}-{sha[:7]}{suffix}", # Use by FluxCD
|
||||
]
|
||||
|
||||
image_main_tag = image_tags[0].split(":")[-1]
|
||||
|
||||
|
||||
def get_attest_image_names(image_with_tags: list[str]) -> str:
|
||||
"""Attestation only for GHCR"""
|
||||
image_tags = []
|
||||
for image_name in set(name.split(":")[0] for name in image_with_tags):
|
||||
if not image_name.startswith("ghcr.io"):
|
||||
continue
|
||||
image_tags.append(image_name)
|
||||
return ",".join(set(image_tags))
|
||||
|
||||
|
||||
# Generate `cache-to` param
|
||||
cache_to = ""
|
||||
if should_push:
|
||||
_cache_tag = "buildcache"
|
||||
if image_arch:
|
||||
_cache_tag += f"-{image_arch}"
|
||||
cache_to = f"type=registry,ref={get_attest_image_names(image_tags)}:{_cache_tag},mode=max"
|
||||
|
||||
|
||||
image_build_args = []
|
||||
if os.getenv("RELEASE", "false").lower() == "true":
|
||||
image_build_args = [f"VERSION={os.getenv('REF')}"]
|
||||
else:
|
||||
image_build_args = [f"GIT_BUILD_HASH={sha}"]
|
||||
image_build_args_str = "\n".join(image_build_args)
|
||||
|
||||
with open(os.environ["GITHUB_OUTPUT"], "a+", encoding="utf-8") as _output:
|
||||
print(f"shouldPush={str(should_push).lower()}", file=_output)
|
||||
print(f"sha={sha}", file=_output)
|
||||
print(f"version={version}", file=_output)
|
||||
print(f"prerelease={prerelease}", file=_output)
|
||||
print(f"imageTags={','.join(image_tags)}", file=_output)
|
||||
print(f"imageTagsJSON={dumps(image_tags)}", file=_output)
|
||||
print(f"attestImageNames={get_attest_image_names(image_tags)}", file=_output)
|
||||
print(f"imageMainTag={image_main_tag}", file=_output)
|
||||
print(f"imageMainName={image_tags[0]}", file=_output)
|
||||
print(f"cacheTo={cache_to}", file=_output)
|
||||
print(f"imageBuildArgs={image_build_args_str}", file=_output)
|
||||
18
.github/actions/docker-push-variables/test.sh
vendored
18
.github/actions/docker-push-variables/test.sh
vendored
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash -x
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
# Non-pushing PR
|
||||
GITHUB_OUTPUT=/dev/stdout \
|
||||
GITHUB_REF=ref \
|
||||
GITHUB_SHA=sha \
|
||||
IMAGE_NAME=ghcr.io/goauthentik/server,authentik/server \
|
||||
GITHUB_REPOSITORY=goauthentik/authentik \
|
||||
python $SCRIPT_DIR/push_vars.py
|
||||
|
||||
# Pushing PR/main
|
||||
GITHUB_OUTPUT=/dev/stdout \
|
||||
GITHUB_REF=ref \
|
||||
GITHUB_SHA=sha \
|
||||
IMAGE_NAME=ghcr.io/goauthentik/server,authentik/server \
|
||||
GITHUB_REPOSITORY=goauthentik/authentik \
|
||||
DOCKER_USERNAME=foo \
|
||||
python $SCRIPT_DIR/push_vars.py
|
||||
107
.github/actions/setup/action.yml
vendored
107
.github/actions/setup/action.yml
vendored
@@ -2,114 +2,43 @@ name: "Setup authentik testing environment"
|
||||
description: "Setup authentik testing environment"
|
||||
|
||||
inputs:
|
||||
dependencies:
|
||||
description: "List of dependencies to setup"
|
||||
default: "system,python,rust,node,go,runtime"
|
||||
postgresql_version:
|
||||
description: "Optional postgresql image tag"
|
||||
default: "16"
|
||||
working-directory:
|
||||
description: |
|
||||
Optional working directory if this repo isn't in the root of the actions workspace.
|
||||
When set, needs to contain a trailing slash
|
||||
default: ""
|
||||
default: "12"
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Cleanup apt
|
||||
if: ${{ contains(inputs.dependencies, 'system') || contains(inputs.dependencies, 'python') }}
|
||||
shell: bash
|
||||
run: sudo apt-get remove --purge man-db
|
||||
- name: Install apt deps
|
||||
if: ${{ contains(inputs.dependencies, 'system') || contains(inputs.dependencies, 'python') }}
|
||||
uses: gerlero/apt-install@f4fa5265092af9e750549565d28c99aec7189639
|
||||
with:
|
||||
packages: libpq-dev openssl libxmlsec1-dev pkg-config gettext krb5-multidev libkrb5-dev heimdal-multidev libclang-dev krb5-kdc krb5-user krb5-admin-server
|
||||
update: true
|
||||
upgrade: false
|
||||
install-recommends: false
|
||||
- name: Make space on disk
|
||||
if: ${{ contains(inputs.dependencies, 'system') || contains(inputs.dependencies, 'python') }}
|
||||
- name: Install poetry & deps
|
||||
shell: bash
|
||||
run: |
|
||||
sudo mkdir -p /tmp/empty/
|
||||
sudo rsync -a --delete /tmp/empty/ /usr/local/lib/android/
|
||||
- name: Install uv
|
||||
if: ${{ contains(inputs.dependencies, 'python') }}
|
||||
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v5
|
||||
pipx install poetry || true
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y libpq-dev openssl libxmlsec1-dev pkg-config gettext
|
||||
- name: Setup python and restore poetry
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Setup python
|
||||
if: ${{ contains(inputs.dependencies, 'python') }}
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v5
|
||||
python-version-file: 'pyproject.toml'
|
||||
cache: "poetry"
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
python-version-file: "${{ inputs.working-directory }}pyproject.toml"
|
||||
- name: Install Python deps
|
||||
if: ${{ contains(inputs.dependencies, 'python') }}
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: uv sync --all-extras --dev --frozen
|
||||
- name: Setup rust (stable)
|
||||
if: ${{ contains(inputs.dependencies, 'rust') && !contains(inputs.dependencies, 'rust-nightly') }}
|
||||
uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1
|
||||
with:
|
||||
rustflags: ""
|
||||
- name: Setup rust (nightly)
|
||||
if: ${{ contains(inputs.dependencies, 'rust-nightly') }}
|
||||
uses: actions-rust-lang/setup-rust-toolchain@2b1f5e9b395427c92ee4e3331786ca3c37afe2d7 # v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: rustfmt
|
||||
rustflags: ""
|
||||
- name: Setup rust dependencies
|
||||
if: ${{ contains(inputs.dependencies, 'rust') }}
|
||||
uses: taiki-e/install-action@b5fddbb5361bce8a06fb168c9d403a6cc552b084 # v2
|
||||
with:
|
||||
tool: cargo-deny cargo-machete cargo-llvm-cov nextest
|
||||
- name: Setup node (web)
|
||||
if: ${{ contains(inputs.dependencies, 'node') }}
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4
|
||||
with:
|
||||
node-version-file: "${{ inputs.working-directory }}web/package.json"
|
||||
node-version-file: web/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: "${{ inputs.working-directory }}web/package-lock.json"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- name: Setup node (root)
|
||||
if: ${{ contains(inputs.dependencies, 'node') }}
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4
|
||||
with:
|
||||
node-version-file: "${{ inputs.working-directory }}package.json"
|
||||
cache: "npm"
|
||||
cache-dependency-path: "${{ inputs.working-directory }}package-lock.json"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- name: Install Node deps
|
||||
if: ${{ contains(inputs.dependencies, 'node') }}
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: npm ci
|
||||
cache-dependency-path: web/package-lock.json
|
||||
- name: Setup go
|
||||
if: ${{ contains(inputs.dependencies, 'go') }}
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v5
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version-file: "${{ inputs.working-directory }}go.mod"
|
||||
- name: Setup docker cache
|
||||
if: ${{ contains(inputs.dependencies, 'runtime') }}
|
||||
uses: AndreKurait/docker-cache@0fe76702a40db986d9663c24954fc14c6a6031b7
|
||||
with:
|
||||
key: docker-images-${{ runner.os }}-${{ hashFiles('.github/actions/setup/compose.yml', 'Makefile') }}-${{ inputs.postgresql_version }}
|
||||
go-version-file: "go.mod"
|
||||
- name: Setup dependencies
|
||||
if: ${{ contains(inputs.dependencies, 'runtime') }}
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: |
|
||||
export PSQL_TAG=${{ inputs.postgresql_version }}
|
||||
docker compose -f .github/actions/setup/compose.yml up -d --wait
|
||||
docker-compose -f .github/actions/setup/docker-compose.yml up -d
|
||||
poetry install
|
||||
cd web && npm ci
|
||||
- name: Generate config
|
||||
if: ${{ contains(inputs.dependencies, 'python') }}
|
||||
shell: uv run python {0}
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
shell: poetry run python {0}
|
||||
run: |
|
||||
from authentik.lib.generators import generate_id
|
||||
from yaml import safe_dump
|
||||
|
||||
40
.github/actions/setup/compose.yml
vendored
40
.github/actions/setup/compose.yml
vendored
@@ -1,40 +0,0 @@
|
||||
services:
|
||||
postgresql:
|
||||
image: docker.io/library/postgres:${PSQL_TAG:-16}
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql
|
||||
command: "-c log_statement=all"
|
||||
environment:
|
||||
POSTGRES_USER: authentik
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
POSTGRES_DB: authentik
|
||||
PGDATA: /var/lib/postgresql/data/pgdata
|
||||
ports:
|
||||
- 5432:5432
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB} -h 127.0.0.1"]
|
||||
interval: 1s
|
||||
timeout: 5s
|
||||
retries: 60
|
||||
restart: always
|
||||
s3:
|
||||
container_name: s3
|
||||
image: docker.io/zenko/cloudserver
|
||||
environment:
|
||||
REMOTE_MANAGEMENT_DISABLE: "1"
|
||||
SCALITY_ACCESS_KEY_ID: accessKey1
|
||||
SCALITY_SECRET_ACCESS_KEY: secretKey1
|
||||
ports:
|
||||
- 8020:8000
|
||||
volumes:
|
||||
- s3-data:/usr/src/app/localData
|
||||
- s3-metadata:/usr/src/app/localMetadata
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
driver: local
|
||||
s3-data:
|
||||
driver: local
|
||||
s3-metadata:
|
||||
driver: local
|
||||
23
.github/actions/setup/docker-compose.yml
vendored
Normal file
23
.github/actions/setup/docker-compose.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
postgresql:
|
||||
image: docker.io/library/postgres:${PSQL_TAG:-12}
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_USER: authentik
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
POSTGRES_DB: authentik
|
||||
ports:
|
||||
- 5432:5432
|
||||
restart: always
|
||||
redis:
|
||||
image: docker.io/library/redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
driver: local
|
||||
32
.github/actions/test-results/action.yml
vendored
32
.github/actions/test-results/action.yml
vendored
@@ -1,32 +0,0 @@
|
||||
name: "Process test results"
|
||||
description: Convert test results to JUnit, add them to GitHub Actions and codecov
|
||||
|
||||
inputs:
|
||||
files:
|
||||
description: Comma-separated explicit list of files to upload
|
||||
flags:
|
||||
description: Codecov flags
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v5
|
||||
with:
|
||||
files: ${{ inputs.files }}
|
||||
flags: ${{ inputs.flags }}
|
||||
use_oidc: true
|
||||
- uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v5
|
||||
with:
|
||||
files: ${{ inputs.files }}
|
||||
flags: ${{ inputs.flags }}
|
||||
use_oidc: true
|
||||
report_type: test_results
|
||||
- name: PostgreSQL Logs
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ $RUNNER_DEBUG == '1' ]]; then
|
||||
docker stop setup-postgresql-1
|
||||
echo "::group::PostgreSQL Logs"
|
||||
docker logs setup-postgresql-1
|
||||
echo "::endgroup::"
|
||||
fi
|
||||
2
.github/cherry-pick-bot.yml
vendored
Normal file
2
.github/cherry-pick-bot.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
enabled: true
|
||||
preservePullRequestTitle: true
|
||||
3
.github/codecov.yml
vendored
3
.github/codecov.yml
vendored
@@ -8,6 +8,3 @@ coverage:
|
||||
threshold: 1%
|
||||
comment:
|
||||
after_n_builds: 3
|
||||
ignore:
|
||||
- packages/client-rust
|
||||
- packages/client-ts
|
||||
|
||||
1
.github/codespell-dictionary.txt
vendored
Normal file
1
.github/codespell-dictionary.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
authentic->authentik
|
||||
4
.github/codespell-words.txt
vendored
Normal file
4
.github/codespell-words.txt
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
keypair
|
||||
keypairs
|
||||
hass
|
||||
warmup
|
||||
254
.github/dependabot.yml
vendored
254
.github/dependabot.yml
vendored
@@ -1,17 +1,7 @@
|
||||
version: 2
|
||||
updates:
|
||||
#region Github Actions
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directories:
|
||||
- /
|
||||
# Required to update composite actions
|
||||
# https://github.com/dependabot/dependabot-core/issues/6704
|
||||
- /.github/actions/cherry-pick
|
||||
- /.github/actions/setup
|
||||
- /.github/actions/docker-push-variables
|
||||
- /.github/actions/comment-pr-instructions
|
||||
- /.github/actions/test-results
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
@@ -20,13 +10,6 @@ updates:
|
||||
prefix: "ci:"
|
||||
labels:
|
||||
- dependencies
|
||||
cooldown:
|
||||
default-days: 3
|
||||
|
||||
#endregion
|
||||
|
||||
#region Golang
|
||||
|
||||
- package-ecosystem: gomod
|
||||
directory: "/"
|
||||
schedule:
|
||||
@@ -37,66 +20,8 @@ updates:
|
||||
prefix: "core:"
|
||||
labels:
|
||||
- dependencies
|
||||
cooldown:
|
||||
default-days: 7
|
||||
semver-major-days: 14
|
||||
semver-patch-days: 3
|
||||
exclude:
|
||||
- "golang.org/x/crypto"
|
||||
- "golang.org/x/net"
|
||||
- "github.com/golang-jwt/jwt/*"
|
||||
- "github.com/coreos/go-oidc/*"
|
||||
- "github.com/go-ldap/ldap/*"
|
||||
|
||||
#endregion
|
||||
|
||||
#region Rust
|
||||
|
||||
- package-ecosystem: cargo
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
open-pull-requests-limit: 10
|
||||
commit-message:
|
||||
prefix: "core:"
|
||||
labels:
|
||||
- dependencies
|
||||
cooldown:
|
||||
default-days: 7
|
||||
semver-major-days: 14
|
||||
semver-patch-days: 3
|
||||
exclude:
|
||||
- aws-lc-fips-sys
|
||||
- aws-lc-rs
|
||||
- aws-lc-sys
|
||||
- rustls
|
||||
- rustls-pki-types
|
||||
- rustls-platform-verifier
|
||||
- rustls-webpki
|
||||
|
||||
- package-ecosystem: rust-toolchain
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
open-pull-requests-limit: 10
|
||||
commit-message:
|
||||
prefix: "core:"
|
||||
labels:
|
||||
- dependencies
|
||||
cooldown:
|
||||
default-days: 3
|
||||
|
||||
#endregion
|
||||
|
||||
#region Web
|
||||
|
||||
- package-ecosystem: npm
|
||||
directories:
|
||||
- "/"
|
||||
- "/web"
|
||||
- "/web/packages/*"
|
||||
directory: "/web"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
@@ -105,10 +30,7 @@ updates:
|
||||
open-pull-requests-limit: 10
|
||||
commit-message:
|
||||
prefix: "web:"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
semver-major-days: 14
|
||||
semver-patch-days: 3
|
||||
# TODO: deduplicate these groups
|
||||
groups:
|
||||
sentry:
|
||||
patterns:
|
||||
@@ -119,51 +41,18 @@ updates:
|
||||
- "babel-*"
|
||||
eslint:
|
||||
patterns:
|
||||
- "@eslint/*"
|
||||
- "@typescript-eslint/*"
|
||||
- "eslint-*"
|
||||
- "eslint"
|
||||
- "typescript-eslint"
|
||||
- "eslint-*"
|
||||
storybook:
|
||||
patterns:
|
||||
- "@storybook/*"
|
||||
- "*storybook*"
|
||||
bundler:
|
||||
esbuild:
|
||||
patterns:
|
||||
- "@esbuild/*"
|
||||
- "esbuild*"
|
||||
- "@vitest/*"
|
||||
- "vitest"
|
||||
rollup:
|
||||
patterns:
|
||||
- "@rollup/*"
|
||||
- "rollup-*"
|
||||
- "rollup*"
|
||||
swc:
|
||||
patterns:
|
||||
- "@swc/*"
|
||||
- "swc-*"
|
||||
goauthentik:
|
||||
patterns:
|
||||
- "@goauthentik/*"
|
||||
react:
|
||||
patterns:
|
||||
- "react"
|
||||
- "react-dom"
|
||||
- "@types/react"
|
||||
- "@types/react-dom"
|
||||
|
||||
#endregion
|
||||
|
||||
#region NPM Packages
|
||||
|
||||
- package-ecosystem: npm
|
||||
directories:
|
||||
- "/packages/esbuild-plugin-live-reload"
|
||||
- "/packages/prettier-config"
|
||||
- "/packages/tsconfig"
|
||||
- "/packages/docusaurus-config"
|
||||
- "/packages/eslint-config"
|
||||
directory: "/tests/wdio"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
@@ -171,11 +60,8 @@ updates:
|
||||
- dependencies
|
||||
open-pull-requests-limit: 10
|
||||
commit-message:
|
||||
prefix: "core, web:"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
semver-major-days: 14
|
||||
semver-patch-days: 3
|
||||
prefix: "web:"
|
||||
# TODO: deduplicate these groups
|
||||
groups:
|
||||
sentry:
|
||||
patterns:
|
||||
@@ -186,44 +72,19 @@ updates:
|
||||
- "babel-*"
|
||||
eslint:
|
||||
patterns:
|
||||
- "@eslint/*"
|
||||
- "@typescript-eslint/*"
|
||||
- "eslint-*"
|
||||
- "eslint"
|
||||
- "typescript-eslint"
|
||||
- "eslint-*"
|
||||
storybook:
|
||||
patterns:
|
||||
- "@storybook/*"
|
||||
- "*storybook*"
|
||||
bundler:
|
||||
esbuild:
|
||||
patterns:
|
||||
- "@esbuild/*"
|
||||
- "esbuild*"
|
||||
- "@vitest/*"
|
||||
- "vitest"
|
||||
rollup:
|
||||
wdio:
|
||||
patterns:
|
||||
- "@rollup/*"
|
||||
- "rollup-*"
|
||||
- "rollup*"
|
||||
swc:
|
||||
patterns:
|
||||
- "@swc/*"
|
||||
- "swc-*"
|
||||
goauthentik:
|
||||
patterns:
|
||||
- "@goauthentik/*"
|
||||
react:
|
||||
patterns:
|
||||
- "react"
|
||||
- "react-dom"
|
||||
- "@types/react"
|
||||
- "@types/react-dom"
|
||||
|
||||
#endregion
|
||||
|
||||
# #region Documentation
|
||||
|
||||
- "@wdio/*"
|
||||
- package-ecosystem: npm
|
||||
directory: "/website"
|
||||
schedule:
|
||||
@@ -234,58 +95,11 @@ updates:
|
||||
prefix: "website:"
|
||||
labels:
|
||||
- dependencies
|
||||
cooldown:
|
||||
default-days: 7
|
||||
semver-major-days: 14
|
||||
semver-patch-days: 3
|
||||
groups:
|
||||
docusaurus:
|
||||
patterns:
|
||||
- "@docusaurus/*"
|
||||
- "@goauthentik/docusaurus-config"
|
||||
build:
|
||||
patterns:
|
||||
- "@swc/*"
|
||||
- "swc-*"
|
||||
- "lightningcss*"
|
||||
- "@rspack/binding*"
|
||||
goauthentik:
|
||||
patterns:
|
||||
- "@goauthentik/eslint-config"
|
||||
- "@goauthentik/prettier-config"
|
||||
- "@goauthentik/tsconfig"
|
||||
eslint:
|
||||
patterns:
|
||||
- "@eslint/*"
|
||||
- "@typescript-eslint/*"
|
||||
- "eslint-*"
|
||||
- "eslint"
|
||||
- "typescript-eslint"
|
||||
|
||||
#endregion
|
||||
|
||||
# AWS Lifecycle
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: "/lifecycle/aws"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
open-pull-requests-limit: 10
|
||||
commit-message:
|
||||
prefix: "lifecycle/aws:"
|
||||
labels:
|
||||
- dependencies
|
||||
cooldown:
|
||||
default-days: 7
|
||||
semver-major-days: 14
|
||||
semver-patch-days: 3
|
||||
|
||||
#endregion
|
||||
|
||||
#region Python
|
||||
|
||||
- package-ecosystem: uv
|
||||
- package-ecosystem: pip
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
@@ -295,27 +109,8 @@ updates:
|
||||
prefix: "core:"
|
||||
labels:
|
||||
- dependencies
|
||||
cooldown:
|
||||
default-days: 7
|
||||
semver-major-days: 14
|
||||
semver-patch-days: 3
|
||||
exclude:
|
||||
- "django"
|
||||
- "cryptography"
|
||||
- "pyjwt"
|
||||
- "xmlsec"
|
||||
- "lxml"
|
||||
- "psycopg"
|
||||
- "pyopenssl"
|
||||
|
||||
#endregion
|
||||
|
||||
#region Docker
|
||||
|
||||
- package-ecosystem: docker
|
||||
directories:
|
||||
- /lifecycle/container
|
||||
- /website
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
@@ -324,24 +119,3 @@ updates:
|
||||
prefix: "core:"
|
||||
labels:
|
||||
- dependencies
|
||||
cooldown:
|
||||
default-days: 3
|
||||
- package-ecosystem: docker-compose
|
||||
directories:
|
||||
- /packages/client-go
|
||||
- /packages/client-rust
|
||||
- /packages/client-ts
|
||||
# - /scripts # Maybe
|
||||
- /tests/e2e
|
||||
schedule:
|
||||
interval: daily
|
||||
time: "04:00"
|
||||
open-pull-requests-limit: 10
|
||||
commit-message:
|
||||
prefix: "core:"
|
||||
labels:
|
||||
- dependencies
|
||||
cooldown:
|
||||
default-days: 3
|
||||
|
||||
#endregion
|
||||
|
||||
11
.github/pull_request_template.md
vendored
11
.github/pull_request_template.md
vendored
@@ -1,11 +1,7 @@
|
||||
<!--
|
||||
👋 Hi there! Welcome.
|
||||
|
||||
Please check the Contributing guidelines: https://docs.goauthentik.io/docs/developer-docs/#how-can-i-contribute
|
||||
|
||||
⚠️ IMPORTANT: Make sure you are opening this PR from a FEATURE BRANCH, not from your main branch!
|
||||
If you opened this PR from your main branch, please close it and create a new feature branch instead.
|
||||
For more information, see: https://docs.goauthentik.io/developer-docs/contributing/#always-use-feature-branches
|
||||
Please check the Contributing guidelines: https://goauthentik.io/developer-docs/#how-can-i-contribute
|
||||
-->
|
||||
|
||||
## Details
|
||||
@@ -26,13 +22,14 @@ REPLACE ME
|
||||
|
||||
If an API change has been made
|
||||
|
||||
- [ ] The API schema and clients have been updated (`make gen`)
|
||||
- [ ] The API schema has been updated (`make gen-build`)
|
||||
|
||||
If changes to the frontend have been made
|
||||
|
||||
- [ ] The code has been formatted (`make web`)
|
||||
- [ ] The translation files have been updated (`make i18n-extract`)
|
||||
|
||||
If applicable
|
||||
|
||||
- [ ] The documentation has been updated
|
||||
- [ ] The documentation has been formatted (`make docs`)
|
||||
- [ ] The documentation has been formatted (`make website`)
|
||||
|
||||
1
.github/transifex.yml
vendored
1
.github/transifex.yml
vendored
@@ -1,4 +1,3 @@
|
||||
---
|
||||
git:
|
||||
filters:
|
||||
- filter_type: file
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
---
|
||||
# Re-usable workflow for a single-architecture build
|
||||
name: Reusable - Single-arch Container build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image_name:
|
||||
required: true
|
||||
type: string
|
||||
image_arch:
|
||||
required: true
|
||||
type: string
|
||||
runs-on:
|
||||
required: true
|
||||
type: string
|
||||
registry_dockerhub:
|
||||
default: false
|
||||
type: boolean
|
||||
registry_ghcr:
|
||||
default: false
|
||||
type: boolean
|
||||
release:
|
||||
default: false
|
||||
type: boolean
|
||||
outputs:
|
||||
image-digest:
|
||||
value: ${{ jobs.build.outputs.image-digest }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build ${{ inputs.image_arch }}
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
outputs:
|
||||
image-digest: ${{ steps.push.outputs.digest }}
|
||||
permissions:
|
||||
# Needed to upload container images to ghcr.io
|
||||
packages: write
|
||||
# Needed for attestation
|
||||
id-token: write
|
||||
attestations: write
|
||||
# Needed for checkout
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
- name: prepare variables
|
||||
uses: ./.github/actions/docker-push-variables
|
||||
id: ev
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_CORP_USERNAME }}
|
||||
with:
|
||||
image-name: ${{ inputs.image_name }}
|
||||
image-arch: ${{ inputs.image_arch }}
|
||||
release: ${{ inputs.release }}
|
||||
- name: Login to Docker Hub
|
||||
if: ${{ inputs.registry_dockerhub }}
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_CORP_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_CORP_PASSWORD }}
|
||||
- name: Login to GitHub Container Registry
|
||||
if: ${{ inputs.registry_ghcr }}
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build Docker Image
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
id: push
|
||||
with:
|
||||
context: .
|
||||
file: lifecycle/container/Dockerfile
|
||||
push: ${{ steps.ev.outputs.shouldPush == 'true' }}
|
||||
secrets: |
|
||||
GEOIPUPDATE_ACCOUNT_ID=${{ secrets.GEOIPUPDATE_ACCOUNT_ID }}
|
||||
GEOIPUPDATE_LICENSE_KEY=${{ secrets.GEOIPUPDATE_LICENSE_KEY }}
|
||||
build-args: |
|
||||
${{ steps.ev.outputs.imageBuildArgs }}
|
||||
tags: ${{ steps.ev.outputs.imageTags }}
|
||||
platforms: linux/${{ inputs.image_arch }}
|
||||
cache-from: type=registry,ref=${{ steps.ev.outputs.attestImageNames }}:buildcache-${{ inputs.image_arch }}
|
||||
cache-to: ${{ steps.ev.outputs.cacheTo }}
|
||||
- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v3
|
||||
id: attest
|
||||
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
|
||||
with:
|
||||
subject-name: ${{ steps.ev.outputs.attestImageNames }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
105
.github/workflows/_reusable-docker-build.yml
vendored
105
.github/workflows/_reusable-docker-build.yml
vendored
@@ -1,105 +0,0 @@
|
||||
---
|
||||
# Re-usable workflow for a multi-architecture build
|
||||
name: Reusable - Multi-arch container build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image_name:
|
||||
required: true
|
||||
type: string
|
||||
registry_dockerhub:
|
||||
default: false
|
||||
type: boolean
|
||||
registry_ghcr:
|
||||
default: true
|
||||
type: boolean
|
||||
release:
|
||||
default: false
|
||||
type: boolean
|
||||
outputs: {}
|
||||
|
||||
jobs:
|
||||
build-server-amd64:
|
||||
uses: ./.github/workflows/_reusable-docker-build-single.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
image_name: ${{ inputs.image_name }}
|
||||
image_arch: amd64
|
||||
runs-on: ubuntu-latest
|
||||
registry_dockerhub: ${{ inputs.registry_dockerhub }}
|
||||
registry_ghcr: ${{ inputs.registry_ghcr }}
|
||||
release: ${{ inputs.release }}
|
||||
build-server-arm64:
|
||||
uses: ./.github/workflows/_reusable-docker-build-single.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
image_name: ${{ inputs.image_name }}
|
||||
image_arch: arm64
|
||||
runs-on: ubuntu-22.04-arm
|
||||
registry_dockerhub: ${{ inputs.registry_dockerhub }}
|
||||
registry_ghcr: ${{ inputs.registry_ghcr }}
|
||||
release: ${{ inputs.release }}
|
||||
get-tags:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-server-amd64
|
||||
- build-server-arm64
|
||||
outputs:
|
||||
tags: ${{ steps.ev.outputs.imageTagsJSON }}
|
||||
shouldPush: ${{ steps.ev.outputs.shouldPush }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- name: prepare variables
|
||||
uses: ./.github/actions/docker-push-variables
|
||||
id: ev
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_CORP_USERNAME }}
|
||||
with:
|
||||
image-name: ${{ inputs.image_name }}
|
||||
merge-server:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ needs.get-tags.outputs.shouldPush == 'true' }}
|
||||
needs:
|
||||
- get-tags
|
||||
- build-server-amd64
|
||||
- build-server-arm64
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
tag: ${{ fromJson(needs.get-tags.outputs.tags) }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- name: prepare variables
|
||||
uses: ./.github/actions/docker-push-variables
|
||||
id: ev
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_CORP_USERNAME }}
|
||||
with:
|
||||
image-name: ${{ inputs.image_name }}
|
||||
- name: Login to Docker Hub
|
||||
if: ${{ inputs.registry_dockerhub }}
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_CORP_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_CORP_PASSWORD }}
|
||||
- name: Login to GitHub Container Registry
|
||||
if: ${{ inputs.registry_ghcr }}
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: int128/docker-manifest-create-action@fa55f72001a6c74b0f4997dca65c70d334905180 # v2
|
||||
id: build
|
||||
with:
|
||||
tags: ${{ matrix.tag }}
|
||||
sources: |
|
||||
${{ steps.ev.outputs.attestImageNames }}@${{ needs.build-server-amd64.outputs.image-digest }}
|
||||
${{ steps.ev.outputs.attestImageNames }}@${{ needs.build-server-arm64.outputs.image-digest }}
|
||||
- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v3
|
||||
id: attest
|
||||
with:
|
||||
subject-name: ${{ steps.ev.outputs.attestImageNames }}
|
||||
subject-digest: ${{ steps.build.outputs.digest }}
|
||||
push-to-registry: true
|
||||
95
.github/workflows/ci-api-docs.yml
vendored
95
.github/workflows/ci-api-docs.yml
vendored
@@ -1,95 +0,0 @@
|
||||
---
|
||||
name: CI - API Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- next
|
||||
- version-*
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- version-*
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
command:
|
||||
- prettier-check
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- name: Install Dependencies
|
||||
working-directory: website/
|
||||
run: npm ci
|
||||
- name: Lint
|
||||
working-directory: website/
|
||||
run: npm run ${{ matrix.command }}
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
|
||||
with:
|
||||
node-version-file: website/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: website/package-lock.json
|
||||
- working-directory: website/
|
||||
name: Install Dependencies
|
||||
run: npm ci
|
||||
- uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v4
|
||||
with:
|
||||
path: |
|
||||
${{ github.workspace }}/website/api/.docusaurus
|
||||
${{ github.workspace }}/website/api/**/.cache
|
||||
key: |
|
||||
${{ runner.os }}-docusaurus-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-docusaurus-${{ hashFiles('**/package-lock.json') }}
|
||||
- name: Build API Docs via Docusaurus
|
||||
working-directory: website
|
||||
env:
|
||||
NODE_ENV: production
|
||||
run: npm run build -w api
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4
|
||||
with:
|
||||
name: api-docs
|
||||
path: website/api/build
|
||||
retention-days: 7
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- lint
|
||||
- build
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v5
|
||||
with:
|
||||
name: api-docs
|
||||
path: website/api/build
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
|
||||
with:
|
||||
node-version-file: website/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: website/package-lock.json
|
||||
- name: Deploy Netlify (Production)
|
||||
working-directory: website/api
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
env:
|
||||
NETLIFY_SITE_ID: authentik-api-docs.netlify.app
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
run: npx netlify deploy --no-build --prod
|
||||
- name: Deploy Netlify (Preview)
|
||||
if: github.event_name == 'pull_request' || github.ref != 'refs/heads/main'
|
||||
working-directory: website/api
|
||||
env:
|
||||
NETLIFY_SITE_ID: authentik-api-docs.netlify.app
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
run: |
|
||||
if [ -n "${VAR}" ]; then
|
||||
npx netlify deploy --no-build --alias=deploy-preview-${{ github.event.number }}
|
||||
fi
|
||||
47
.github/workflows/ci-aws-cfn.yml
vendored
47
.github/workflows/ci-aws-cfn.yml
vendored
@@ -1,47 +0,0 @@
|
||||
---
|
||||
name: CI - AWS cfn
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- next
|
||||
- version-*
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- version-*
|
||||
|
||||
env:
|
||||
POSTGRES_DB: authentik
|
||||
POSTGRES_USER: authentik
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
|
||||
jobs:
|
||||
check-changes-applied:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
|
||||
with:
|
||||
node-version-file: lifecycle/aws/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: lifecycle/aws/package-lock.json
|
||||
- working-directory: lifecycle/aws/
|
||||
run: |
|
||||
npm ci
|
||||
- name: Check changes have been applied
|
||||
run: |
|
||||
uv run make aws-cfn
|
||||
git diff --exit-code lifecycle/aws/template.yaml
|
||||
ci-aws-cfn-mark:
|
||||
if: always()
|
||||
needs:
|
||||
- check-changes-applied
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1
|
||||
with:
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
32
.github/workflows/ci-docs-source.yml
vendored
32
.github/workflows/ci-docs-source.yml
vendored
@@ -1,32 +0,0 @@
|
||||
---
|
||||
name: CI - Source code docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
POSTGRES_DB: authentik
|
||||
POSTGRES_USER: authentik
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
|
||||
jobs:
|
||||
publish-source-docs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- name: generate docs
|
||||
run: |
|
||||
uv run make migrate
|
||||
uv run ak build_source_docs
|
||||
- name: Publish
|
||||
env:
|
||||
NETLIFY_SITE_ID: eb246b7b-1d83-4f69-89f7-01a936b4ca59
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
run: |
|
||||
npm install -g netlify-cli
|
||||
netlify deploy --dir=source_docs --prod
|
||||
126
.github/workflows/ci-docs.yml
vendored
126
.github/workflows/ci-docs.yml
vendored
@@ -1,126 +0,0 @@
|
||||
---
|
||||
name: CI - Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- next
|
||||
- version-*
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- version-*
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NODE_ENV: production
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
command:
|
||||
- prettier-check
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- name: Install dependencies
|
||||
working-directory: website/
|
||||
run: npm ci
|
||||
- name: Lint
|
||||
working-directory: website/
|
||||
run: npm run ${{ matrix.command }}
|
||||
build-docs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NODE_ENV: production
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
|
||||
with:
|
||||
node-version-file: website/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: website/package-lock.json
|
||||
- working-directory: website/
|
||||
name: Install Dependencies
|
||||
run: npm ci
|
||||
- name: Build Documentation via Docusaurus
|
||||
working-directory: website/
|
||||
run: npm run build
|
||||
build-integrations:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NODE_ENV: production
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
|
||||
with:
|
||||
node-version-file: website/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: website/package-lock.json
|
||||
- working-directory: website/
|
||||
name: Install Dependencies
|
||||
run: npm ci
|
||||
- name: Build Integrations via Docusaurus
|
||||
working-directory: website/
|
||||
run: npm run build -w integrations
|
||||
build-container:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed to upload container images to ghcr.io
|
||||
packages: write
|
||||
# Needed for attestation
|
||||
id-token: write
|
||||
attestations: write
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
- name: prepare variables
|
||||
uses: ./.github/actions/docker-push-variables
|
||||
id: ev
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_CORP_USERNAME }}
|
||||
with:
|
||||
image-name: ghcr.io/goauthentik/dev-docs
|
||||
- name: Login to Container Registry
|
||||
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build Docker Image
|
||||
id: push
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
with:
|
||||
tags: ${{ steps.ev.outputs.imageTags }}
|
||||
file: website/Dockerfile
|
||||
push: ${{ steps.ev.outputs.shouldPush == 'true' }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
context: .
|
||||
cache-from: type=registry,ref=ghcr.io/goauthentik/dev-docs:buildcache
|
||||
cache-to: ${{ steps.ev.outputs.shouldPush == 'true' && 'type=registry,ref=ghcr.io/goauthentik/dev-docs:buildcache,mode=max' || '' }}
|
||||
- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v3
|
||||
id: attest
|
||||
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
|
||||
with:
|
||||
subject-name: ${{ steps.ev.outputs.attestImageNames }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
ci-website-mark:
|
||||
if: always()
|
||||
needs:
|
||||
- lint
|
||||
- build-docs
|
||||
- build-integrations
|
||||
- build-container
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1
|
||||
with:
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
38
.github/workflows/ci-main-daily.yml
vendored
38
.github/workflows/ci-main-daily.yml
vendored
@@ -1,38 +0,0 @@
|
||||
---
|
||||
name: CI - Main daily
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Every night at 3am
|
||||
- cron: "0 3 * * *"
|
||||
pull_request:
|
||||
paths:
|
||||
# Needs to refer to itself
|
||||
- .github/workflows/ci-main-daily.yml
|
||||
|
||||
jobs:
|
||||
test-container:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- docs
|
||||
- version-2025-12
|
||||
- version-2026-2
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- run: |
|
||||
set -euo pipefail
|
||||
current="$(pwd)"
|
||||
dir="/tmp/authentik/${{ matrix.version }}"
|
||||
# 2025.12 still serves the legacy docker-compose filename; newer sites use compose.yml.
|
||||
compose_path="compose.yml"
|
||||
if [ "${{ matrix.version }}" = "version-2025-12" ]; then
|
||||
compose_path="docker-compose.yml"
|
||||
fi
|
||||
mkdir -p "${dir}/lifecycle/container"
|
||||
cd "${dir}"
|
||||
wget "https://${{ matrix.version }}.goauthentik.io/${compose_path}" -O "${dir}/lifecycle/container/compose.yml"
|
||||
"${current}/scripts/test_docker.sh"
|
||||
405
.github/workflows/ci-main.yml
vendored
405
.github/workflows/ci-main.yml
vendored
@@ -1,5 +1,4 @@
|
||||
---
|
||||
name: CI - Main
|
||||
name: authentik-ci-main
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -7,6 +6,8 @@ on:
|
||||
- main
|
||||
- next
|
||||
- version-*
|
||||
paths-ignore:
|
||||
- website
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
@@ -17,120 +18,70 @@ env:
|
||||
POSTGRES_USER: authentik
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
|
||||
permissions:
|
||||
# Needed for checkout
|
||||
contents: read
|
||||
# Needed for codecov OIDC token
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- job: bandit
|
||||
deps: python
|
||||
- job: black
|
||||
deps: python
|
||||
- job: spellcheck
|
||||
deps: node
|
||||
- job: pending-migrations
|
||||
deps: python,runtime
|
||||
- job: ruff
|
||||
deps: python
|
||||
- job: mypy
|
||||
deps: python
|
||||
- job: cargo-deny
|
||||
deps: rust
|
||||
- job: cargo-machete
|
||||
deps: rust
|
||||
- job: clippy
|
||||
deps: rust
|
||||
- job: rustfmt
|
||||
deps: rust-nightly
|
||||
job:
|
||||
- bandit
|
||||
- black
|
||||
- codespell
|
||||
- isort
|
||||
- pending-migrations
|
||||
- pylint
|
||||
- pyright
|
||||
- ruff
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
dependencies: ${{ matrix.deps }}
|
||||
- name: run job
|
||||
run: make ci-lint-${{ matrix.job }}
|
||||
test-gen:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
dependencies: "system,python,go,node,runtime,rust-nightly"
|
||||
- name: generate schema
|
||||
run: make migrate gen-build
|
||||
- name: generate API clients
|
||||
run: make gen-clients
|
||||
- name: ensure schema is up-to-date
|
||||
run: git diff --exit-code -- schema.yml blueprints/schema.json packages/client-go packages/client-rust packages/client-ts
|
||||
run: poetry run make ci-${{ matrix.job }}
|
||||
test-migrations:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- name: run migrations
|
||||
run: uv run python -m lifecycle.migrate
|
||||
test-make-seed:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: seed
|
||||
run: |
|
||||
echo "seed=$(printf "%d\n" "0x$(openssl rand -hex 4)")" >> "$GITHUB_OUTPUT"
|
||||
outputs:
|
||||
seed: ${{ steps.seed.outputs.seed }}
|
||||
run: poetry run python -m lifecycle.migrate
|
||||
test-migrations-from-stable:
|
||||
name: test-migrations-from-stable - PostgreSQL ${{ matrix.psql }} - Run ${{ matrix.run_id }}/5
|
||||
name: test-migrations-from-stable - PostgreSQL ${{ matrix.psql }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
needs: test-make-seed
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
psql:
|
||||
- 14-alpine
|
||||
- 18-alpine
|
||||
run_id: [1, 2, 3, 4, 5]
|
||||
- 12-alpine
|
||||
- 15-alpine
|
||||
- 16-alpine
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
postgresql_version: ${{ matrix.psql }}
|
||||
- name: checkout stable
|
||||
run: |
|
||||
set -e -o pipefail
|
||||
# Delete all poetry envs
|
||||
rm -rf /home/runner/.cache/pypoetry
|
||||
# Copy current, latest config to local
|
||||
cp authentik/lib/default.yml local.env.yml
|
||||
cp -R .github ..
|
||||
cp -R scripts ..
|
||||
# Previous stable tag
|
||||
prev_stable=$(git tag --sort=version:refname | grep '^version/' | grep -vE -- '-rc[0-9]+$' | tail -n1)
|
||||
# Current version family based on
|
||||
current_version_family=$(cat internal/constants/VERSION | grep -vE -- 'rc[0-9]+$' || true)
|
||||
if [[ -n $current_version_family ]]; then
|
||||
prev_stable="version/${current_version_family}"
|
||||
fi
|
||||
echo "::notice::Checking out ${prev_stable} as stable version..."
|
||||
git checkout ${prev_stable}
|
||||
git checkout version/$(python -c "from authentik import __version__; print(__version__)")
|
||||
rm -rf .github/ scripts/
|
||||
mv ../.github ../scripts .
|
||||
- name: Setup authentik env (stable)
|
||||
- name: Setup authentik env (ensure stable deps are installed)
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
postgresql_version: ${{ matrix.psql }}
|
||||
- name: run migrations to stable
|
||||
run: |
|
||||
docker ps
|
||||
docker logs setup-postgresql-1
|
||||
uv run python -m lifecycle.migrate
|
||||
run: poetry run python -m lifecycle.migrate
|
||||
- name: checkout current code
|
||||
run: |
|
||||
set -x
|
||||
@@ -138,71 +89,55 @@ jobs:
|
||||
git reset --hard HEAD
|
||||
git clean -d -fx .
|
||||
git checkout $GITHUB_SHA
|
||||
# Delete previous poetry env
|
||||
rm -rf $(poetry env info --path)
|
||||
poetry install
|
||||
- name: Setup authentik env (ensure latest deps are installed)
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
postgresql_version: ${{ matrix.psql }}
|
||||
- name: migrate to latest
|
||||
run: |
|
||||
uv run python -m lifecycle.migrate
|
||||
- name: run tests
|
||||
env:
|
||||
# Test in the main database that we just migrated from the previous stable version
|
||||
AUTHENTIK_POSTGRESQL__TEST__NAME: authentik
|
||||
CI_TEST_SEED: ${{ needs.test-make-seed.outputs.seed }}
|
||||
CI_RUN_ID: ${{ matrix.run_id }}
|
||||
CI_TOTAL_RUNS: "5"
|
||||
run: |
|
||||
uv run make ci-test
|
||||
- uses: ./.github/actions/test-results
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
flags: unit-migrate
|
||||
run: poetry run python -m lifecycle.migrate
|
||||
test-unittest:
|
||||
name: test-unittest - PostgreSQL ${{ matrix.psql }} - Run ${{ matrix.run_id }}/5
|
||||
name: test-unittest - PostgreSQL ${{ matrix.psql }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
needs: test-make-seed
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
psql:
|
||||
- 14-alpine
|
||||
- 18-alpine
|
||||
run_id: [1, 2, 3, 4, 5]
|
||||
- 12-alpine
|
||||
- 15-alpine
|
||||
- 16-alpine
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
postgresql_version: ${{ matrix.psql }}
|
||||
- name: run unittest
|
||||
env:
|
||||
CI_TEST_SEED: ${{ needs.test-make-seed.outputs.seed }}
|
||||
CI_RUN_ID: ${{ matrix.run_id }}
|
||||
CI_TOTAL_RUNS: "5"
|
||||
run: |
|
||||
uv run make ci-test
|
||||
- uses: ./.github/actions/test-results
|
||||
if: ${{ always() }}
|
||||
poetry run make test
|
||||
poetry run coverage xml
|
||||
- if: ${{ always() }}
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
flags: unit
|
||||
test-integration:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- name: Create k8s Kind Cluster
|
||||
uses: helm/kind-action@ef37e7f390d99f746eb8b610417061a60e82a6cc # v1.14.0
|
||||
uses: helm/kind-action@v1.8.0
|
||||
- name: run integration
|
||||
run: |
|
||||
uv run coverage run manage.py test tests/integration
|
||||
uv run coverage combine
|
||||
uv run coverage xml
|
||||
- uses: ./.github/actions/test-results
|
||||
if: ${{ always() }}
|
||||
poetry run coverage run manage.py test tests/integration
|
||||
poetry run coverage xml
|
||||
- if: ${{ always() }}
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
flags: integration
|
||||
test-e2e:
|
||||
@@ -215,151 +150,48 @@ jobs:
|
||||
job:
|
||||
- name: proxy
|
||||
glob: tests/e2e/test_provider_proxy*
|
||||
profiles: selenium
|
||||
- name: oauth
|
||||
glob: tests/e2e/test_provider_oauth2* tests/e2e/test_source_oauth*
|
||||
profiles: selenium
|
||||
- name: oauth-oidc
|
||||
glob: tests/e2e/test_provider_oidc*
|
||||
profiles: selenium
|
||||
- name: saml
|
||||
glob: tests/e2e/test_provider_saml* tests/e2e/test_source_saml*
|
||||
profiles: selenium
|
||||
- name: ldap
|
||||
glob: tests/e2e/test_provider_ldap* tests/e2e/test_source_ldap*
|
||||
- name: rac
|
||||
glob: tests/e2e/test_provider_rac*
|
||||
profiles: selenium
|
||||
- name: ws-fed
|
||||
glob: tests/e2e/test_provider_ws_fed*
|
||||
profiles: selenium
|
||||
- name: radius
|
||||
glob: tests/e2e/test_provider_radius*
|
||||
- name: scim
|
||||
glob: tests/e2e/test_source_scim*
|
||||
- name: flows
|
||||
glob: tests/e2e/test_flows*
|
||||
profiles: selenium
|
||||
- name: endpoints
|
||||
glob: tests/e2e/test_endpoints_*
|
||||
profiles: selenium
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- name: Setup e2e env
|
||||
env:
|
||||
COMPOSE_PROFILES: ${{ matrix.job.profiles }}
|
||||
run: |
|
||||
docker compose -f tests/e2e/compose.yml up -d --quiet-pull
|
||||
- id: cache-web
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v4
|
||||
if: contains(matrix.job.profiles, 'selenium')
|
||||
with:
|
||||
path: web/dist
|
||||
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
|
||||
- name: prepare web ui
|
||||
if: steps.cache-web.outputs.cache-hit != 'true' && contains(matrix.job.profiles, 'selenium')
|
||||
working-directory: web
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
npm run build:sfe
|
||||
- name: run e2e
|
||||
run: |
|
||||
uv run coverage run manage.py test ${{ matrix.job.glob }}
|
||||
uv run coverage combine
|
||||
uv run coverage xml
|
||||
- uses: ./.github/actions/test-results
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
flags: e2e
|
||||
test-openid-conformance:
|
||||
name: test-openid-conformance (${{ matrix.job.name }})
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- name: oidc_basic
|
||||
glob: tests/openid_conformance/test_oidc_basic.py
|
||||
- name: oidc_implicit
|
||||
glob: tests/openid_conformance/test_oidc_implicit.py
|
||||
- name: oidc_rp-initiated
|
||||
glob: tests/openid_conformance/test_oidc_rp_initiated.py
|
||||
- name: oidc_frontchannel
|
||||
glob: tests/openid_conformance/test_oidc_frontchannel.py
|
||||
- name: oidc_backchannel
|
||||
glob: tests/openid_conformance/test_oidc_backchannel.py
|
||||
- name: ssf_transmitter
|
||||
glob: tests/openid_conformance/test_ssf_transmitter.py
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- name: Setup e2e env (chrome, etc)
|
||||
env:
|
||||
COMPOSE_PROFILES: selenium
|
||||
run: |
|
||||
docker compose -f tests/e2e/compose.yml up -d --quiet-pull
|
||||
- name: Setup conformance suite
|
||||
run: |
|
||||
docker compose -f tests/openid_conformance/compose.yml up -d --quiet-pull
|
||||
docker-compose -f tests/e2e/docker-compose.yml up -d
|
||||
- id: cache-web
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: web/dist
|
||||
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**', 'web/packages/sfe/src/**') }}-b
|
||||
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/src/**') }}
|
||||
- name: prepare web ui
|
||||
if: steps.cache-web.outputs.cache-hit != 'true'
|
||||
working-directory: web
|
||||
run: |
|
||||
npm ci
|
||||
make -C .. gen-client-ts
|
||||
npm run build
|
||||
npm run build:sfe
|
||||
- name: run conformance
|
||||
- name: run e2e
|
||||
run: |
|
||||
uv run coverage run manage.py test ${{ matrix.job.glob }}
|
||||
uv run coverage combine
|
||||
uv run coverage xml
|
||||
- uses: ./.github/actions/test-results
|
||||
if: ${{ always() }}
|
||||
poetry run coverage run manage.py test ${{ matrix.job.glob }}
|
||||
poetry run coverage xml
|
||||
- if: ${{ always() }}
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
flags: conformance
|
||||
- if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: conformance-certification-${{ matrix.job.name }}
|
||||
path: tests/openid_conformance/exports/
|
||||
test-rust:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
dependencies: rust,runtime
|
||||
- name: run tests
|
||||
run: |
|
||||
cargo llvm-cov --no-report nextest --workspace
|
||||
cargo llvm-cov report --codecov --output-path target/llvm-cov-target/rust.json
|
||||
- uses: ./.github/actions/test-results
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
files: target/llvm-cov-target/rust.json
|
||||
flags: rust
|
||||
- if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: test-rust
|
||||
path: target/llvm-cov-target/rust.json
|
||||
flags: e2e
|
||||
ci-core-mark:
|
||||
if: always()
|
||||
needs:
|
||||
- lint
|
||||
- test-gen
|
||||
- test-migrations
|
||||
- test-migrations-from-stable
|
||||
- test-unittest
|
||||
@@ -367,46 +199,105 @@ jobs:
|
||||
- test-e2e
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1
|
||||
with:
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
- run: echo mark
|
||||
build:
|
||||
permissions:
|
||||
# Needed to upload container images to ghcr.io
|
||||
packages: write
|
||||
# Needed for attestation
|
||||
id-token: write
|
||||
attestations: write
|
||||
# Needed for checkout
|
||||
contents: read
|
||||
needs: ci-core-mark
|
||||
uses: ./.github/workflows/_reusable-docker-build.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
image_name: ${{ github.repository == 'goauthentik/authentik-internal' && 'ghcr.io/goauthentik/internal-server' || 'ghcr.io/goauthentik/dev-server' }}
|
||||
release: false
|
||||
pr-comment:
|
||||
needs:
|
||||
- build
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
permissions:
|
||||
# Needed to write comments on PRs
|
||||
pull-requests: write
|
||||
# Needed to upload contianer images to ghcr.io
|
||||
packages: write
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.0.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: prepare variables
|
||||
uses: ./.github/actions/docker-push-variables
|
||||
id: ev
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_CORP_USERNAME }}
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
- name: Login to Container Registry
|
||||
uses: docker/login-action@v3
|
||||
if: ${{ steps.ev.outputs.shouldBuild == 'true' }}
|
||||
with:
|
||||
image-name: ghcr.io/goauthentik/dev-server
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: generate ts client
|
||||
run: make gen-client-ts
|
||||
- name: Build Docker Image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
secrets: |
|
||||
GEOIPUPDATE_ACCOUNT_ID=${{ secrets.GEOIPUPDATE_ACCOUNT_ID }}
|
||||
GEOIPUPDATE_LICENSE_KEY=${{ secrets.GEOIPUPDATE_LICENSE_KEY }}
|
||||
push: ${{ steps.ev.outputs.shouldBuild == 'true' }}
|
||||
tags: |
|
||||
ghcr.io/goauthentik/dev-server:gh-${{ steps.ev.outputs.branchNameContainer }}
|
||||
ghcr.io/goauthentik/dev-server:gh-${{ steps.ev.outputs.sha }}
|
||||
ghcr.io/goauthentik/dev-server:gh-${{ steps.ev.outputs.branchNameContainer }}-${{ steps.ev.outputs.timestamp }}-${{ steps.ev.outputs.shortHash }}
|
||||
build-args: |
|
||||
GIT_BUILD_HASH=${{ steps.ev.outputs.sha }}
|
||||
VERSION=${{ steps.ev.outputs.version }}
|
||||
VERSION_FAMILY=${{ steps.ev.outputs.versionFamily }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
- name: Comment on PR
|
||||
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
|
||||
if: github.event_name == 'pull_request'
|
||||
continue-on-error: true
|
||||
uses: ./.github/actions/comment-pr-instructions
|
||||
with:
|
||||
tag: ${{ steps.ev.outputs.imageMainTag }}
|
||||
tag: gh-${{ steps.ev.outputs.branchNameContainer }}-${{ steps.ev.outputs.timestamp }}-${{ steps.ev.outputs.shortHash }}
|
||||
build-arm64:
|
||||
needs: ci-core-mark
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed to upload contianer images to ghcr.io
|
||||
packages: write
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.0.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: prepare variables
|
||||
uses: ./.github/actions/docker-push-variables
|
||||
id: ev
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
- name: Login to Container Registry
|
||||
uses: docker/login-action@v3
|
||||
if: ${{ steps.ev.outputs.shouldBuild == 'true' }}
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: generate ts client
|
||||
run: make gen-client-ts
|
||||
- name: Build Docker Image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
secrets: |
|
||||
GEOIPUPDATE_ACCOUNT_ID=${{ secrets.GEOIPUPDATE_ACCOUNT_ID }}
|
||||
GEOIPUPDATE_LICENSE_KEY=${{ secrets.GEOIPUPDATE_LICENSE_KEY }}
|
||||
push: ${{ steps.ev.outputs.shouldBuild == 'true' }}
|
||||
tags: |
|
||||
ghcr.io/goauthentik/dev-server:gh-${{ steps.ev.outputs.branchNameContainer }}-arm64
|
||||
ghcr.io/goauthentik/dev-server:gh-${{ steps.ev.outputs.sha }}-arm64
|
||||
ghcr.io/goauthentik/dev-server:gh-${{ steps.ev.outputs.branchNameContainer }}-${{ steps.ev.outputs.timestamp }}-${{ steps.ev.outputs.shortHash }}-arm64
|
||||
build-args: |
|
||||
GIT_BUILD_HASH=${{ steps.ev.outputs.sha }}
|
||||
VERSION=${{ steps.ev.outputs.version }}
|
||||
VERSION_FAMILY=${{ steps.ev.outputs.versionFamily }}
|
||||
platforms: linux/arm64
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
87
.github/workflows/ci-outpost.yml
vendored
87
.github/workflows/ci-outpost.yml
vendored
@@ -1,5 +1,4 @@
|
||||
---
|
||||
name: CI - Outpost
|
||||
name: authentik-ci-outpost
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -12,17 +11,12 @@ on:
|
||||
- main
|
||||
- version-*
|
||||
|
||||
env:
|
||||
POSTGRES_DB: authentik
|
||||
POSTGRES_USER: authentik
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
|
||||
jobs:
|
||||
lint-golint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
- name: Prepare and generate API
|
||||
@@ -31,37 +25,35 @@ jobs:
|
||||
mkdir -p web/dist
|
||||
mkdir -p website/help
|
||||
touch web/dist/test website/help/test
|
||||
- name: Generate API
|
||||
run: make gen-client-go
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v8
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: latest
|
||||
version: v1.54.2
|
||||
args: --timeout 5000s --verbose
|
||||
skip-cache: true
|
||||
test-unittest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- name: prepare database
|
||||
run: |
|
||||
uv run make migrate
|
||||
- name: Generate API
|
||||
run: make gen-client-go
|
||||
- name: Go unittests
|
||||
run: |
|
||||
go test -timeout 0 -v -race -coverprofile=coverage.out -covermode=atomic -cover ./...
|
||||
ci-outpost-mark:
|
||||
if: always()
|
||||
needs:
|
||||
- lint-golint
|
||||
- test-unittest
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1
|
||||
with:
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
- run: echo mark
|
||||
build-container:
|
||||
timeout-minutes: 120
|
||||
needs:
|
||||
@@ -73,56 +65,48 @@ jobs:
|
||||
- proxy
|
||||
- ldap
|
||||
- radius
|
||||
- rac
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed to upload container images to ghcr.io
|
||||
# Needed to upload contianer images to ghcr.io
|
||||
packages: write
|
||||
# Needed for attestation
|
||||
id-token: write
|
||||
attestations: write
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
uses: docker/setup-qemu-action@v3.0.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: prepare variables
|
||||
uses: ./.github/actions/docker-push-variables
|
||||
id: ev
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_CORP_USERNAME }}
|
||||
with:
|
||||
image-name: ghcr.io/goauthentik/dev-${{ matrix.type }}
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
- name: Login to Container Registry
|
||||
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
uses: docker/login-action@v3
|
||||
if: ${{ steps.ev.outputs.shouldBuild == 'true' }}
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Generate API
|
||||
run: make gen-client-go
|
||||
- name: Build Docker Image
|
||||
id: push
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
tags: ${{ steps.ev.outputs.imageTags }}
|
||||
file: lifecycle/container/${{ matrix.type }}.Dockerfile
|
||||
push: ${{ steps.ev.outputs.shouldPush == 'true' }}
|
||||
push: ${{ steps.ev.outputs.shouldBuild == 'true' }}
|
||||
tags: |
|
||||
ghcr.io/goauthentik/dev-${{ matrix.type }}:gh-${{ steps.ev.outputs.branchNameContainer }}
|
||||
ghcr.io/goauthentik/dev-${{ matrix.type }}:gh-${{ steps.ev.outputs.sha }}
|
||||
file: ${{ matrix.type }}.Dockerfile
|
||||
build-args: |
|
||||
GIT_BUILD_HASH=${{ steps.ev.outputs.sha }}
|
||||
VERSION=${{ steps.ev.outputs.version }}
|
||||
VERSION_FAMILY=${{ steps.ev.outputs.versionFamily }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
context: .
|
||||
cache-from: type=registry,ref=ghcr.io/goauthentik/dev-${{ matrix.type }}:buildcache
|
||||
cache-to: ${{ steps.ev.outputs.shouldPush == 'true' && format('type=registry,ref=ghcr.io/goauthentik/dev-{0}:buildcache,mode=max', matrix.type) || '' }}
|
||||
- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v3
|
||||
id: attest
|
||||
if: ${{ steps.ev.outputs.shouldPush == 'true' }}
|
||||
with:
|
||||
subject-name: ${{ steps.ev.outputs.attestImageNames }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
build-binary:
|
||||
timeout-minutes: 120
|
||||
needs:
|
||||
@@ -135,21 +119,22 @@ jobs:
|
||||
- proxy
|
||||
- ldap
|
||||
- radius
|
||||
- rac
|
||||
goos: [linux]
|
||||
goarch: [amd64, arm64]
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: web/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: web/package-lock.json
|
||||
- name: Generate API
|
||||
run: make gen-client-go
|
||||
- name: Build web
|
||||
working-directory: web/
|
||||
run: |
|
||||
|
||||
109
.github/workflows/ci-web.yml
vendored
109
.github/workflows/ci-web.yml
vendored
@@ -1,5 +1,4 @@
|
||||
---
|
||||
name: CI - Web
|
||||
name: authentik-ci-web
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -13,73 +12,111 @@ on:
|
||||
- version-*
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
lint-eslint:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
command:
|
||||
- lint
|
||||
- lint:lockfile
|
||||
- tsc
|
||||
- prettier-check
|
||||
project:
|
||||
- web
|
||||
include:
|
||||
- command: tsc
|
||||
project: web
|
||||
- command: lit-analyse
|
||||
project: web
|
||||
- tests/wdio
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: ${{ matrix.project }}/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: ${{ matrix.project }}/package-lock.json
|
||||
- working-directory: ${{ matrix.project }}/
|
||||
run: |
|
||||
npm ci
|
||||
- name: Lint
|
||||
run: npm ci
|
||||
- name: Generate API
|
||||
run: make gen-client-ts
|
||||
- name: Eslint
|
||||
working-directory: ${{ matrix.project }}/
|
||||
run: npm run ${{ matrix.command }}
|
||||
build:
|
||||
run: npm run lint
|
||||
lint-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: web/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: web/package-lock.json
|
||||
- working-directory: web/
|
||||
run: npm ci
|
||||
- name: build
|
||||
- name: Generate API
|
||||
run: make gen-client-ts
|
||||
- name: TSC
|
||||
working-directory: web/
|
||||
run: npm run build
|
||||
ci-web-mark:
|
||||
if: always()
|
||||
needs:
|
||||
- build
|
||||
- lint
|
||||
run: npm run tsc
|
||||
lint-prettier:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
project:
|
||||
- web
|
||||
- tests/wdio
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: ${{ matrix.project }}/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: ${{ matrix.project }}/package-lock.json
|
||||
- working-directory: ${{ matrix.project }}/
|
||||
run: npm ci
|
||||
- name: Generate API
|
||||
run: make gen-client-ts
|
||||
- name: prettier
|
||||
working-directory: ${{ matrix.project }}/
|
||||
run: npm run prettier-check
|
||||
lint-lit-analyse:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # release/v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
test:
|
||||
node-version-file: web/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: web/package-lock.json
|
||||
- working-directory: web/
|
||||
run: |
|
||||
npm ci
|
||||
# lit-analyse doesn't understand path rewrites, so make it
|
||||
# belive it's an actual module
|
||||
cd node_modules/@goauthentik
|
||||
ln -s ../../src/ web
|
||||
- name: Generate API
|
||||
run: make gen-client-ts
|
||||
- name: lit-analyse
|
||||
working-directory: web/
|
||||
run: npm run lit-analyse
|
||||
ci-web-mark:
|
||||
needs:
|
||||
- lint-eslint
|
||||
- lint-prettier
|
||||
- lint-lit-analyse
|
||||
- lint-build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo mark
|
||||
build:
|
||||
needs:
|
||||
- ci-web-mark
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: web/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: web/package-lock.json
|
||||
- working-directory: web/
|
||||
run: npm ci
|
||||
- name: test
|
||||
- name: Generate API
|
||||
run: make gen-client-ts
|
||||
- name: build
|
||||
working-directory: web/
|
||||
run: npm run test || exit 0
|
||||
run: npm run build
|
||||
|
||||
71
.github/workflows/ci-website.yml
vendored
Normal file
71
.github/workflows/ci-website.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
name: authentik-ci-website
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- next
|
||||
- version-*
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- version-*
|
||||
|
||||
jobs:
|
||||
lint-prettier:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: website/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: website/package-lock.json
|
||||
- working-directory: website/
|
||||
run: npm ci
|
||||
- name: prettier
|
||||
working-directory: website/
|
||||
run: npm run prettier-check
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: website/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: website/package-lock.json
|
||||
- working-directory: website/
|
||||
run: npm ci
|
||||
- name: test
|
||||
working-directory: website/
|
||||
run: npm test
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: ${{ matrix.job }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- build
|
||||
- build-docs-only
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: website/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: website/package-lock.json
|
||||
- working-directory: website/
|
||||
run: npm ci
|
||||
- name: build
|
||||
working-directory: website/
|
||||
run: npm run ${{ matrix.job }}
|
||||
ci-website-mark:
|
||||
needs:
|
||||
- lint-prettier
|
||||
- test
|
||||
- build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo mark
|
||||
36
.github/workflows/codeql-analysis.yml
vendored
Normal file
36
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, "*", next, version*]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
schedule:
|
||||
- cron: "30 6 * * 5"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ["go", "javascript", "python"]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
62
.github/workflows/gen-image-compress.yml
vendored
62
.github/workflows/gen-image-compress.yml
vendored
@@ -1,62 +0,0 @@
|
||||
---
|
||||
name: Gen - Compress images
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "**.jpg"
|
||||
- "**.jpeg"
|
||||
- "**.png"
|
||||
- "**.webp"
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.jpg"
|
||||
- "**.jpeg"
|
||||
- "**.png"
|
||||
- "**.webp"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
compress:
|
||||
name: compress
|
||||
runs-on: ubuntu-latest
|
||||
# Don't run on forks. Token will not be available. Will run on main and open a PR anyway
|
||||
if: |
|
||||
github.repository == 'goauthentik/authentik' &&
|
||||
(github.event_name != 'pull_request' ||
|
||||
github.event.pull_request.head.repo.full_name == github.repository)
|
||||
steps:
|
||||
- id: generate_token
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
- name: Compress images
|
||||
id: compress
|
||||
uses: calibreapp/image-actions@e2cc8db5d49c849e00844dfebf01438318e96fa2 # main
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
compressOnly: ${{ github.event_name != 'pull_request' }}
|
||||
- uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v7
|
||||
if: "${{ github.event_name != 'pull_request' && steps.compress.outputs.markdown != '' }}"
|
||||
id: cpr
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
title: "*: Auto compress images"
|
||||
branch-suffix: timestamp
|
||||
commit-message: "*: compress images"
|
||||
body: ${{ steps.compress.outputs.markdown }}
|
||||
delete-branch: true
|
||||
signoff: true
|
||||
labels: dependencies
|
||||
- uses: peter-evans/enable-pull-request-automerge@a660677d5469627102a1c1e11409dd063606628d # v3
|
||||
if: "${{ github.event_name != 'pull_request' && steps.compress.outputs.markdown != '' }}"
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
merge-method: squash
|
||||
46
.github/workflows/gen-update-webauthn-mds.yml
vendored
46
.github/workflows/gen-update-webauthn-mds.yml
vendored
@@ -1,46 +0,0 @@
|
||||
---
|
||||
name: Gen - Webauthn MDS
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "30 1 1,15 * *"
|
||||
|
||||
env:
|
||||
POSTGRES_DB: authentik
|
||||
POSTGRES_USER: authentik
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: generate_token
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- run: uv run ak update_webauthn_mds
|
||||
- uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v7
|
||||
id: cpr
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
branch: update-fido-mds-client
|
||||
commit-message: "stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs"
|
||||
title: "stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs"
|
||||
body: "stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs"
|
||||
delete-branch: true
|
||||
signoff: true
|
||||
# ID from https://api.github.com/users/authentik-automation[bot]
|
||||
author: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
|
||||
labels: dependencies
|
||||
- uses: peter-evans/enable-pull-request-automerge@a660677d5469627102a1c1e11409dd063606628d # v3
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
merge-method: squash
|
||||
36
.github/workflows/gh-cherry-pick.yml
vendored
36
.github/workflows/gh-cherry-pick.yml
vendored
@@ -1,36 +0,0 @@
|
||||
name: GH - Cherry-pick
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [closed, labeled]
|
||||
|
||||
jobs:
|
||||
cherry-pick:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: app-token
|
||||
name: Generate app token
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
|
||||
if: ${{ env.GH_APP_ID != '' }}
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
|
||||
env:
|
||||
GH_APP_ID: ${{ secrets.GH_APP_ID }}
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
if: ${{ steps.app-token.outcome != 'skipped' }}
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: "${{ steps.app-token.outputs.token }}"
|
||||
- id: get-user-id
|
||||
if: ${{ steps.app-token.outcome != 'skipped' }}
|
||||
name: Get GitHub app user ID
|
||||
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GH_TOKEN: "${{ steps.app-token.outputs.token }}"
|
||||
- uses: ./.github/actions/cherry-pick
|
||||
if: ${{ steps.app-token.outcome != 'skipped' }}
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
git_user: ${{ steps.app-token.outputs.app-slug }}[bot]
|
||||
git_user_email: '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com'
|
||||
39
.github/workflows/gh-gha-cache-cleanup.yml
vendored
39
.github/workflows/gh-gha-cache-cleanup.yml
vendored
@@ -1,39 +0,0 @@
|
||||
---
|
||||
# See https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries
|
||||
name: GH - Cleanup actions cache after PR is closed
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
|
||||
permissions:
|
||||
# Permission to delete cache
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
|
||||
- name: Cleanup
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
|
||||
REPO=${{ github.repository }}
|
||||
BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge"
|
||||
|
||||
echo "Fetching list of cache key"
|
||||
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
|
||||
|
||||
# Setting this to not fail the workflow while deleting cache keys.
|
||||
set +e
|
||||
echo "Deleting caches..."
|
||||
for cacheKey in $cacheKeysForPR; do
|
||||
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
|
||||
done
|
||||
echo "Done"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
32
.github/workflows/gh-ghcr-retention.yml
vendored
32
.github/workflows/gh-ghcr-retention.yml
vendored
@@ -1,32 +0,0 @@
|
||||
---
|
||||
name: GH - GHCR retention
|
||||
|
||||
on:
|
||||
# schedule:
|
||||
# - cron: "0 0 * * *" # every day at midnight
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry-run:
|
||||
type: boolean
|
||||
description: Enable dry-run mode
|
||||
|
||||
jobs:
|
||||
clean-ghcr:
|
||||
name: Delete old unused container images
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: generate_token
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
|
||||
- name: Delete 'dev' containers older than a week
|
||||
uses: snok/container-retention-policy@3b0972b2276b171b212f8c4efbca59ebba26eceb # v3.0.1
|
||||
with:
|
||||
image-names: dev-server,dev-ldap,dev-proxy
|
||||
image-tags: "!gh-next,!gh-main"
|
||||
cut-off: One week ago UTC
|
||||
account: goauthentik
|
||||
tag-selection: untagged
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
dry-run: ${{ inputs.dry-run }}
|
||||
34
.github/workflows/gha-cache-cleanup.yml
vendored
Normal file
34
.github/workflows/gha-cache-cleanup.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
# See https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries
|
||||
name: Cleanup cache after PR is closed
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cleanup
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
|
||||
REPO=${{ github.repository }}
|
||||
BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge"
|
||||
|
||||
echo "Fetching list of cache key"
|
||||
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
|
||||
|
||||
# Setting this to not fail the workflow while deleting cache keys.
|
||||
set +e
|
||||
echo "Deleting caches..."
|
||||
for cacheKey in $cacheKeysForPR; do
|
||||
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
|
||||
done
|
||||
echo "Done"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
27
.github/workflows/ghcr-retention.yml
vendored
Normal file
27
.github/workflows/ghcr-retention.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: ghcr-retention
|
||||
|
||||
on:
|
||||
# schedule:
|
||||
# - cron: "0 0 * * *" # every day at midnight
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
clean-ghcr:
|
||||
name: Delete old unused container images
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: generate_token
|
||||
uses: tibdex/github-app-token@v2
|
||||
with:
|
||||
app_id: ${{ secrets.GH_APP_ID }}
|
||||
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- name: Delete 'dev' containers older than a week
|
||||
uses: snok/container-retention-policy@v2
|
||||
with:
|
||||
image-names: dev-server,dev-ldap,dev-proxy
|
||||
cut-off: One week ago UTC
|
||||
account-type: org
|
||||
org-name: goauthentik
|
||||
untagged-only: false
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
skip-tags: gh-next,gh-main
|
||||
61
.github/workflows/image-compress.yml
vendored
Normal file
61
.github/workflows/image-compress.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: authentik-compress-images
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "**.jpg"
|
||||
- "**.jpeg"
|
||||
- "**.png"
|
||||
- "**.webp"
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.jpg"
|
||||
- "**.jpeg"
|
||||
- "**.png"
|
||||
- "**.webp"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
compress:
|
||||
name: compress
|
||||
runs-on: ubuntu-latest
|
||||
# Don't run on forks. Token will not be available. Will run on main and open a PR anyway
|
||||
if: |
|
||||
github.repository == 'goauthentik/authentik' &&
|
||||
(github.event_name != 'pull_request' ||
|
||||
github.event.pull_request.head.repo.full_name == github.repository)
|
||||
steps:
|
||||
- id: generate_token
|
||||
uses: tibdex/github-app-token@v2
|
||||
with:
|
||||
app_id: ${{ secrets.GH_APP_ID }}
|
||||
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
- name: Compress images
|
||||
id: compress
|
||||
uses: calibreapp/image-actions@main
|
||||
with:
|
||||
githubToken: ${{ steps.generate_token.outputs.token }}
|
||||
compressOnly: ${{ github.event_name != 'pull_request' }}
|
||||
- uses: peter-evans/create-pull-request@v5
|
||||
if: "${{ github.event_name != 'pull_request' && steps.compress.outputs.markdown != '' }}"
|
||||
id: cpr
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
title: "*: Auto compress images"
|
||||
branch-suffix: timestamp
|
||||
commit-messsage: "*: compress images"
|
||||
body: ${{ steps.compress.outputs.markdown }}
|
||||
delete-branch: true
|
||||
signoff: true
|
||||
- uses: peter-evans/enable-pull-request-automerge@v3
|
||||
if: "${{ github.event_name != 'pull_request' && steps.compress.outputs.markdown != '' }}"
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
merge-method: squash
|
||||
56
.github/workflows/packages-npm-publish.yml
vendored
56
.github/workflows/packages-npm-publish.yml
vendored
@@ -1,56 +0,0 @@
|
||||
---
|
||||
name: Packages - Publish NPM packages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- packages/tsconfig/**
|
||||
- packages/eslint-config/**
|
||||
- packages/prettier-config/**
|
||||
- packages/docusaurus-config/**
|
||||
- packages/esbuild-plugin-live-reload/**
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
# Required for NPM OIDC trusted publisher
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package:
|
||||
# The order of the `*config` packages should not be changed, as they depend on each other.
|
||||
- packages/tsconfig
|
||||
- packages/eslint-config
|
||||
- packages/prettier-config
|
||||
- packages/docusaurus-config
|
||||
- packages/logger-js
|
||||
- packages/esbuild-plugin-live-reload
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
|
||||
with:
|
||||
node-version-file: ${{ matrix.package }}/package.json
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # 24d32ffd492484c1d75e0c0b894501ddb9d30d62
|
||||
with:
|
||||
files: |
|
||||
${{ matrix.package }}/package.json
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
- name: Publish package
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
working-directory: ${{ matrix.package }}
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
npm publish
|
||||
31
.github/workflows/publish-source-docs.yml
vendored
Normal file
31
.github/workflows/publish-source-docs.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: authentik-publish-source-docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
POSTGRES_DB: authentik
|
||||
POSTGRES_USER: authentik
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
|
||||
jobs:
|
||||
publish-source-docs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 120
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- name: generate docs
|
||||
run: |
|
||||
poetry run make migrate
|
||||
poetry run ak build_source_docs
|
||||
- name: Publish
|
||||
uses: netlify/actions/cli@master
|
||||
with:
|
||||
args: deploy --dir=source_docs --prod
|
||||
env:
|
||||
NETLIFY_SITE_ID: eb246b7b-1d83-4f69-89f7-01a936b4ca59
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
37
.github/workflows/qa-codeql.yml
vendored
37
.github/workflows/qa-codeql.yml
vendored
@@ -1,37 +0,0 @@
|
||||
---
|
||||
name: QA - CodeQL
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, next, version*]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
schedule:
|
||||
- cron: "30 6 * * 5"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ["go", "javascript", "python"]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
30
.github/workflows/qa-semgrep.yml
vendored
30
.github/workflows/qa-semgrep.yml
vendored
@@ -1,30 +0,0 @@
|
||||
---
|
||||
name: QA - Semgrep
|
||||
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
pull_request: {}
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
paths:
|
||||
- .github/workflows/qa-semgrep.yml
|
||||
schedule:
|
||||
# random HH:MM to avoid a load spike on GitHub Actions at 00:00
|
||||
- cron: '12 15 * * *'
|
||||
|
||||
jobs:
|
||||
semgrep:
|
||||
name: semgrep/ci
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
|
||||
container:
|
||||
image: semgrep/semgrep
|
||||
if: (github.actor != 'dependabot[bot]')
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- run: semgrep ci
|
||||
86
.github/workflows/release-branch-off.yml
vendored
86
.github/workflows/release-branch-off.yml
vendored
@@ -1,86 +0,0 @@
|
||||
---
|
||||
name: Release - Branch-off
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
next_version:
|
||||
description: Next major version (for example, if releasing 2042.2, this is 2042.4)
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
POSTGRES_DB: authentik
|
||||
POSTGRES_USER: authentik
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
|
||||
jobs:
|
||||
check-inputs:
|
||||
name: Check inputs validity
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
echo "${{ inputs.next_version }}" | grep -E "^[0-9]{4}\.[0-9]{1,2}$"
|
||||
branch-off:
|
||||
name: Branch-off
|
||||
needs:
|
||||
- check-inputs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: app-token
|
||||
name: Generate app token
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
|
||||
- name: Checkout main
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
with:
|
||||
ref: main
|
||||
token: "${{ steps.app-token.outputs.token }}"
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
dependencies: python
|
||||
- name: Create version branch
|
||||
env:
|
||||
GH_TOKEN: "${{ steps.app-token.outputs.token }}"
|
||||
run: |
|
||||
current_major_version="$(uv version --short | grep -oE "^[0-9]{4}\.[0-9]{1,2}")"
|
||||
git checkout -b "version-${current_major_version}"
|
||||
git push origin "version-${current_major_version}"
|
||||
gh label create "backport/version-${current_major_version}" --description "Add this label to PRs to backport changes to version-${current_major_version}" --color "fbca04"
|
||||
bump-version-pr:
|
||||
name: Open version bump PR
|
||||
needs:
|
||||
- branch-off
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: generate_token
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
|
||||
- name: Checkout main
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
with:
|
||||
ref: main
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- name: Run migrations
|
||||
run: make migrate
|
||||
- name: Bump version
|
||||
run: "make bump version=${{ inputs.next_version }}.0-rc1"
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v7
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
branch: release-bump-${{ inputs.next_version }}
|
||||
commit-message: "root: bump version to ${{ inputs.next_version }}.0-rc1"
|
||||
title: "root: bump version to ${{ inputs.next_version }}.0-rc1"
|
||||
body: "root: bump version to ${{ inputs.next_version }}.0-rc1"
|
||||
delete-branch: true
|
||||
signoff: true
|
||||
# ID from https://api.github.com/users/authentik-automation[bot]
|
||||
author: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
|
||||
5
.github/workflows/release-next-branch.yml
vendored
5
.github/workflows/release-next-branch.yml
vendored
@@ -1,5 +1,4 @@
|
||||
---
|
||||
name: Release - Update next branch
|
||||
name: authentik-on-release-next-branch
|
||||
|
||||
on:
|
||||
schedule:
|
||||
@@ -15,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
environment: internal-production
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
- run: |
|
||||
|
||||
197
.github/workflows/release-publish.yml
vendored
197
.github/workflows/release-publish.yml
vendored
@@ -1,5 +1,4 @@
|
||||
---
|
||||
name: Release - On publish
|
||||
name: authentik-on-release
|
||||
|
||||
on:
|
||||
release:
|
||||
@@ -7,73 +6,58 @@ on:
|
||||
|
||||
jobs:
|
||||
build-server:
|
||||
uses: ./.github/workflows/_reusable-docker-build.yml
|
||||
secrets: inherit
|
||||
permissions:
|
||||
contents: read
|
||||
# Needed to upload container images to ghcr.io
|
||||
packages: write
|
||||
# Needed for attestation
|
||||
id-token: write
|
||||
attestations: write
|
||||
with:
|
||||
image_name: ghcr.io/goauthentik/server,authentik/server
|
||||
release: true
|
||||
registry_dockerhub: true
|
||||
registry_ghcr: true
|
||||
build-docs:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
# Needed to upload container images to ghcr.io
|
||||
# Needed to upload contianer images to ghcr.io
|
||||
packages: write
|
||||
# Needed for attestation
|
||||
id-token: write
|
||||
attestations: write
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
uses: docker/setup-qemu-action@v3.0.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: prepare variables
|
||||
uses: ./.github/actions/docker-push-variables
|
||||
id: ev
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_CORP_USERNAME }}
|
||||
- name: Docker Login Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
image-name: ghcr.io/goauthentik/docs
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: make empty clients
|
||||
run: |
|
||||
mkdir -p ./gen-ts-api
|
||||
mkdir -p ./gen-go-api
|
||||
- name: Build Docker Image
|
||||
id: push
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
tags: ${{ steps.ev.outputs.imageTags }}
|
||||
file: website/Dockerfile
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64
|
||||
context: .
|
||||
- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v3
|
||||
id: attest
|
||||
if: true
|
||||
with:
|
||||
subject-name: ${{ steps.ev.outputs.attestImageNames }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
push: ${{ github.event_name == 'release' }}
|
||||
secrets: |
|
||||
GEOIPUPDATE_ACCOUNT_ID=${{ secrets.GEOIPUPDATE_ACCOUNT_ID }}
|
||||
GEOIPUPDATE_LICENSE_KEY=${{ secrets.GEOIPUPDATE_LICENSE_KEY }}
|
||||
tags: |
|
||||
beryju/authentik:${{ steps.ev.outputs.version }},
|
||||
beryju/authentik:${{ steps.ev.outputs.versionFamily }},
|
||||
beryju/authentik:latest,
|
||||
ghcr.io/goauthentik/server:${{ steps.ev.outputs.version }},
|
||||
ghcr.io/goauthentik/server:${{ steps.ev.outputs.versionFamily }},
|
||||
ghcr.io/goauthentik/server:latest
|
||||
platforms: linux/amd64,linux/arm64
|
||||
build-args: |
|
||||
VERSION=${{ steps.ev.outputs.version }}
|
||||
VERSION_FAMILY=${{ steps.ev.outputs.versionFamily }}
|
||||
build-outpost:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
# Needed to upload container images to ghcr.io
|
||||
# Needed to upload contianer images to ghcr.io
|
||||
packages: write
|
||||
# Needed for attestation
|
||||
id-token: write
|
||||
attestations: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -81,56 +65,50 @@ jobs:
|
||||
- proxy
|
||||
- ldap
|
||||
- radius
|
||||
- rac
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
|
||||
with:
|
||||
node-version-file: web/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: web/package-lock.json
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
|
||||
uses: docker/setup-qemu-action@v3.0.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: prepare variables
|
||||
uses: ./.github/actions/docker-push-variables
|
||||
id: ev
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_CORP_USERNAME }}
|
||||
with:
|
||||
image-name: ghcr.io/goauthentik/${{ matrix.type }},authentik/${{ matrix.type }}
|
||||
- name: make empty clients
|
||||
run: |
|
||||
mkdir -p ./gen-ts-api
|
||||
mkdir -p ./gen-go-api
|
||||
- name: Docker Login Registry
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_CORP_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_CORP_PASSWORD }}
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build Docker Image
|
||||
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
|
||||
id: push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
push: true
|
||||
build-args: |
|
||||
VERSION=${{ github.ref }}
|
||||
tags: ${{ steps.ev.outputs.imageTags }}
|
||||
file: lifecycle/container/${{ matrix.type }}.Dockerfile
|
||||
push: ${{ github.event_name == 'release' }}
|
||||
tags: |
|
||||
beryju/authentik-${{ matrix.type }}:${{ steps.ev.outputs.version }},
|
||||
beryju/authentik-${{ matrix.type }}:${{ steps.ev.outputs.versionFamily }},
|
||||
beryju/authentik-${{ matrix.type }}:latest,
|
||||
ghcr.io/goauthentik/${{ matrix.type }}:${{ steps.ev.outputs.version }},
|
||||
ghcr.io/goauthentik/${{ matrix.type }}:${{ steps.ev.outputs.versionFamily }},
|
||||
ghcr.io/goauthentik/${{ matrix.type }}:latest
|
||||
file: ${{ matrix.type }}.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
context: .
|
||||
- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v3
|
||||
id: attest
|
||||
with:
|
||||
subject-name: ${{ steps.ev.outputs.attestImageNames }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
build-args: |
|
||||
VERSION=${{ steps.ev.outputs.version }}
|
||||
VERSION_FAMILY=${{ steps.ev.outputs.versionFamily }}
|
||||
build-outpost-binary:
|
||||
timeout-minutes: 120
|
||||
runs-on: ubuntu-latest
|
||||
@@ -147,22 +125,19 @@ jobs:
|
||||
goos: [linux, darwin]
|
||||
goarch: [amd64, arm64]
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v5
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: web/package.json
|
||||
cache: "npm"
|
||||
cache-dependency-path: web/package-lock.json
|
||||
- name: Install web dependencies
|
||||
working-directory: web/
|
||||
run: |
|
||||
npm ci
|
||||
- name: Build web
|
||||
working-directory: web/
|
||||
run: |
|
||||
npm ci
|
||||
npm run build-proxy
|
||||
- name: Build outpost
|
||||
run: |
|
||||
@@ -172,33 +147,12 @@ jobs:
|
||||
export CGO_ENABLED=0
|
||||
go build -tags=outpost_static_embed -v -o ./authentik-outpost-${{ matrix.type }}_${{ matrix.goos }}_${{ matrix.goarch }} ./cmd/${{ matrix.type }}
|
||||
- name: Upload binaries to release
|
||||
uses: svenstaro/upload-release-action@29e53e917877a24fad85510ded594ab3c9ca12de # v2
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ./authentik-outpost-${{ matrix.type }}_${{ matrix.goos }}_${{ matrix.goarch }}
|
||||
asset_name: authentik-outpost-${{ matrix.type }}_${{ matrix.goos }}_${{ matrix.goarch }}
|
||||
tag: ${{ github.ref }}
|
||||
upload-aws-cfn-template:
|
||||
permissions:
|
||||
# Needed for AWS login
|
||||
id-token: write
|
||||
contents: read
|
||||
needs:
|
||||
- build-server
|
||||
- build-outpost
|
||||
env:
|
||||
AWS_REGION: eu-central-1
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: aws-actions/configure-aws-credentials@ec61189d14ec14c8efccab744f656cffd0e33f37 # v6.1.0
|
||||
with:
|
||||
role-to-assume: "arn:aws:iam::016170277896:role/github_goauthentik_authentik"
|
||||
aws-region: ${{ env.AWS_REGION }}
|
||||
- name: Upload template
|
||||
run: |
|
||||
aws s3 cp --acl=public-read lifecycle/aws/template.yaml s3://authentik-cloudformation-templates/authentik.ecs.${{ github.ref }}.yaml
|
||||
aws s3 cp --acl=public-read lifecycle/aws/template.yaml s3://authentik-cloudformation-templates/authentik.ecs.latest.yaml
|
||||
test-release:
|
||||
needs:
|
||||
- build-server
|
||||
@@ -206,15 +160,15 @@ jobs:
|
||||
- build-outpost-binary
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run test suite in final docker images
|
||||
run: |
|
||||
echo "PG_PASS=$(openssl rand 32 | base64 -w 0)" >> lifecycle/container/.env
|
||||
echo "AUTHENTIK_SECRET_KEY=$(openssl rand 32 | base64 -w 0)" >> lifecycle/container/.env
|
||||
docker compose -f lifecycle/container/compose.yml pull -q
|
||||
docker compose -f lifecycle/container/compose.yml up --no-start
|
||||
docker compose -f lifecycle/container/compose.yml start postgresql
|
||||
docker compose -f lifecycle/container/compose.yml run -u root server test-all
|
||||
echo "PG_PASS=$(openssl rand -base64 32)" >> .env
|
||||
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 32)" >> .env
|
||||
docker-compose pull -q
|
||||
docker-compose up --no-start
|
||||
docker-compose start postgresql redis
|
||||
docker-compose run -u root server test-all
|
||||
sentry-release:
|
||||
needs:
|
||||
- build-server
|
||||
@@ -222,27 +176,24 @@ jobs:
|
||||
- build-outpost-binary
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: prepare variables
|
||||
uses: ./.github/actions/docker-push-variables
|
||||
id: ev
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_CORP_USERNAME }}
|
||||
with:
|
||||
image-name: ghcr.io/goauthentik/server
|
||||
- name: Get static files from docker image
|
||||
run: |
|
||||
docker pull ${{ steps.ev.outputs.imageMainName }}
|
||||
container=$(docker container create ${{ steps.ev.outputs.imageMainName }})
|
||||
docker pull ghcr.io/goauthentik/server:latest
|
||||
container=$(docker container create ghcr.io/goauthentik/server:latest)
|
||||
docker cp ${container}:web/ .
|
||||
- name: Create a Sentry.io release
|
||||
uses: getsentry/action-release@5657c9e888b4e2cc85f4d29143ea4131fde4a73a # v3
|
||||
uses: getsentry/action-release@v1
|
||||
continue-on-error: true
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_ORG: authentik-security-inc
|
||||
SENTRY_PROJECT: authentik
|
||||
with:
|
||||
release: authentik@${{ steps.ev.outputs.version }}
|
||||
version: authentik@${{ steps.ev.outputs.version }}
|
||||
sourcemaps: "./web/dist"
|
||||
url_prefix: "~/static/dist"
|
||||
|
||||
231
.github/workflows/release-tag.yml
vendored
231
.github/workflows/release-tag.yml
vendored
@@ -1,208 +1,47 @@
|
||||
---
|
||||
name: Release - Tag new version
|
||||
name: authentik-on-tag
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: Version
|
||||
required: true
|
||||
type: string
|
||||
release_reason:
|
||||
description: Release reason
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- bugfix
|
||||
- feature
|
||||
- security
|
||||
- other
|
||||
- prerelease
|
||||
|
||||
env:
|
||||
POSTGRES_DB: authentik
|
||||
POSTGRES_USER: authentik
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
push:
|
||||
tags:
|
||||
- "version/*"
|
||||
|
||||
jobs:
|
||||
check-inputs:
|
||||
name: Check inputs validity
|
||||
build:
|
||||
name: Create Release from Tag
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: check
|
||||
- uses: actions/checkout@v4
|
||||
- name: Pre-release test
|
||||
run: |
|
||||
echo "${{ inputs.version }}" | grep -E '^[0-9]{4}\.(0?[1-9]|1[0-2])\.[0-9]+(-rc[0-9]+)?$'
|
||||
echo "major_version=${{ inputs.version }}" | grep -oE "^major_version=[0-9]{4}\.[0-9]{1,2}" >> "$GITHUB_OUTPUT"
|
||||
- id: changelog-url
|
||||
run: |
|
||||
if [ "${{ inputs.release_reason }}" = "feature" ]; then
|
||||
changelog_url="https://docs.goauthentik.io/docs/releases/${{ steps.check.outputs.major_version }}"
|
||||
elif [ "${{ inputs.release_reason }}" = "prerelease" ]; then
|
||||
changelog_url="https://next.goauthentik.io/docs/releases/${{ steps.check.outputs.major_version }}"
|
||||
else
|
||||
changelog_url="https://docs.goauthentik.io/docs/releases/${{ steps.check.outputs.major_version }}#fixed-in-$(echo -n ${{ inputs.version }} | sed 's/\.//g')"
|
||||
fi
|
||||
echo "changelog_url=${changelog_url}" >> "$GITHUB_OUTPUT"
|
||||
outputs:
|
||||
major_version: "${{ steps.check.outputs.major_version }}"
|
||||
changelog_url: "${{ steps.changelog-url.outputs.changelog_url }}"
|
||||
test:
|
||||
name: Pre-release test
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- check-inputs
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
echo "PG_PASS=$(openssl rand -base64 32)" >> .env
|
||||
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 32)" >> .env
|
||||
docker buildx install
|
||||
mkdir -p ./gen-ts-api
|
||||
docker build -t testing:latest .
|
||||
echo "AUTHENTIK_IMAGE=testing" >> .env
|
||||
echo "AUTHENTIK_TAG=latest" >> .env
|
||||
docker-compose up --no-start
|
||||
docker-compose start postgresql redis
|
||||
docker-compose run -u root server test-all
|
||||
- id: generate_token
|
||||
uses: tibdex/github-app-token@v2
|
||||
with:
|
||||
ref: "version-${{ needs.check-inputs.outputs.major_version }}"
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- run: make test-docker
|
||||
bump-authentik:
|
||||
name: Bump authentik version
|
||||
needs:
|
||||
- check-inputs
|
||||
- test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: app-token
|
||||
name: Generate app token
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
|
||||
app_id: ${{ secrets.GH_APP_ID }}
|
||||
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- name: Extract version number
|
||||
id: get_version
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
|
||||
- id: get-user-id
|
||||
name: Get GitHub app user ID
|
||||
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GH_TOKEN: "${{ steps.app-token.outputs.token }}"
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
with:
|
||||
ref: "version-${{ needs.check-inputs.outputs.major_version }}"
|
||||
token: "${{ steps.app-token.outputs.token }}"
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- name: Run migrations
|
||||
run: make migrate
|
||||
- name: Bump version
|
||||
run: "make bump version=${{ inputs.version }}"
|
||||
- name: Commit and push
|
||||
run: |
|
||||
# ID from https://api.github.com/users/authentik-automation[bot]
|
||||
git config --global user.name '${{ steps.app-token.outputs.app-slug }}[bot]'
|
||||
git config --global user.email '${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com'
|
||||
git pull
|
||||
git commit -a -m "release: ${{ inputs.version }}" --allow-empty
|
||||
git tag "version/${{ inputs.version }}" HEAD -m "version/${{ inputs.version }}"
|
||||
git push --follow-tags
|
||||
github-token: ${{ steps.generate_token.outputs.token }}
|
||||
script: |
|
||||
return context.payload.ref.replace(/\/refs\/tags\/version\//, '');
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
|
||||
id: create_release
|
||||
uses: actions/create-release@v1.1.4
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
with:
|
||||
token: "${{ steps.app-token.outputs.token }}"
|
||||
tag_name: "version/${{ inputs.version }}"
|
||||
name: Release ${{ inputs.version }}
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ steps.get_version.outputs.result }}
|
||||
draft: true
|
||||
prerelease: ${{ inputs.release_reason == 'prerelease' }}
|
||||
generate_release_notes: true
|
||||
body: |
|
||||
See ${{ needs.check-inputs.outputs.changelog_url }}
|
||||
bump-helm:
|
||||
name: Bump Helm version
|
||||
if: ${{ inputs.release_reason != 'prerelease' }}
|
||||
needs:
|
||||
- bump-authentik
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: app-token
|
||||
name: Generate app token
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
|
||||
repositories: helm
|
||||
- id: get-user-id
|
||||
name: Get GitHub app user ID
|
||||
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GH_TOKEN: "${{ steps.app-token.outputs.token }}"
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
with:
|
||||
repository: "${{ github.repository_owner }}/helm"
|
||||
token: "${{ steps.app-token.outputs.token }}"
|
||||
- name: Bump version
|
||||
run: |
|
||||
sed -i 's/^version: .*/version: ${{ inputs.version }}/' charts/authentik/Chart.yaml
|
||||
sed -i 's/^appVersion: .*/appVersion: ${{ inputs.version }}/' charts/authentik/Chart.yaml
|
||||
sed -i 's/upgrade to authentik .*/upgrade to authentik ${{ inputs.version }}/' charts/authentik/Chart.yaml
|
||||
sed -E -i 's/[0-9]{4}\.[0-9]{1,2}\.[0-9]+$/${{ inputs.version }}/' charts/authentik/Chart.yaml
|
||||
./scripts/helm-docs.sh
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v7
|
||||
with:
|
||||
token: "${{ steps.app-token.outputs.token }}"
|
||||
branch: bump-${{ inputs.version }}
|
||||
commit-message: "charts/authentik: bump to ${{ inputs.version }}"
|
||||
title: "charts/authentik: bump to ${{ inputs.version }}"
|
||||
body: "charts/authentik: bump to ${{ inputs.version }}"
|
||||
delete-branch: true
|
||||
signoff: true
|
||||
author: "${{ steps.app-token.outputs.app-slug }}[bot] <${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com>"
|
||||
bump-version:
|
||||
name: Bump version repository
|
||||
if: ${{ inputs.release_reason != 'prerelease' }}
|
||||
needs:
|
||||
- check-inputs
|
||||
- bump-authentik
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: app-token
|
||||
name: Generate app token
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
|
||||
repositories: version
|
||||
- id: get-user-id
|
||||
name: Get GitHub app user ID
|
||||
run: echo "user-id=$(gh api "/users/${{ steps.app-token.outputs.app-slug }}[bot]" --jq .id)" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GH_TOKEN: "${{ steps.app-token.outputs.token }}"
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
with:
|
||||
repository: "${{ github.repository_owner }}/version"
|
||||
token: "${{ steps.app-token.outputs.token }}"
|
||||
- name: Bump version
|
||||
if: "${{ inputs.release_reason == 'feature' }}"
|
||||
run: |
|
||||
changelog_url="https://docs.goauthentik.io/docs/releases/${{ needs.check-inputs.outputs.major_version }}"
|
||||
reason="${{ inputs.release_reason }}"
|
||||
jq \
|
||||
--arg version "${{ inputs.version }}" \
|
||||
--arg changelog "See ${changelog_url}" \
|
||||
--arg changelog_url "${changelog_url}" \
|
||||
--arg reason "${reason}" \
|
||||
'.stable.version = $version | .stable.changelog = $changelog | .stable.changelog_url = $changelog_url | .stable.reason = $reason' version.json > version.new.json
|
||||
mv version.new.json version.json
|
||||
- name: Bump version
|
||||
if: "${{ inputs.release_reason != 'feature' }}"
|
||||
run: |
|
||||
changelog_url="https://docs.goauthentik.io/docs/releases/${{ needs.check-inputs.outputs.major_version }}#fixed-in-$(echo -n ${{ inputs.version}} | sed 's/\.//g')"
|
||||
reason="${{ inputs.release_reason }}"
|
||||
jq \
|
||||
--arg version "${{ inputs.version }}" \
|
||||
--arg changelog "See ${changelog_url}" \
|
||||
--arg changelog_url "${changelog_url}" \
|
||||
--arg reason "${reason}" \
|
||||
'.stable.version = $version | .stable.changelog = $changelog | .stable.changelog_url = $changelog_url | .stable.reason = $reason' version.json > version.new.json
|
||||
mv version.new.json version.json
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v7
|
||||
with:
|
||||
token: "${{ steps.app-token.outputs.token }}"
|
||||
branch: bump-${{ inputs.version }}
|
||||
commit-message: "version: bump to ${{ inputs.version }}"
|
||||
title: "version: bump to ${{ inputs.version }}"
|
||||
body: "version: bump to ${{ inputs.version }}"
|
||||
delete-branch: true
|
||||
signoff: true
|
||||
author: "${{ steps.app-token.outputs.app-slug }}[bot] <${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com>"
|
||||
prerelease: false
|
||||
|
||||
17
.github/workflows/repo-stale.yml
vendored
17
.github/workflows/repo-stale.yml
vendored
@@ -1,9 +1,8 @@
|
||||
---
|
||||
name: Repo - Mark and close stale issues
|
||||
name: 'authentik-repo-stale'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
- cron: '30 1 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
@@ -15,17 +14,17 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: generate_token
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
|
||||
uses: tibdex/github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
|
||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10
|
||||
app_id: ${{ secrets.GH_APP_ID }}
|
||||
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- uses: actions/stale@v8
|
||||
with:
|
||||
repo-token: ${{ steps.generate_token.outputs.token }}
|
||||
days-before-stale: 60
|
||||
days-before-close: 7
|
||||
exempt-issue-labels: pinned,security,pr_wanted,enhancement,bug/confirmed,enhancement/confirmed,question,status/reviewing
|
||||
stale-issue-label: status/stale
|
||||
exempt-issue-labels: pinned,security,pr_wanted,enhancement,bug/confirmed,enhancement/confirmed,question
|
||||
stale-issue-label: wontfix
|
||||
stale-issue-message: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
|
||||
14
.github/workflows/translation-advice.yml
vendored
14
.github/workflows/translation-advice.yml
vendored
@@ -1,5 +1,4 @@
|
||||
---
|
||||
name: Translation - Post advice
|
||||
name: authentik-translation-advice
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -8,26 +7,21 @@ on:
|
||||
paths:
|
||||
- "!**"
|
||||
- "locale/**"
|
||||
- "!locale/en/**"
|
||||
- "web/xliff/**"
|
||||
|
||||
permissions:
|
||||
# Permission to write comment
|
||||
pull-requests: write
|
||||
- "web/src/locales/**"
|
||||
|
||||
jobs:
|
||||
post-comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4
|
||||
uses: peter-evans/find-comment@v2
|
||||
id: fc
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: "github-actions[bot]"
|
||||
body-includes: authentik translations instructions
|
||||
- name: Create or update comment
|
||||
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
with:
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
|
||||
40
.github/workflows/translation-compile.yml
vendored
Normal file
40
.github/workflows/translation-compile.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: authentik-backend-translate-compile
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "locale/**"
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
POSTGRES_DB: authentik
|
||||
POSTGRES_USER: authentik
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: generate_token
|
||||
uses: tibdex/github-app-token@v2
|
||||
with:
|
||||
app_id: ${{ secrets.GH_APP_ID }}
|
||||
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- name: run compile
|
||||
run: poetry run ak compilemessages
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
id: cpr
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
branch: compile-backend-translation
|
||||
commit-message: "core: compile backend translations"
|
||||
title: "core: compile backend translations"
|
||||
body: "core: compile backend translations"
|
||||
delete-branch: true
|
||||
signoff: true
|
||||
@@ -1,56 +0,0 @@
|
||||
---
|
||||
name: Translation - Extract and compile
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" # every day at midnight
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- version-*
|
||||
|
||||
env:
|
||||
POSTGRES_DB: authentik
|
||||
POSTGRES_USER: authentik
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: generate_token
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
|
||||
with:
|
||||
app-id: ${{ secrets.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIV_KEY }}
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- name: run extract
|
||||
run: |
|
||||
uv run make i18n-extract
|
||||
- name: run compile
|
||||
run: |
|
||||
uv run ak compilemessages
|
||||
make web-check-compile
|
||||
- name: Create Pull Request
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v7
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
branch: extract-compile-backend-translation
|
||||
commit-message: "core, web: update translations"
|
||||
title: "core, web: update translations"
|
||||
body: "core, web: update translations"
|
||||
delete-branch: true
|
||||
signoff: true
|
||||
labels: dependencies
|
||||
# ID from https://api.github.com/users/authentik-automation[bot]
|
||||
author: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
|
||||
45
.github/workflows/translation-rename.yml
vendored
Normal file
45
.github/workflows/translation-rename.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# Rename transifex pull requests to have a correct naming
|
||||
# Also enables auto squash-merge
|
||||
name: authentik-translation-transifex-rename
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened]
|
||||
|
||||
jobs:
|
||||
rename_pr:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.pull_request.user.login == 'transifex-integration[bot]'}}
|
||||
steps:
|
||||
- id: generate_token
|
||||
uses: tibdex/github-app-token@v2
|
||||
with:
|
||||
app_id: ${{ secrets.GH_APP_ID }}
|
||||
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- name: Get current title
|
||||
id: title
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
run: |
|
||||
title=$(curl -q -L \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${GH_TOKEN}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${GITHUB_REPOSITORY}/pulls/${{ github.event.pull_request.number }} | jq -r .title)
|
||||
echo "title=${title}" >> "$GITHUB_OUTPUT"
|
||||
- name: Rename
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
|
||||
run: |
|
||||
curl -L \
|
||||
-X PATCH \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${GH_TOKEN}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${GITHUB_REPOSITORY}/pulls/${{ github.event.pull_request.number }} \
|
||||
-d "{\"title\":\"translate: ${{ steps.title.outputs.title }}\"}"
|
||||
- uses: peter-evans/enable-pull-request-automerge@v3
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
pull-request-number: ${{ github.event.pull_request.number }}
|
||||
merge-method: squash
|
||||
54
.github/workflows/web-api-publish.yml
vendored
Normal file
54
.github/workflows/web-api-publish.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
name: authentik-web-api-publish
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "schema.yml"
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- id: generate_token
|
||||
uses: tibdex/github-app-token@v2
|
||||
with:
|
||||
app_id: ${{ secrets.GH_APP_ID }}
|
||||
private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: web/package.json
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- name: Generate API Client
|
||||
run: make gen-client-ts
|
||||
- name: Publish package
|
||||
working-directory: gen-ts-api/
|
||||
run: |
|
||||
npm ci
|
||||
npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
||||
- name: Upgrade /web
|
||||
working-directory: web/
|
||||
run: |
|
||||
export VERSION=`node -e 'console.log(require("../gen-ts-api/package.json").version)'`
|
||||
npm i @goauthentik/api@$VERSION
|
||||
- uses: peter-evans/create-pull-request@v5
|
||||
id: cpr
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
branch: update-web-api-client
|
||||
commit-message: "web: bump API Client version"
|
||||
title: "web: bump API Client version"
|
||||
body: "web: bump API Client version"
|
||||
delete-branch: true
|
||||
signoff: true
|
||||
# ID from https://api.github.com/users/authentik-automation[bot]
|
||||
author: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
|
||||
- uses: peter-evans/enable-pull-request-automerge@v3
|
||||
with:
|
||||
token: ${{ steps.generate_token.outputs.token }}
|
||||
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
merge-method: squash
|
||||
43
.gitignore
vendored
43
.gitignore
vendored
@@ -11,13 +11,6 @@ local_settings.py
|
||||
db.sqlite3
|
||||
media
|
||||
|
||||
# Node
|
||||
|
||||
node_modules
|
||||
|
||||
.cspellcache
|
||||
cspell-report.*
|
||||
|
||||
# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/
|
||||
# in your Git repository. Update and uncomment the following line accordingly.
|
||||
# <django-project-name>/staticfiles/
|
||||
@@ -40,7 +33,6 @@ eggs/
|
||||
lib64/
|
||||
parts/
|
||||
dist/
|
||||
out/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
@@ -75,7 +67,7 @@ unittest.xml
|
||||
|
||||
# Translations
|
||||
# Have to include binary mo files as they are annoying to compile at build time
|
||||
# since a full postgres instance is required
|
||||
# since a full postgres and redis instance are required
|
||||
# *.mo
|
||||
|
||||
# Django stuff:
|
||||
@@ -103,6 +95,9 @@ ipython_config.py
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
@@ -166,6 +161,8 @@ dmypy.json
|
||||
|
||||
# pyenv
|
||||
|
||||
# celery beat schedule file
|
||||
|
||||
# SageMath parsed files
|
||||
|
||||
# Environments
|
||||
@@ -195,24 +192,6 @@ pyvenv.cfg
|
||||
pip-selfcheck.json
|
||||
|
||||
# End of https://www.gitignore.io/api/python,django
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/rust
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=rust
|
||||
|
||||
### Rust ###
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
debug/
|
||||
target/
|
||||
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
||||
*.pdb
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/rust
|
||||
|
||||
/static/
|
||||
local.env.yml
|
||||
|
||||
@@ -220,6 +199,7 @@ media/
|
||||
*mmdb
|
||||
|
||||
.idea/
|
||||
/gen-*/
|
||||
data/
|
||||
|
||||
# Local Netlify folder
|
||||
@@ -229,12 +209,3 @@ source_docs/
|
||||
|
||||
### Golang ###
|
||||
/vendor/
|
||||
server
|
||||
proxy
|
||||
ldap
|
||||
rac
|
||||
radius
|
||||
|
||||
### Docker ###
|
||||
tests/openid_conformance/exports/*.zip
|
||||
compose.override.yml
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
# Prettier Ignorefile
|
||||
|
||||
## Static Files
|
||||
CODEOWNERS
|
||||
**/LICENSE
|
||||
|
||||
authentik/stages/**/*
|
||||
|
||||
## Build asset directories
|
||||
coverage
|
||||
dist
|
||||
out
|
||||
.docusaurus
|
||||
# TODO Replace after moving website to docs
|
||||
website/api/reference
|
||||
|
||||
## Environment
|
||||
*.env
|
||||
|
||||
## Secrets
|
||||
*.secrets
|
||||
|
||||
## Yarn
|
||||
.yarn/**/*
|
||||
|
||||
## Node
|
||||
node_modules
|
||||
coverage
|
||||
|
||||
## Vendored files
|
||||
vendored
|
||||
*.min.js
|
||||
|
||||
## Configs
|
||||
*.log
|
||||
*.yaml
|
||||
*.yml
|
||||
|
||||
# Templates
|
||||
# TODO: Rename affected files to *.template.* or similar.
|
||||
*.html
|
||||
*.mdx
|
||||
*.md
|
||||
|
||||
## Import order matters
|
||||
poly.ts
|
||||
src/locale-codes.ts
|
||||
src/locales/
|
||||
|
||||
# Storybook
|
||||
storybook-static/
|
||||
.storybook/css-import-maps*
|
||||
|
||||
7
.vscode/extensions.json
vendored
7
.vscode/extensions.json
vendored
@@ -2,7 +2,6 @@
|
||||
"recommendations": [
|
||||
"bashmish.es6-string-css",
|
||||
"bpruitt-goddard.mermaid-markdown-syntax-highlighting",
|
||||
"charliermarsh.ruff",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"EditorConfig.EditorConfig",
|
||||
"esbenp.prettier-vscode",
|
||||
@@ -11,12 +10,12 @@
|
||||
"Gruntfuggly.todo-tree",
|
||||
"mechatroner.rainbow-csv",
|
||||
"ms-python.black-formatter",
|
||||
"ms-python.black-formatter",
|
||||
"ms-python.debugpy",
|
||||
"ms-python.isort",
|
||||
"ms-python.pylint",
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance",
|
||||
"redhat.vscode-yaml",
|
||||
"Tobermory.es6-string-html",
|
||||
"unifiedjs.vscode-mdx"
|
||||
"unifiedjs.vscode-mdx",
|
||||
]
|
||||
}
|
||||
|
||||
66
.vscode/launch.json
vendored
66
.vscode/launch.json
vendored
@@ -2,76 +2,26 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug: Attach Server Core",
|
||||
"type": "debugpy",
|
||||
"name": "Python: PDB attach Server",
|
||||
"type": "python",
|
||||
"request": "attach",
|
||||
"connect": {
|
||||
"host": "localhost",
|
||||
"port": 9901
|
||||
"port": 6800
|
||||
},
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceFolder}",
|
||||
"remoteRoot": "."
|
||||
}
|
||||
],
|
||||
"justMyCode": true,
|
||||
"django": true
|
||||
},
|
||||
{
|
||||
"name": "Debug: Attach Worker",
|
||||
"type": "debugpy",
|
||||
"name": "Python: PDB attach Worker",
|
||||
"type": "python",
|
||||
"request": "attach",
|
||||
"connect": {
|
||||
"host": "localhost",
|
||||
"port": 9901
|
||||
"port": 6900
|
||||
},
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceFolder}",
|
||||
"remoteRoot": "."
|
||||
}
|
||||
],
|
||||
"justMyCode": true,
|
||||
"django": true
|
||||
},
|
||||
{
|
||||
"name": "Debug: Start Server Router",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/cmd/server",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Debug: Start LDAP Outpost",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/cmd/ldap",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Debug: Start Proxy Outpost",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/cmd/proxy",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Debug: Start RAC Outpost",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/cmd/rac",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"name": "Debug: Start Radius Outpost",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/cmd/radius",
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
89
.vscode/settings.json
vendored
89
.vscode/settings.json
vendored
@@ -1,47 +1,43 @@
|
||||
{
|
||||
"[css]": {
|
||||
"editor.minimap.markSectionHeaderRegex": "#\\bregion\\s*(?<separator>-?)\\s*(?<label>.*)\\*/$"
|
||||
},
|
||||
"[makefile]": {
|
||||
"editor.minimap.markSectionHeaderRegex": "^#{25}\n##\\s\\s*(?<separator>-?)\\s*(?<label>[^\n]*)\n#{25}$"
|
||||
},
|
||||
"[dockerfile]": {
|
||||
"editor.minimap.markSectionHeaderRegex": "\\bStage\\s*\\d:(?<separator>-?)\\s*(?<label>.*)$"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.minimap.markSectionHeaderRegex": "#\\bregion\\s*(?<separator>-?)\\s*(?<label>.*)$"
|
||||
},
|
||||
"[xml]": {
|
||||
"editor.minimap.markSectionHeaderRegex": "<!--\\s*#\\bregion\\s*(?<separator>-?)\\s*(?<label>.*)\\s*-->"
|
||||
},
|
||||
"files.associations": {
|
||||
// The built-in "ignore" language gives us enough syntax highlighting to make these files readable.
|
||||
"**/dictionaries/*.txt": "ignore"
|
||||
},
|
||||
"cSpell.words": [
|
||||
"akadmin",
|
||||
"asgi",
|
||||
"authentik",
|
||||
"authn",
|
||||
"goauthentik",
|
||||
"jwks",
|
||||
"oidc",
|
||||
"openid",
|
||||
"plex",
|
||||
"saml",
|
||||
"totp",
|
||||
"webauthn",
|
||||
"traefik",
|
||||
"passwordless",
|
||||
"kubernetes",
|
||||
"sso",
|
||||
"slo",
|
||||
"scim",
|
||||
],
|
||||
"python.linting.pylintEnabled": true,
|
||||
"todo-tree.tree.showCountsInTree": true,
|
||||
"todo-tree.tree.showBadges": true,
|
||||
"python.formatting.provider": "black",
|
||||
"yaml.customTags": [
|
||||
"!Condition sequence",
|
||||
"!Context scalar",
|
||||
"!Enumerate sequence",
|
||||
"!Env scalar",
|
||||
"!Env sequence",
|
||||
"!File scalar",
|
||||
"!File sequence",
|
||||
"!Find sequence",
|
||||
"!FindObject sequence",
|
||||
"!Format sequence",
|
||||
"!If sequence",
|
||||
"!Index scalar",
|
||||
"!KeyOf scalar",
|
||||
"!Value scalar",
|
||||
"!AtIndex scalar",
|
||||
"!ParseJSON scalar"
|
||||
"!Context scalar",
|
||||
"!Context sequence",
|
||||
"!Format sequence",
|
||||
"!Condition sequence",
|
||||
"!Env sequence",
|
||||
"!Env scalar",
|
||||
"!If sequence"
|
||||
],
|
||||
"js/ts.preferences.importModuleSpecifier": "non-relative",
|
||||
"js/ts.preferences.importModuleSpecifierEnding": "index",
|
||||
"js/ts.tsdk.path": "./node_modules/typescript/lib",
|
||||
"js/ts.tsdk.promptToUseWorkspaceVersion": true,
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"typescript.preferences.importModuleSpecifierEnding": "index",
|
||||
"typescript.tsdk": "./web/node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"yaml.schemas": {
|
||||
"./blueprints/schema.json": "blueprints/**/*.yaml"
|
||||
},
|
||||
@@ -53,17 +49,10 @@
|
||||
"ignoreCase": false
|
||||
}
|
||||
],
|
||||
"go.testFlags": ["-count=1"],
|
||||
"go.testEnvVars": {
|
||||
"WORKSPACE_DIR": "${workspaceFolder}"
|
||||
},
|
||||
"github-actions.workflows.pinned.workflows": [".github/workflows/ci-main.yml"],
|
||||
"search.exclude": {
|
||||
"**/*.code-search": true,
|
||||
"**/bower_components": true,
|
||||
"**/node_modules": true,
|
||||
"**/playwright-report/**": true,
|
||||
"**/website/**/build": true,
|
||||
"**/client-*": true
|
||||
}
|
||||
"go.testFlags": [
|
||||
"-count=1"
|
||||
],
|
||||
"github-actions.workflows.pinned.workflows": [
|
||||
".github/workflows/ci-main.yml"
|
||||
]
|
||||
}
|
||||
|
||||
74
.vscode/tasks.json
vendored
74
.vscode/tasks.json
vendored
@@ -2,67 +2,85 @@
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "authentik/core: make",
|
||||
"command": "uv",
|
||||
"args": ["run", "make", "lint-fix", "lint"],
|
||||
"presentation": {
|
||||
"panel": "new"
|
||||
},
|
||||
"group": "test"
|
||||
"label": "authentik[core]: format & test",
|
||||
"command": "poetry",
|
||||
"args": [
|
||||
"run",
|
||||
"make"
|
||||
],
|
||||
"group": "build",
|
||||
},
|
||||
{
|
||||
"label": "authentik/core: run",
|
||||
"command": "uv",
|
||||
"args": ["run", "ak", "server"],
|
||||
"label": "authentik[core]: run",
|
||||
"command": "poetry",
|
||||
"args": [
|
||||
"run",
|
||||
"make",
|
||||
"run",
|
||||
],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
"group": "running"
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "authentik/web: make",
|
||||
"label": "authentik[web]: format",
|
||||
"command": "make",
|
||||
"args": ["web"],
|
||||
"group": "build"
|
||||
"group": "build",
|
||||
},
|
||||
{
|
||||
"label": "authentik/web: watch",
|
||||
"label": "authentik[web]: watch",
|
||||
"command": "make",
|
||||
"args": ["web-watch"],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
"group": "running"
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "authentik: install",
|
||||
"command": "make",
|
||||
"args": ["install", "-j4"],
|
||||
"group": "build"
|
||||
"args": ["install"],
|
||||
"group": "build",
|
||||
},
|
||||
{
|
||||
"label": "authentik/docs: make",
|
||||
"command": "make",
|
||||
"args": ["docs"],
|
||||
"group": "build"
|
||||
"label": "authentik: i18n-extract",
|
||||
"command": "poetry",
|
||||
"args": [
|
||||
"run",
|
||||
"make",
|
||||
"i18n-extract"
|
||||
],
|
||||
"group": "build",
|
||||
},
|
||||
{
|
||||
"label": "authentik/docs: watch",
|
||||
"label": "authentik[website]: format",
|
||||
"command": "make",
|
||||
"args": ["docs-watch"],
|
||||
"args": ["website"],
|
||||
"group": "build",
|
||||
},
|
||||
{
|
||||
"label": "authentik[website]: watch",
|
||||
"command": "make",
|
||||
"args": ["website-watch"],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
"group": "running"
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"label": "authentik/api: generate",
|
||||
"command": "uv",
|
||||
"args": ["run", "make", "gen"],
|
||||
"label": "authentik[api]: generate",
|
||||
"command": "poetry",
|
||||
"args": [
|
||||
"run",
|
||||
"make",
|
||||
"gen"
|
||||
],
|
||||
"group": "build"
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
75
CODEOWNERS
75
CODEOWNERS
@@ -1,59 +1,26 @@
|
||||
# Fallback
|
||||
* @goauthentik/backend @goauthentik/frontend
|
||||
* @goauthentik/backend @goauthentik/frontend
|
||||
# Backend
|
||||
authentik/ @goauthentik/backend
|
||||
blueprints/ @goauthentik/backend
|
||||
src/ @goauthentik/backend
|
||||
cmd/ @goauthentik/backend
|
||||
internal/ @goauthentik/backend
|
||||
lifecycle/ @goauthentik/backend
|
||||
schemas/ @goauthentik/backend
|
||||
scripts/ @goauthentik/backend
|
||||
tests/ @goauthentik/backend
|
||||
pyproject.toml @goauthentik/backend
|
||||
uv.lock @goauthentik/backend
|
||||
Cargo.toml @goauthentik/backend
|
||||
Cargo.lock @goauthentik/backend
|
||||
build.rs @goauthentik/backend
|
||||
go.mod @goauthentik/backend
|
||||
go.sum @goauthentik/backend
|
||||
.cargo/ @goauthentik/backend
|
||||
rust-toolchain.toml @goauthentik/backend
|
||||
authentik/ @goauthentik/backend
|
||||
blueprints/ @goauthentik/backend
|
||||
cmd/ @goauthentik/backend
|
||||
internal/ @goauthentik/backend
|
||||
lifecycle/ @goauthentik/backend
|
||||
schemas/ @goauthentik/backend
|
||||
scripts/ @goauthentik/backend
|
||||
tests/ @goauthentik/backend
|
||||
pyproject.toml @goauthentik/backend
|
||||
poetry.lock @goauthentik/backend
|
||||
# Infrastructure
|
||||
.github/ @goauthentik/infrastructure
|
||||
lifecycle/aws/ @goauthentik/infrastructure
|
||||
lifecycle/container/ @goauthentik/infrastructure
|
||||
.dockerignore @goauthentik/infrastructure
|
||||
Makefile @goauthentik/infrastructure
|
||||
.editorconfig @goauthentik/infrastructure
|
||||
CODEOWNERS @goauthentik/infrastructure
|
||||
# Backend packages
|
||||
packages/ak-* @goauthentik/backend
|
||||
packages/client-rust @goauthentik/backend
|
||||
packages/django-channels-postgres @goauthentik/backend
|
||||
packages/django-postgres-cache @goauthentik/backend
|
||||
packages/django-dramatiq-postgres @goauthentik/backend
|
||||
# Web packages
|
||||
tsconfig.json @goauthentik/frontend
|
||||
package.json @goauthentik/frontend
|
||||
package-lock.json @goauthentik/frontend
|
||||
packages/package.json @goauthentik/frontend
|
||||
packages/package-lock.json @goauthentik/frontend
|
||||
packages/client-ts @goauthentik/frontend
|
||||
packages/docusaurus-config @goauthentik/frontend
|
||||
packages/esbuild-plugin-live-reload @goauthentik/frontend
|
||||
packages/eslint-config @goauthentik/frontend
|
||||
packages/prettier-config @goauthentik/frontend
|
||||
packages/logger-js @goauthentik/frontend
|
||||
packages/tsconfig @goauthentik/frontend
|
||||
.github/ @goauthentik/infrastructure
|
||||
Dockerfile @goauthentik/infrastructure
|
||||
*Dockerfile @goauthentik/infrastructure
|
||||
.dockerignore @goauthentik/infrastructure
|
||||
docker-compose.yml @goauthentik/infrastructure
|
||||
# Web
|
||||
web/ @goauthentik/frontend
|
||||
# Locale
|
||||
/locale/ @goauthentik/backend @goauthentik/frontend
|
||||
web/xliff/ @goauthentik/backend @goauthentik/frontend
|
||||
# Docs
|
||||
website/ @goauthentik/docs
|
||||
CODE_OF_CONDUCT.md @goauthentik/docs
|
||||
web/ @goauthentik/frontend
|
||||
tests/wdio/ @goauthentik/frontend
|
||||
# Docs & Website
|
||||
website/ @goauthentik/docs
|
||||
# Security
|
||||
SECURITY.md @goauthentik/security @goauthentik/docs
|
||||
website/security/ @goauthentik/security @goauthentik/docs
|
||||
website/docs/security/ @goauthentik/security
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socioeconomic status,
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# Contributing to authentik
|
||||
|
||||
Thanks for your interest in contributing! Please see our [contributing guide](https://docs.goauthentik.io/docs/developer-docs/?utm_source=github) for more information.
|
||||
|
||||
1
CONTRIBUTING.md
Symbolic link
1
CONTRIBUTING.md
Symbolic link
@@ -0,0 +1 @@
|
||||
website/developer-docs/index.md
|
||||
5264
Cargo.lock
generated
5264
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
305
Cargo.toml
305
Cargo.toml
@@ -1,305 +0,0 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"packages/ak-axum",
|
||||
"packages/ak-common",
|
||||
"packages/client-rust",
|
||||
"website/scripts/docsmg",
|
||||
]
|
||||
resolver = "3"
|
||||
|
||||
[workspace.package]
|
||||
version = "2026.5.0-rc1"
|
||||
authors = ["authentik Team <hello@goauthentik.io>"]
|
||||
description = "Making authentication simple."
|
||||
edition = "2024"
|
||||
readme = "README.md"
|
||||
homepage = "https://goauthentik.io"
|
||||
repository = "https://github.com/goauthentik/authentik.git"
|
||||
license-file = "LICENSE"
|
||||
publish = false
|
||||
|
||||
[workspace.dependencies]
|
||||
arc-swap = "= 1.9.1"
|
||||
argh = "= 0.1.19"
|
||||
axum-server = { version = "= 0.8.0", features = ["tls-rustls-no-provider"] }
|
||||
aws-lc-rs = { version = "= 1.16.3", features = ["fips"] }
|
||||
axum = { version = "= 0.8.9", features = ["http2", "macros", "ws"] }
|
||||
clap = { version = "= 4.6.1", features = ["derive", "env"] }
|
||||
client-ip = { version = "0.2.1", features = ["forwarded-header"] }
|
||||
color-eyre = "= 0.6.5"
|
||||
colored = "= 3.1.1"
|
||||
config-rs = { package = "config", version = "= 0.15.22", default-features = false, features = [
|
||||
"json",
|
||||
"yaml",
|
||||
] }
|
||||
console-subscriber = "= 0.5.0"
|
||||
dotenvy = "= 0.15.7"
|
||||
durstr = "= 0.5.1"
|
||||
eyre = "= 0.6.12"
|
||||
forwarded-header-value = "= 0.1.1"
|
||||
futures = "= 0.3.32"
|
||||
glob = "= 0.3.3"
|
||||
hyper-unix-socket = "= 0.6.1"
|
||||
hyper-util = "= 0.1.20"
|
||||
ipnet = { version = "= 2.12.0", features = ["serde"] }
|
||||
json-subscriber = "= 0.2.8"
|
||||
metrics = "= 0.24.5"
|
||||
metrics-exporter-prometheus = { version = "= 0.18.3", default-features = false }
|
||||
nix = { version = "= 0.31.2", features = ["hostname", "signal"] }
|
||||
notify = "= 8.2.0"
|
||||
pin-project-lite = "= 0.2.17"
|
||||
pyo3 = "= 0.28.3"
|
||||
pyo3-build-config = "= 0.28.3"
|
||||
rand = "= 0.10.1"
|
||||
regex = "= 1.12.3"
|
||||
reqwest = { version = "= 0.13.3", features = [
|
||||
"form",
|
||||
"json",
|
||||
"multipart",
|
||||
"query",
|
||||
"rustls",
|
||||
"stream",
|
||||
] }
|
||||
reqwest-middleware = { version = "= 0.5.1", features = [
|
||||
"form",
|
||||
"json",
|
||||
"multipart",
|
||||
"query",
|
||||
"rustls",
|
||||
] }
|
||||
rustls = { version = "= 0.23.40", features = ["fips"] }
|
||||
sentry = { version = "= 0.47.0", default-features = false, features = [
|
||||
"backtrace",
|
||||
"contexts",
|
||||
"debug-images",
|
||||
"panic",
|
||||
"rustls",
|
||||
"reqwest",
|
||||
"tower",
|
||||
"tracing",
|
||||
] }
|
||||
serde = { version = "= 1.0.228", features = ["derive"] }
|
||||
serde_json = "= 1.0.149"
|
||||
serde_repr = "= 0.1.20"
|
||||
serde_with = { version = "= 3.18.0", default-features = false, features = [
|
||||
"base64",
|
||||
] }
|
||||
sqlx = { version = "= 0.8.6", default-features = false, features = [
|
||||
"runtime-tokio",
|
||||
"tls-rustls-aws-lc-rs",
|
||||
"postgres",
|
||||
"derive",
|
||||
"macros",
|
||||
"uuid",
|
||||
"chrono",
|
||||
"ipnet",
|
||||
"json",
|
||||
] }
|
||||
tempfile = "= 3.27.0"
|
||||
thiserror = "= 2.0.18"
|
||||
time = { version = "= 0.3.47", features = ["macros"] }
|
||||
tokio = { version = "= 1.52.1", features = ["full", "tracing"] }
|
||||
tokio-retry2 = "= 0.9.1"
|
||||
tokio-rustls = "= 0.26.4"
|
||||
tokio-tungstenite = { version = "= 0.29.0", features = [
|
||||
"rustls-tls-webpki-roots",
|
||||
"url",
|
||||
] }
|
||||
tokio-util = { version = "= 0.7.18", features = ["full"] }
|
||||
tower = "= 0.5.3"
|
||||
tower-http = { version = "= 0.6.8", features = ["timeout"] }
|
||||
tracing = "= 0.1.44"
|
||||
tracing-error = "= 0.2.1"
|
||||
tracing-subscriber = { version = "= 0.3.23", features = [
|
||||
"env-filter",
|
||||
"json",
|
||||
"local-time",
|
||||
"tracing-log",
|
||||
] }
|
||||
url = "= 2.5.8"
|
||||
uuid = { version = "= 1.23.1", features = ["serde", "v4"] }
|
||||
which = "= 8.0.2"
|
||||
|
||||
ak-axum = { package = "authentik-axum", version = "2026.5.0-rc1", path = "./packages/ak-axum" }
|
||||
ak-client = { package = "authentik-client", version = "2026.5.0-rc1", path = "./packages/client-rust" }
|
||||
ak-common = { package = "authentik-common", version = "2026.5.0-rc1", path = "./packages/ak-common", default-features = false }
|
||||
|
||||
[workspace.lints.rust]
|
||||
ambiguous_negative_literals = "warn"
|
||||
closure_returning_async_block = "warn"
|
||||
macro_use_extern_crate = "deny"
|
||||
# must_not_suspend = "deny", unstable see https://github.com/rust-lang/rust/issues/83310
|
||||
non_ascii_idents = "deny"
|
||||
redundant_imports = "warn"
|
||||
semicolon_in_expressions_from_macros = "warn"
|
||||
trivial_casts = "warn"
|
||||
trivial_numeric_casts = "warn"
|
||||
unit_bindings = "warn"
|
||||
unreachable_pub = "warn"
|
||||
unsafe_code = "deny"
|
||||
unused_extern_crates = "warn"
|
||||
unused_import_braces = "warn"
|
||||
unused_lifetimes = "warn"
|
||||
unused_macro_rules = "warn"
|
||||
unused_qualifications = "warn"
|
||||
|
||||
[workspace.lints.rustdoc]
|
||||
unescaped_backticks = "warn"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
### enable all lints
|
||||
cargo = { priority = -1, level = "warn" }
|
||||
complexity = { priority = -1, level = "warn" }
|
||||
correctness = { priority = -1, level = "warn" }
|
||||
nursery = { priority = -1, level = "warn" }
|
||||
pedantic = { priority = -1, level = "warn" }
|
||||
perf = { priority = -1, level = "warn" }
|
||||
# Those are too restrictive and disabled by default, however we enable some below
|
||||
# restriction = { priority = -1, level = "warn" }
|
||||
style = { priority = -1, level = "warn" }
|
||||
suspicious = { priority = -1, level = "warn" }
|
||||
### and disable the ones we don't want
|
||||
### cargo group
|
||||
multiple_crate_versions = "allow"
|
||||
### pedantic group
|
||||
missing_errors_doc = "allow"
|
||||
missing_panics_doc = "allow"
|
||||
must_use_candidate = "allow"
|
||||
redundant_closure_for_method_calls = "allow"
|
||||
struct_field_names = "allow"
|
||||
too_many_lines = "allow"
|
||||
### nursery
|
||||
missing_const_for_fn = "allow"
|
||||
option_if_let_else = "allow"
|
||||
redundant_pub_crate = "allow"
|
||||
significant_drop_tightening = "allow"
|
||||
### restriction group
|
||||
allow_attributes = "warn"
|
||||
allow_attributes_without_reason = "warn"
|
||||
as_conversions = "warn"
|
||||
as_pointer_underscore = "warn"
|
||||
as_underscore = "warn"
|
||||
assertions_on_result_states = "warn"
|
||||
clone_on_ref_ptr = "warn"
|
||||
create_dir = "warn"
|
||||
dbg_macro = "warn"
|
||||
default_numeric_fallback = "warn"
|
||||
disallowed_script_idents = "warn"
|
||||
empty_drop = "warn"
|
||||
empty_enum_variants_with_brackets = "warn"
|
||||
empty_structs_with_brackets = "warn"
|
||||
error_impl_error = "warn"
|
||||
exit = "warn"
|
||||
filetype_is_file = "warn"
|
||||
float_cmp_const = "warn"
|
||||
fn_to_numeric_cast_any = "warn"
|
||||
get_unwrap = "warn"
|
||||
if_then_some_else_none = "warn"
|
||||
impl_trait_in_params = "warn"
|
||||
infinite_loop = "warn"
|
||||
lossy_float_literal = "warn"
|
||||
map_with_unused_argument_over_ranges = "warn"
|
||||
mem_forget = "warn"
|
||||
missing_asserts_for_indexing = "warn"
|
||||
missing_trait_methods = "warn"
|
||||
mixed_read_write_in_expression = "warn"
|
||||
mutex_atomic = "warn"
|
||||
mutex_integer = "warn"
|
||||
needless_raw_strings = "warn"
|
||||
non_zero_suggestions = "warn"
|
||||
panic_in_result_fn = "warn"
|
||||
pathbuf_init_then_push = "warn"
|
||||
print_stdout = "warn"
|
||||
rc_buffer = "warn"
|
||||
redundant_test_prefix = "warn"
|
||||
redundant_type_annotations = "warn"
|
||||
ref_patterns = "warn"
|
||||
renamed_function_params = "warn"
|
||||
rest_pat_in_fully_bound_structs = "warn"
|
||||
return_and_then = "warn"
|
||||
same_name_method = "warn"
|
||||
semicolon_inside_block = "warn"
|
||||
str_to_string = "warn"
|
||||
string_add = "warn"
|
||||
suspicious_xor_used_as_pow = "warn"
|
||||
tests_outside_test_module = "warn"
|
||||
todo = "warn"
|
||||
try_err = "warn"
|
||||
undocumented_unsafe_blocks = "warn"
|
||||
unimplemented = "warn"
|
||||
unnecessary_safety_comment = "warn"
|
||||
unnecessary_safety_doc = "warn"
|
||||
unnecessary_self_imports = "warn"
|
||||
unneeded_field_pattern = "warn"
|
||||
unseparated_literal_suffix = "warn"
|
||||
unused_result_ok = "warn"
|
||||
unused_trait_names = "warn"
|
||||
unwrap_in_result = "warn"
|
||||
unwrap_used = "warn"
|
||||
verbose_file_reads = "warn"
|
||||
|
||||
[profile.dev.package.backtrace]
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
[profile.release]
|
||||
debug = 2
|
||||
lto = "fat"
|
||||
# Because of the async runtime, we want to die straightaway if we panic.
|
||||
panic = "abort"
|
||||
strip = true
|
||||
|
||||
[package]
|
||||
name = "authentik"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
readme.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
license-file.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["core", "proxy"]
|
||||
core = ["ak-common/core", "dep:pyo3", "dep:sqlx"]
|
||||
proxy = ["ak-common/proxy", "dep:ak-client"]
|
||||
|
||||
[build-dependencies]
|
||||
pyo3-build-config.workspace = true
|
||||
|
||||
[dependencies]
|
||||
ak-axum.workspace = true
|
||||
ak-client = { workspace = true, optional = true }
|
||||
ak-common.workspace = true
|
||||
arc-swap.workspace = true
|
||||
argh.workspace = true
|
||||
axum.workspace = true
|
||||
color-eyre.workspace = true
|
||||
eyre.workspace = true
|
||||
futures.workspace = true
|
||||
hyper-unix-socket.workspace = true
|
||||
hyper-util.workspace = true
|
||||
metrics-exporter-prometheus.workspace = true
|
||||
metrics.workspace = true
|
||||
nix.workspace = true
|
||||
pyo3 = { workspace = true, optional = true }
|
||||
rand.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde_repr.workspace = true
|
||||
sqlx = { workspace = true, optional = true }
|
||||
time.workspace = true
|
||||
tokio-retry2.workspace = true
|
||||
tokio-tungstenite.workspace = true
|
||||
tokio.workspace = true
|
||||
tower.workspace = true
|
||||
tracing.workspace = true
|
||||
url.workspace = true
|
||||
uuid.workspace = true
|
||||
which.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
166
Dockerfile
Normal file
166
Dockerfile
Normal file
@@ -0,0 +1,166 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Stage 1: Build website
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/node:21 as website-builder
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
WORKDIR /work/website
|
||||
|
||||
RUN --mount=type=bind,target=/work/website/package.json,src=./website/package.json \
|
||||
--mount=type=bind,target=/work/website/package-lock.json,src=./website/package-lock.json \
|
||||
--mount=type=cache,id=npm-website,sharing=shared,target=/root/.npm \
|
||||
npm ci --include=dev
|
||||
|
||||
COPY ./website /work/website/
|
||||
COPY ./blueprints /work/blueprints/
|
||||
COPY ./SECURITY.md /work/
|
||||
|
||||
RUN npm run build-docs-only
|
||||
|
||||
# Stage 2: Build webui
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/node:21 as web-builder
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
WORKDIR /work/web
|
||||
|
||||
RUN --mount=type=bind,target=/work/web/package.json,src=./web/package.json \
|
||||
--mount=type=bind,target=/work/web/package-lock.json,src=./web/package-lock.json \
|
||||
--mount=type=cache,id=npm-web,sharing=shared,target=/root/.npm \
|
||||
npm ci --include=dev
|
||||
|
||||
COPY ./web /work/web/
|
||||
COPY ./website /work/website/
|
||||
COPY ./gen-ts-api /work/web/node_modules/@goauthentik/api
|
||||
|
||||
RUN npm run build
|
||||
|
||||
# Stage 3: Build go proxy
|
||||
FROM --platform=${BUILDPLATFORM} docker.io/golang:1.21.4-bookworm AS go-builder
|
||||
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ARG TARGETVARIANT
|
||||
|
||||
ARG GOOS=$TARGETOS
|
||||
ARG GOARCH=$TARGETARCH
|
||||
|
||||
WORKDIR /go/src/goauthentik.io
|
||||
|
||||
RUN --mount=type=bind,target=/go/src/goauthentik.io/go.mod,src=./go.mod \
|
||||
--mount=type=bind,target=/go/src/goauthentik.io/go.sum,src=./go.sum \
|
||||
--mount=type=cache,target=/go/pkg/mod \
|
||||
go mod download
|
||||
|
||||
COPY ./cmd /go/src/goauthentik.io/cmd
|
||||
COPY ./authentik/lib /go/src/goauthentik.io/authentik/lib
|
||||
COPY ./web/static.go /go/src/goauthentik.io/web/static.go
|
||||
COPY --from=web-builder /work/web/robots.txt /go/src/goauthentik.io/web/robots.txt
|
||||
COPY --from=web-builder /work/web/security.txt /go/src/goauthentik.io/web/security.txt
|
||||
COPY ./internal /go/src/goauthentik.io/internal
|
||||
COPY ./go.mod /go/src/goauthentik.io/go.mod
|
||||
COPY ./go.sum /go/src/goauthentik.io/go.sum
|
||||
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
RUN --mount=type=cache,sharing=locked,target=/go/pkg/mod \
|
||||
--mount=type=cache,id=go-build-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/root/.cache/go-build \
|
||||
GOARM="${TARGETVARIANT#v}" go build -o /go/authentik ./cmd/server
|
||||
|
||||
# Stage 4: MaxMind GeoIP
|
||||
FROM --platform=${BUILDPLATFORM} ghcr.io/maxmind/geoipupdate:v6.0 as geoip
|
||||
|
||||
ENV GEOIPUPDATE_EDITION_IDS="GeoLite2-City"
|
||||
ENV GEOIPUPDATE_VERBOSE="true"
|
||||
ENV GEOIPUPDATE_ACCOUNT_ID_FILE="/run/secrets/GEOIPUPDATE_ACCOUNT_ID"
|
||||
ENV GEOIPUPDATE_LICENSE_KEY_FILE="/run/secrets/GEOIPUPDATE_LICENSE_KEY"
|
||||
|
||||
USER root
|
||||
RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
||||
--mount=type=secret,id=GEOIPUPDATE_LICENSE_KEY \
|
||||
mkdir -p /usr/share/GeoIP && \
|
||||
/bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
|
||||
|
||||
# Stage 5: Python dependencies
|
||||
FROM docker.io/python:3.11.5-bookworm AS python-deps
|
||||
|
||||
WORKDIR /ak-root/poetry
|
||||
|
||||
ENV VENV_PATH="/ak-root/venv" \
|
||||
POETRY_VIRTUALENVS_CREATE=false \
|
||||
PATH="/ak-root/venv/bin:$PATH"
|
||||
|
||||
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
|
||||
|
||||
RUN --mount=type=cache,id=apt-$TARGETARCH$TARGETVARIANT,sharing=locked,target=/var/cache/apt \
|
||||
apt-get update && \
|
||||
# Required for installing pip packages
|
||||
apt-get install -y --no-install-recommends build-essential pkg-config libxmlsec1-dev zlib1g-dev libpq-dev
|
||||
|
||||
RUN --mount=type=bind,target=./pyproject.toml,src=./pyproject.toml \
|
||||
--mount=type=bind,target=./poetry.lock,src=./poetry.lock \
|
||||
--mount=type=cache,target=/root/.cache/pip \
|
||||
--mount=type=cache,target=/root/.cache/pypoetry \
|
||||
python -m venv /ak-root/venv/ && \
|
||||
pip3 install --upgrade pip && \
|
||||
pip3 install poetry && \
|
||||
poetry install --only=main --no-ansi --no-interaction
|
||||
|
||||
# Stage 6: Run
|
||||
FROM docker.io/python:3.11.5-slim-bookworm AS final-image
|
||||
|
||||
ARG GIT_BUILD_HASH
|
||||
ARG VERSION
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
LABEL org.opencontainers.image.url https://goauthentik.io
|
||||
LABEL org.opencontainers.image.description goauthentik.io Main server image, see https://goauthentik.io for more info.
|
||||
LABEL org.opencontainers.image.source https://github.com/goauthentik/authentik
|
||||
LABEL org.opencontainers.image.version ${VERSION}
|
||||
LABEL org.opencontainers.image.revision ${GIT_BUILD_HASH}
|
||||
|
||||
WORKDIR /
|
||||
|
||||
# We cannot cache this layer otherwise we'll end up with a bigger image
|
||||
RUN apt-get update && \
|
||||
# Required for runtime
|
||||
apt-get install -y --no-install-recommends libpq5 openssl libxmlsec1-openssl libmaxminddb0 && \
|
||||
# Required for bootstrap & healtcheck
|
||||
apt-get install -y --no-install-recommends runit && \
|
||||
apt-get clean && \
|
||||
rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/ && \
|
||||
adduser --system --no-create-home --uid 1000 --group --home /authentik authentik && \
|
||||
mkdir -p /certs /media /blueprints && \
|
||||
mkdir -p /authentik/.ssh && \
|
||||
mkdir -p /ak-root && \
|
||||
chown authentik:authentik /certs /media /authentik/.ssh /ak-root
|
||||
|
||||
COPY ./authentik/ /authentik
|
||||
COPY ./pyproject.toml /
|
||||
COPY ./poetry.lock /
|
||||
COPY ./schemas /schemas
|
||||
COPY ./locale /locale
|
||||
COPY ./tests /tests
|
||||
COPY ./manage.py /
|
||||
COPY ./blueprints /blueprints
|
||||
COPY ./lifecycle/ /lifecycle
|
||||
COPY --from=go-builder /go/authentik /bin/authentik
|
||||
COPY --from=python-deps /ak-root/venv /ak-root/venv
|
||||
COPY --from=web-builder /work/web/dist/ /web/dist/
|
||||
COPY --from=web-builder /work/web/authentik/ /web/authentik/
|
||||
COPY --from=website-builder /work/website/help/ /website/help/
|
||||
COPY --from=geoip /usr/share/GeoIP /geoip
|
||||
|
||||
USER 1000
|
||||
|
||||
ENV TMPDIR=/dev/shm/ \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PATH="/ak-root/venv/bin:/lifecycle:$PATH" \
|
||||
VENV_PATH="/ak-root/venv" \
|
||||
POETRY_VIRTUALENVS_CREATE=false
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=30s --start-period=60s --retries=3 CMD [ "ak", "healthcheck" ]
|
||||
|
||||
ENTRYPOINT [ "dumb-init", "--", "ak" ]
|
||||
394
Makefile
394
Makefile
@@ -1,60 +1,32 @@
|
||||
.PHONY: gen dev-reset all clean test web docs
|
||||
.PHONY: gen dev-reset all clean test web website
|
||||
|
||||
SHELL := /usr/bin/env bash
|
||||
.SHELLFLAGS += ${SHELLFLAGS} -e -o pipefail
|
||||
.SHELLFLAGS += ${SHELLFLAGS} -e
|
||||
PWD = $(shell pwd)
|
||||
UID = $(shell id -u)
|
||||
GID = $(shell id -g)
|
||||
PY_SOURCES = authentik packages tests scripts lifecycle .github
|
||||
NPM_VERSION = $(shell python -m scripts.npm_version)
|
||||
PY_SOURCES = authentik tests scripts lifecycle
|
||||
DOCKER_IMAGE ?= "authentik:test"
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
SED_INPLACE = sed -i ''
|
||||
else
|
||||
SED_INPLACE = sed -i
|
||||
endif
|
||||
pg_user := $(shell python -m authentik.lib.config postgresql.user 2>/dev/null)
|
||||
pg_host := $(shell python -m authentik.lib.config postgresql.host 2>/dev/null)
|
||||
pg_name := $(shell python -m authentik.lib.config postgresql.name 2>/dev/null)
|
||||
|
||||
BREW_LDFLAGS :=
|
||||
BREW_CPPFLAGS :=
|
||||
BREW_PKG_CONFIG_PATH :=
|
||||
CODESPELL_ARGS = -D - -D .github/codespell-dictionary.txt \
|
||||
-I .github/codespell-words.txt \
|
||||
-S 'web/src/locales/**' \
|
||||
authentik \
|
||||
internal \
|
||||
cmd \
|
||||
web/src \
|
||||
website/src \
|
||||
website/blog \
|
||||
website/developer-docs \
|
||||
website/docs \
|
||||
website/integrations \
|
||||
website/src
|
||||
|
||||
CARGO := cargo
|
||||
UV := uv
|
||||
|
||||
# For macOS users, add the libxml2 installed from brew libxmlsec1 to the build path
|
||||
# to prevent SAML-related tests from failing and ensure correct pip dependency compilation
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
# Only add for brew users who installed libxmlsec1
|
||||
BREW_EXISTS := $(shell command -v brew 2> /dev/null)
|
||||
ifdef BREW_EXISTS
|
||||
LIBXML2_EXISTS := $(shell brew list libxml2 2> /dev/null)
|
||||
ifdef LIBXML2_EXISTS
|
||||
_xml_pref := $(shell brew --prefix libxml2)
|
||||
BREW_LDFLAGS += -L${_xml_pref}/lib
|
||||
BREW_CPPFLAGS += -I${_xml_pref}/include
|
||||
BREW_PKG_CONFIG_PATH = ${_xml_pref}/lib/pkgconfig:$(PKG_CONFIG_PATH)
|
||||
endif
|
||||
KRB5_EXISTS := $(shell brew list krb5 2> /dev/null)
|
||||
ifdef KRB5_EXISTS
|
||||
_krb5_pref := $(shell brew --prefix krb5)
|
||||
BREW_LDFLAGS += -L${_krb5_pref}/lib
|
||||
BREW_CPPFLAGS += -I${_krb5_pref}/include
|
||||
BREW_PKG_CONFIG_PATH = ${_krb5_pref}/lib/pkgconfig:$(PKG_CONFIG_PATH)
|
||||
endif
|
||||
UV := LDFLAGS="$(BREW_LDFLAGS)" CPPFLAGS="$(BREW_CPPFLAGS)" PKG_CONFIG_PATH="$(BREW_PKG_CONFIG_PATH)" uv
|
||||
endif
|
||||
endif
|
||||
|
||||
NPM_VERSION :=
|
||||
UV_EXISTS := $(shell command -v uv 2> /dev/null)
|
||||
ifdef UV_EXISTS
|
||||
NPM_VERSION := $(shell $(UV) run python -m scripts.generate_semver)
|
||||
else
|
||||
NPM_VERSION = $(shell python -m scripts.generate_semver)
|
||||
endif
|
||||
|
||||
all: lint-fix lint gen web test ## Lint, build, and test everything
|
||||
all: lint-fix lint test gen web ## Lint, build, and test everything
|
||||
|
||||
HELP_WIDTH := $(shell grep -h '^[a-z][^ ]*:.*\#\#' $(MAKEFILE_LIST) 2>/dev/null | \
|
||||
cut -d':' -f1 | awk '{printf "%d\n", length}' | sort -rn | head -1)
|
||||
@@ -66,247 +38,180 @@ help: ## Show this help
|
||||
sort
|
||||
@echo ""
|
||||
|
||||
go-test: ## Run the golang tests
|
||||
test-go:
|
||||
go test -timeout 0 -v -race -cover ./...
|
||||
|
||||
rust-test: ## Run the Rust tests
|
||||
$(CARGO) nextest run --workspace
|
||||
test-docker: ## Run all tests in a docker-compose
|
||||
echo "PG_PASS=$(openssl rand -base64 32)" >> .env
|
||||
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 32)" >> .env
|
||||
docker-compose pull -q
|
||||
docker-compose up --no-start
|
||||
docker-compose start postgresql redis
|
||||
docker-compose run -u root server test-all
|
||||
rm -f .env
|
||||
|
||||
test: ## Run the server tests and produce a coverage report (locally)
|
||||
$(UV) run coverage run manage.py test --keepdb $(or $(filter-out $@,$(MAKECMDGOALS)),authentik)
|
||||
$(UV) run coverage combine
|
||||
$(UV) run coverage html
|
||||
$(UV) run coverage report
|
||||
coverage run manage.py test --keepdb authentik
|
||||
coverage html
|
||||
coverage report
|
||||
|
||||
lint-fix-rust:
|
||||
$(CARGO) +nightly fmt --all -- --config-path "${PWD}/.cargo/rustfmt.toml"
|
||||
lint-fix: ## Lint and automatically fix errors in the python source code. Reports spelling errors.
|
||||
isort $(PY_SOURCES)
|
||||
black $(PY_SOURCES)
|
||||
ruff $(PY_SOURCES)
|
||||
codespell -w $(CODESPELL_ARGS)
|
||||
|
||||
lint-fix: lint-fix-rust ## Lint and automatically fix errors in the python source code. Reports spelling errors.
|
||||
$(UV) run black $(PY_SOURCES)
|
||||
$(UV) run ruff check --fix $(PY_SOURCES)
|
||||
|
||||
lint-spellcheck: ## Reports spelling errors.
|
||||
npm run lint:spellcheck
|
||||
|
||||
lint: ci-lint-bandit ci-lint-mypy ci-lint-cargo-deny ci-lint-cargo-machete ## Lint the python and golang sources
|
||||
lint: ## Lint the python and golang sources
|
||||
bandit -r $(PY_SOURCES) -x node_modules
|
||||
./web/node_modules/.bin/pyright $(PY_SOURCES)
|
||||
pylint $(PY_SOURCES)
|
||||
golangci-lint run -v
|
||||
|
||||
core-install:
|
||||
ifdef ($(BREW_EXISTS))
|
||||
# Clear cache to ensure fresh compilation
|
||||
$(UV) cache clean
|
||||
# Force compilation from source for lxml and xmlsec with correct environment
|
||||
$(UV) sync --frozen --reinstall-package lxml --reinstall-package xmlsec --no-binary-package lxml --no-binary-package xmlsec
|
||||
else
|
||||
$(UV) sync --frozen
|
||||
endif
|
||||
|
||||
migrate: ## Run the Authentik Django server's migrations
|
||||
$(UV) run python -m lifecycle.migrate
|
||||
python -m lifecycle.migrate
|
||||
|
||||
i18n-extract: core-i18n-extract web-i18n-extract ## Extract strings that require translation into files to send to a translation service
|
||||
i18n-extract: i18n-extract-core web-i18n-extract ## Extract strings that require translation into files to send to a translation service
|
||||
|
||||
aws-cfn:
|
||||
cd lifecycle/aws && npm i && $(UV) run npm run aws-cfn
|
||||
i18n-extract-core:
|
||||
ak makemessages --ignore web --ignore internal --ignore web --ignore web-api --ignore website -l en
|
||||
|
||||
run: ## Run the main authentik server and worker processes
|
||||
$(UV) run ak allinone
|
||||
|
||||
run-watch: ## Run the authentik server and worker, with auto reloading
|
||||
watchexec --on-busy-update=restart --stop-signal=SIGINT --exts py,rs,go --no-meta --notify -- $(UV) run ak allinone
|
||||
|
||||
core-i18n-extract:
|
||||
$(UV) run ak makemessages \
|
||||
--add-location file \
|
||||
--no-obsolete \
|
||||
--ignore web \
|
||||
--ignore internal \
|
||||
--ignore packages/client-ts \
|
||||
--ignore website \
|
||||
-l en
|
||||
|
||||
install: node-install docs-install core-install ## Install all requires dependencies for `node`, `docs` and `core`
|
||||
install: web-install website-install ## Install all requires dependencies for `web`, `website` and `core`
|
||||
poetry install
|
||||
|
||||
dev-drop-db:
|
||||
$(eval pg_user := $(shell $(UV) run python -m authentik.lib.config postgresql.user 2>/dev/null))
|
||||
$(eval pg_host := $(shell $(UV) run python -m authentik.lib.config postgresql.host 2>/dev/null))
|
||||
$(eval pg_name := $(shell $(UV) run python -m authentik.lib.config postgresql.name 2>/dev/null))
|
||||
dropdb -U ${pg_user} -h ${pg_host} ${pg_name} || true
|
||||
dropdb -U ${pg_user} -h ${pg_host} ${pg_name}
|
||||
# Also remove the test-db if it exists
|
||||
dropdb -U ${pg_user} -h ${pg_host} test_${pg_name} || true
|
||||
redis-cli -n 0 flushall
|
||||
|
||||
dev-create-db:
|
||||
$(eval pg_user := $(shell $(UV) run python -m authentik.lib.config postgresql.user 2>/dev/null))
|
||||
$(eval pg_host := $(shell $(UV) run python -m authentik.lib.config postgresql.host 2>/dev/null))
|
||||
$(eval pg_name := $(shell $(UV) run python -m authentik.lib.config postgresql.name 2>/dev/null))
|
||||
createdb -U ${pg_user} -h ${pg_host} ${pg_name}
|
||||
|
||||
dev-reset: dev-drop-db dev-create-db migrate ## Drop and restore the Authentik PostgreSQL instance to a "fresh install" state.
|
||||
|
||||
update-test-mmdb: ## Update test GeoIP and ASN Databases
|
||||
curl \
|
||||
-L \
|
||||
-o ${PWD}/tests/geoip/GeoLite2-ASN-Test.mmdb \
|
||||
https://raw.githubusercontent.com/maxmind/MaxMind-DB/refs/heads/main/test-data/GeoLite2-ASN-Test.mmdb
|
||||
curl \
|
||||
-L \
|
||||
-o ${PWD}/tests/geoip/GeoLite2-City-Test.mmdb \
|
||||
https://raw.githubusercontent.com/maxmind/MaxMind-DB/refs/heads/main/test-data/GeoLite2-City-Test.mmdb
|
||||
|
||||
bump: ## Bump authentik version. Usage: make bump version=20xx.xx.xx
|
||||
ifndef version
|
||||
$(error Usage: make bump version=20xx.xx.xx )
|
||||
endif
|
||||
$(eval current_version := $(shell cat ${PWD}/internal/constants/VERSION))
|
||||
$(SED_INPLACE) 's/^version = ".*"/version = "$(version)"/' ${PWD}/pyproject.toml
|
||||
$(SED_INPLACE) 's/^VERSION = ".*"/VERSION = "$(version)"/' ${PWD}/authentik/__init__.py
|
||||
$(SED_INPLACE) "s/version = \"${current_version}\"/version = \"$(version)\"" ${PWD}/Cargo.toml ${PWD}/Cargo.lock
|
||||
$(MAKE) gen-build gen-compose aws-cfn
|
||||
$(SED_INPLACE) "s/\"${current_version}\"/\"$(version)\"/" ${PWD}/package.json ${PWD}/package-lock.json ${PWD}/web/package.json ${PWD}/web/package-lock.json
|
||||
echo -n $(version) > ${PWD}/internal/constants/VERSION
|
||||
|
||||
#########################
|
||||
## API Schema
|
||||
#########################
|
||||
|
||||
gen-build: ## Extract the schema from the database
|
||||
AUTHENTIK_DEBUG=true \
|
||||
AUTHENTIK_TENANTS__ENABLED=true \
|
||||
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \
|
||||
$(UV) run ak build_schema
|
||||
AUTHENTIK_DEBUG=true ak make_blueprint_schema > blueprints/schema.json
|
||||
AUTHENTIK_DEBUG=true ak spectacular --file schema.yml
|
||||
|
||||
gen-compose:
|
||||
$(UV) run scripts/generate_compose.py
|
||||
|
||||
gen-changelog: ## (Release) generate the changelog based from the commits since the last version
|
||||
# These are best-effort guesses based on commit messages
|
||||
$(eval last_version := $(shell git tag --list 'version/*' --sort 'version:refname' | grep -vE 'rc\d+$$' | tail -1))
|
||||
$(eval current_commit := $(shell git rev-parse HEAD))
|
||||
git log --pretty=format:"- %s" $(shell git merge-base ${last_version} ${current_commit})...${current_commit} > merged_to_current
|
||||
git log --pretty=format:"- %s" $(shell git merge-base ${last_version} ${current_commit})...${last_version} > merged_to_last
|
||||
grep -Eo 'cherry-pick (#\d+)' merged_to_last | cut -d ' ' -f 2 | sed 's/.*/(&)$$/' > cherry_picked_to_last
|
||||
grep -vf cherry_picked_to_last merged_to_current | sort > changelog.md
|
||||
rm merged_to_current
|
||||
rm merged_to_last
|
||||
rm cherry_picked_to_last
|
||||
gen-changelog: ## (Release) generate the changelog based from the commits since the last tag
|
||||
git log --pretty=format:" - %s" $(shell git describe --tags $(shell git rev-list --tags --max-count=1))...$(shell git branch --show-current) | sort > changelog.md
|
||||
npx prettier --write changelog.md
|
||||
|
||||
gen-diff: ## (Release) generate the changelog diff between the current schema and the last version
|
||||
$(eval last_version := $(shell git tag --list 'version/*' --sort 'version:refname' | grep -vE 'rc\d+$$' | tail -1))
|
||||
git show ${last_version}:schema.yml > schema-old.yml
|
||||
docker compose -f scripts/compose.yml run --rm --user "${UID}:${GID}" diff \
|
||||
--markdown \
|
||||
/local/diff.md \
|
||||
/local/schema-old.yml \
|
||||
/local/schema.yml
|
||||
rm schema-old.yml
|
||||
$(SED_INPLACE) 's/{/{/g' diff.md
|
||||
$(SED_INPLACE) 's/}/}/g' diff.md
|
||||
gen-diff: ## (Release) generate the changelog diff between the current schema and the last tag
|
||||
git show $(shell git describe --tags $(shell git rev-list --tags --max-count=1)):schema.yml > old_schema.yml
|
||||
docker run \
|
||||
--rm -v ${PWD}:/local \
|
||||
--user ${UID}:${GID} \
|
||||
docker.io/openapitools/openapi-diff:2.1.0-beta.6 \
|
||||
--markdown /local/diff.md \
|
||||
/local/old_schema.yml /local/schema.yml
|
||||
rm old_schema.yml
|
||||
npx prettier --write diff.md
|
||||
|
||||
gen-client-go: ## Build and install the authentik API for Golang
|
||||
$(UV) run make -C "${PWD}/packages/client-go" build
|
||||
|
||||
gen-client-rust: ## Build and install the authentik API for Rust
|
||||
$(UV) run make -C "${PWD}/packages/client-rust" build version=${NPM_VERSION}
|
||||
make lint-fix-rust
|
||||
gen-clean:
|
||||
rm -rf web/api/src/
|
||||
rm -rf api/
|
||||
|
||||
gen-client-ts: ## Build and install the authentik API for Typescript into the authentik UI Application
|
||||
make -C "${PWD}/packages/client-ts" build
|
||||
npm --prefix web install
|
||||
docker run \
|
||||
--rm -v ${PWD}:/local \
|
||||
--user ${UID}:${GID} \
|
||||
docker.io/openapitools/openapi-generator-cli:v6.5.0 generate \
|
||||
-i /local/schema.yml \
|
||||
-g typescript-fetch \
|
||||
-o /local/gen-ts-api \
|
||||
-c /local/scripts/api-ts-config.yaml \
|
||||
--additional-properties=npmVersion=${NPM_VERSION} \
|
||||
--git-repo-id authentik \
|
||||
--git-user-id goauthentik
|
||||
mkdir -p web/node_modules/@goauthentik/api
|
||||
cd gen-ts-api && npm i
|
||||
\cp -rfv gen-ts-api/* web/node_modules/@goauthentik/api
|
||||
|
||||
_gen-clients: gen-client-go gen-client-rust gen-client-ts
|
||||
gen-clients: ## Build and install API clients used by authentik
|
||||
$(MAKE) _gen-clients -j
|
||||
|
||||
gen: gen-build gen-clients ## Build and install API schema and clients used by authentik
|
||||
gen-client-go: ## Build and install the authentik API for Golang
|
||||
mkdir -p ./gen-go-api ./gen-go-api/templates
|
||||
wget https://raw.githubusercontent.com/goauthentik/client-go/main/config.yaml -O ./gen-go-api/config.yaml
|
||||
wget https://raw.githubusercontent.com/goauthentik/client-go/main/templates/README.mustache -O ./gen-go-api/templates/README.mustache
|
||||
wget https://raw.githubusercontent.com/goauthentik/client-go/main/templates/go.mod.mustache -O ./gen-go-api/templates/go.mod.mustache
|
||||
cp schema.yml ./gen-go-api/
|
||||
docker run \
|
||||
--rm -v ${PWD}/gen-go-api:/local \
|
||||
--user ${UID}:${GID} \
|
||||
docker.io/openapitools/openapi-generator-cli:v6.5.0 generate \
|
||||
-i /local/schema.yml \
|
||||
-g go \
|
||||
-o /local/ \
|
||||
-c /local/config.yaml
|
||||
go mod edit -replace goauthentik.io/api/v3=./gen-go-api
|
||||
rm -rf ./gen-go-api/config.yaml ./gen-go-api/templates/
|
||||
|
||||
gen-dev-config: ## Generate a local development config file
|
||||
$(UV) run scripts/generate_config.py
|
||||
python -m scripts.generate_config
|
||||
|
||||
#########################
|
||||
## Node.js
|
||||
#########################
|
||||
|
||||
node-install: ## Install the necessary libraries to build Node.js packages
|
||||
npm ci
|
||||
npm ci --prefix web
|
||||
gen: gen-build gen-clean gen-client-ts
|
||||
|
||||
#########################
|
||||
## Web
|
||||
#########################
|
||||
|
||||
web-build: node-install ## Build the Authentik UI
|
||||
npm run --prefix web build
|
||||
web-build: web-install ## Build the Authentik UI
|
||||
cd web && npm run build
|
||||
|
||||
web: web-lint-fix web-lint web-check-compile ## Automatically fix formatting issues in the Authentik UI source code, lint the code, and compile it
|
||||
web: web-lint-fix web-lint web-check-compile web-i18n-extract ## Automatically fix formatting issues in the Authentik UI source code, lint the code, and compile it
|
||||
|
||||
web-test: ## Run tests for the Authentik UI
|
||||
npm run --prefix web test
|
||||
web-install: ## Install the necessary libraries to build the Authentik UI
|
||||
cd web && npm ci
|
||||
|
||||
web-watch: ## Build and watch the Authentik UI for changes, updating automatically
|
||||
npm run --prefix web watch
|
||||
rm -rf web/dist/
|
||||
mkdir web/dist/
|
||||
touch web/dist/.gitkeep
|
||||
cd web && npm run watch
|
||||
|
||||
web-storybook-watch: ## Build and run the storybook documentation server
|
||||
npm run --prefix web storybook
|
||||
cd web && npm run storybook
|
||||
|
||||
web-lint-fix:
|
||||
npm run --prefix web prettier
|
||||
cd web && npm run prettier
|
||||
|
||||
web-lint:
|
||||
npm run --prefix web lint
|
||||
npm run --prefix web lit-analyse
|
||||
cd web && npm run lint
|
||||
cd web && npm run lit-analyse
|
||||
|
||||
web-check-compile:
|
||||
npm run --prefix web tsc
|
||||
cd web && npm run tsc
|
||||
|
||||
web-i18n-extract:
|
||||
npm run --prefix web extract-locales
|
||||
cd web && npm run extract-locales
|
||||
|
||||
#########################
|
||||
## Docs
|
||||
## Website
|
||||
#########################
|
||||
|
||||
docs: docs-lint-fix docs-build ## Automatically fix formatting issues in the Authentik docs source code, lint the code, and compile it
|
||||
website: website-lint-fix website-build ## Automatically fix formatting issues in the Authentik website/docs source code, lint the code, and compile it
|
||||
|
||||
docs-install:
|
||||
npm ci --prefix website
|
||||
website-install:
|
||||
cd website && npm ci
|
||||
|
||||
docs-lint-fix: lint-spellcheck
|
||||
npm run --prefix website prettier
|
||||
website-lint-fix:
|
||||
cd website && npm run prettier
|
||||
|
||||
docs-build:
|
||||
npm run --prefix website build
|
||||
website-build:
|
||||
cd website && npm run build
|
||||
|
||||
docs-watch: ## Build and watch the topics documentation
|
||||
npm run --prefix website start
|
||||
|
||||
integrations: docs-lint-fix integrations-build ## Fix formatting issues in the integrations source code, lint the code, and compile it
|
||||
|
||||
integrations-build:
|
||||
npm run --prefix website -w integrations build
|
||||
|
||||
integrations-watch: ## Build and watch the Integrations documentation
|
||||
npm run --prefix website -w integrations start
|
||||
|
||||
docs-api-build:
|
||||
npm run --prefix website -w api build
|
||||
|
||||
docs-api-watch: ## Build and watch the API documentation
|
||||
npm run --prefix website -w api generate
|
||||
npm run --prefix website -w api start
|
||||
|
||||
docs-api-clean: ## Clean generated API documentation
|
||||
npm run --prefix website -w api build:api:clean
|
||||
website-watch: ## Build and watch the documentation website, updating automatically
|
||||
cd website && npm run watch
|
||||
|
||||
#########################
|
||||
## Docker
|
||||
#########################
|
||||
|
||||
docker: ## Build a docker image of the current source tree
|
||||
DOCKER_BUILDKIT=1 docker build . -f lifecycle/container/Dockerfile --progress plain --tag ${DOCKER_IMAGE}
|
||||
|
||||
test-docker:
|
||||
BUILD=true ${PWD}/scripts/test_docker.sh
|
||||
DOCKER_BUILDKIT=1 docker build . --progress plain --tag ${DOCKER_IMAGE}
|
||||
|
||||
#########################
|
||||
## CI
|
||||
@@ -315,42 +220,29 @@ test-docker:
|
||||
# which makes the YAML File a lot smaller
|
||||
|
||||
ci--meta-debug:
|
||||
$(UV) run python -V || echo "No python installed"
|
||||
$(CARGO) --version || echo "No rust installed"
|
||||
node --version || echo "No node installed"
|
||||
python -V
|
||||
node --version
|
||||
|
||||
ci-lint-mypy: ci--meta-debug
|
||||
$(UV) run mypy --strict $(PY_SOURCES)
|
||||
ci-pylint: ci--meta-debug
|
||||
pylint $(PY_SOURCES)
|
||||
|
||||
ci-lint-black: ci--meta-debug
|
||||
$(UV) run black --check $(PY_SOURCES)
|
||||
ci-black: ci--meta-debug
|
||||
black --check $(PY_SOURCES)
|
||||
|
||||
ci-lint-ruff: ci--meta-debug
|
||||
$(UV) run ruff check $(PY_SOURCES)
|
||||
ci-ruff: ci--meta-debug
|
||||
ruff check $(PY_SOURCES)
|
||||
|
||||
ci-lint-spellcheck: ci--meta-debug
|
||||
npm run lint:spellcheck
|
||||
ci-codespell: ci--meta-debug
|
||||
codespell $(CODESPELL_ARGS) -s
|
||||
|
||||
ci-lint-bandit: ci--meta-debug
|
||||
$(UV) run bandit -c pyproject.toml -r $(PY_SOURCES) -iii
|
||||
ci-isort: ci--meta-debug
|
||||
isort --check $(PY_SOURCES)
|
||||
|
||||
ci-lint-pending-migrations: ci--meta-debug
|
||||
$(UV) run ak makemigrations --check
|
||||
ci-bandit: ci--meta-debug
|
||||
bandit -r $(PY_SOURCES)
|
||||
|
||||
ci-lint-cargo-deny: ci--meta-debug
|
||||
$(CARGO) deny --locked --workspace check --config "${PWD}/.cargo/deny.toml"
|
||||
ci-pyright: ci--meta-debug
|
||||
./web/node_modules/.bin/pyright $(PY_SOURCES)
|
||||
|
||||
ci-lint-cargo-machete: ci--meta-debug
|
||||
$(CARGO) machete
|
||||
|
||||
ci-lint-rustfmt: ci--meta-debug
|
||||
$(CARGO) +nightly fmt --all --check -- --config-path "${PWD}/.cargo/rustfmt.toml"
|
||||
|
||||
ci-lint-clippy: ci--meta-debug
|
||||
$(CARGO) clippy --workspace -- -D warnings
|
||||
|
||||
ci-test: ci--meta-debug
|
||||
$(UV) run coverage run manage.py test --keepdb --parallel auto authentik
|
||||
$(UV) run coverage combine
|
||||
$(UV) run coverage report
|
||||
$(UV) run coverage xml
|
||||
ci-pending-migrations: ci--meta-debug
|
||||
ak makemigrations --check
|
||||
|
||||
40
README.md
40
README.md
@@ -9,43 +9,35 @@
|
||||
[](https://github.com/goauthentik/authentik/actions/workflows/ci-outpost.yml)
|
||||
[](https://github.com/goauthentik/authentik/actions/workflows/ci-web.yml)
|
||||
[](https://codecov.io/gh/goauthentik/authentik)
|
||||

|
||||
[](https://explore.transifex.com/authentik/authentik/)
|
||||

|
||||

|
||||
[](https://www.transifex.com/authentik/authentik/)
|
||||
|
||||
## What is authentik?
|
||||
|
||||
authentik is an open-source Identity Provider (IdP) for modern SSO. It supports SAML, OAuth2/OIDC, LDAP, RADIUS, and more, designed for self-hosting from small labs to large production clusters.
|
||||
|
||||
Our [enterprise offering](https://goauthentik.io/pricing) is available for organizations to securely replace existing IdPs such as Okta, Auth0, Entra ID, and Ping Identity for robust, large-scale identity management.
|
||||
authentik is an open-source Identity Provider that emphasizes flexibility and versatility. It can be seamlessly integrated into existing environments to support new protocols. authentik is also a great solution for implementing sign-up, recovery, and other similar features in your application, saving you the hassle of dealing with them.
|
||||
|
||||
## Installation
|
||||
|
||||
- Docker Compose: recommended for small/test setups. See the [documentation](https://docs.goauthentik.io/docs/install-config/install/docker-compose/).
|
||||
- Kubernetes (Helm Chart): recommended for larger setups. See the [documentation](https://docs.goauthentik.io/docs/install-config/install/kubernetes/) and the Helm chart [repository](https://github.com/goauthentik/helm).
|
||||
- AWS CloudFormation: deploy on AWS using our official templates. See the [documentation](https://docs.goauthentik.io/docs/install-config/install/aws/).
|
||||
- DigitalOcean Marketplace: one-click deployment via the official Marketplace app. See the [app listing](https://marketplace.digitalocean.com/apps/authentik).
|
||||
For small/test setups it is recommended to use Docker Compose; refer to the [documentation](https://goauthentik.io/docs/installation/docker-compose/?utm_source=github).
|
||||
|
||||
For bigger setups, there is a Helm Chart [here](https://github.com/goauthentik/helm). This is documented [here](https://goauthentik.io/docs/installation/kubernetes/?utm_source=github).
|
||||
|
||||
## Screenshots
|
||||
|
||||
| Light | Dark |
|
||||
| ----------------------------------------------------------- | ---------------------------------------------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
| Light | Dark |
|
||||
| ------------------------------------------------------ | ----------------------------------------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
## Development and contributions
|
||||
## Development
|
||||
|
||||
See the [Developer Documentation](https://docs.goauthentik.io/docs/developer-docs/) for information about setting up local build environments, testing your contributions, and our contribution process.
|
||||
See [Developer Documentation](https://goauthentik.io/developer-docs/?utm_source=github)
|
||||
|
||||
## Security
|
||||
|
||||
Please see [SECURITY.md](SECURITY.md).
|
||||
See [SECURITY.md](SECURITY.md)
|
||||
|
||||
## Adoption
|
||||
## Adoption and Contributions
|
||||
|
||||
Using authentik? We'd love to hear your story and feature your logo. Email us at [hello@goauthentik.io](mailto:hello@goauthentik.io) or open a GitHub Issue/PR!
|
||||
|
||||
## License
|
||||
|
||||
[](LICENSE)
|
||||
[](website/LICENSE)
|
||||
[](authentik/enterprise/LICENSE)
|
||||
Your organization uses authentik? We'd love to add your logo to the readme and our website! Email us @ hello@goauthentik.io or open a GitHub Issue/PR! For more information on how to contribute to authentik, please refer to our [CONTRIBUTING.md file](./CONTRIBUTING.md).
|
||||
|
||||
81
SECURITY.md
81
SECURITY.md
@@ -1,9 +1,5 @@
|
||||
authentik takes security very seriously. We follow the rules of [responsible disclosure](https://en.wikipedia.org/wiki/Responsible_disclosure), and we urge our community to do so as well, instead of reporting vulnerabilities publicly. This allows us to patch the issue quickly, announce it's existence and release the fixed version.
|
||||
|
||||
## Independent audits and pentests
|
||||
|
||||
We are committed to engaging in regular pentesting and security audits of authentik. Defining and adhering to a cadence of external testing ensures a stronger probability that our code base, our features, and our architecture is as secure and non-exploitable as possible. For more details about specific audits and pentests, refer to "Audits and Certificates" in our [Security documentation](https://docs.goauthentik.io/docs/security).
|
||||
|
||||
## What authentik classifies as a CVE
|
||||
|
||||
CVE (Common Vulnerability and Exposure) is a system designed to aggregate all vulnerabilities. As such, a CVE will be issued when there is a either vulnerability or exposure. Per NIST, A vulnerability is:
|
||||
@@ -18,82 +14,27 @@ Even if the issue is not a CVE, we still greatly appreciate your help in hardeni
|
||||
|
||||
(.x being the latest patch release for each version)
|
||||
|
||||
| Version | Supported |
|
||||
| --------- | --------- |
|
||||
| 2025.12.x | ✅ |
|
||||
| 2026.2.x | ✅ |
|
||||
| Version | Supported |
|
||||
| --- | --- |
|
||||
| 2023.6.x | ✅ |
|
||||
| 2023.8.x | ✅ |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you discover a potential vulnerability, please report it responsibly through one of the following channels:
|
||||
|
||||
- **Email**: [security@goauthentik.io](mailto:security@goauthentik.io)
|
||||
- **GitHub**: Submit a private security advisory via our [repository’s advisory portal](https://github.com/goauthentik/authentik/security/advisories/new)
|
||||
|
||||
When submitting a report, please include as much detail as possible, such as:
|
||||
|
||||
- **Affected version(s)**: The version of authentik where the issue was identified.
|
||||
- **Steps to reproduce**: A clear description or proof of concept to help us verify the issue.
|
||||
- **Impact assessment**: How the vulnerability could be exploited and its potential effect.
|
||||
- **Additional information**: Logs, configuration details (if relevant), or any suggested mitigations.
|
||||
|
||||
We kindly ask that you do not disclose the vulnerability publicly until we have confirmed and addressed the issue.
|
||||
|
||||
Our team will:
|
||||
|
||||
- Acknowledge receipt of your report as quickly as possible.
|
||||
- Keep you updated on the investigation and resolution progress.
|
||||
|
||||
## Researcher Recognition
|
||||
|
||||
We value contributions from the security community. For each valid report, we will publish a dedicated entry on our Security Advisory page that optionally includes the reporter’s name (or preferred alias). Please note that while we do not currently offer monetary bounties, we are committed to giving researchers appropriate credit for their efforts in keeping authentik secure.
|
||||
To report a vulnerability, send an email to [security@goauthentik.io](mailto:security@goauthentik.io). Be sure to include relevant information like which version you've found the issue in, instructions on how to reproduce the issue, and anything else that might make it easier for us to find the issue.
|
||||
|
||||
## Severity levels
|
||||
|
||||
authentik reserves the right to reclassify CVSS as necessary. To determine severity, we will use the CVSS calculator from NVD (https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator). The calculated CVSS score will then be translated into one of the following categories:
|
||||
|
||||
| Score | Severity |
|
||||
| ---------- | -------- |
|
||||
| 0.0 | None |
|
||||
| 0.1 – 3.9 | Low |
|
||||
| 4.0 – 6.9 | Medium |
|
||||
| 7.0 – 8.9 | High |
|
||||
| Score | Severity |
|
||||
| --- | --- |
|
||||
| 0.0 | None |
|
||||
| 0.1 – 3.9 | Low |
|
||||
| 4.0 – 6.9 | Medium |
|
||||
| 7.0 – 8.9 | High |
|
||||
| 9.0 – 10.0 | Critical |
|
||||
|
||||
## Intended functionality
|
||||
|
||||
The following capabilities are part of intentional system design and should not be reported as security vulnerabilities:
|
||||
|
||||
- Expressions (property mappings/policies/prompts) can execute arbitrary Python code without safeguards.
|
||||
|
||||
This is expected behavior. Any user with permission to create or modify objects containing expression fields can write code that is executed within authentik. If a vulnerability allows a user without the required permissions to write or modify code and have it executed, that would be a valid security report.
|
||||
|
||||
However, the fact that expressions are executed as part of normal operations is not considered a privilege escalation or security vulnerability.
|
||||
|
||||
- Blueprints can access all files on the filesystem.
|
||||
|
||||
This access is intentional to allow legitimate configuration and deployment tasks. It does not represent a security problem by itself.
|
||||
|
||||
- Importing blueprints allows arbitrary modification of application objects.
|
||||
|
||||
This is intended functionality. This behavior reflects the privileged design of blueprint imports. It is "exploitable" when importing blueprints from untrusted sources without reviewing the blueprint beforehand. However, any method to create, modify or execute blueprints without the required permissions would be a valid security report.
|
||||
|
||||
- Flow imports may contain objects other than flows (such as policies, users, groups, etc.)
|
||||
|
||||
This is expected behavior as flow imports are blueprint files.
|
||||
|
||||
- Prompt HTML is not escaped.
|
||||
|
||||
Prompts intentionally allow raw HTML, including script tags, so they can be used to create interactive or customized user interface elements. Because of this, scripts within prompts may affect or interact with the surrounding page as designed.
|
||||
|
||||
- Open redirects that do not include tokens or other sensitive information are not considered a security vulnerability.
|
||||
|
||||
Redirects that only change navigation flow and do not expose session tokens, API keys, or other confidential data are considered acceptable and do not require reporting.
|
||||
|
||||
- Outgoing network requests are not filtered.
|
||||
|
||||
The destinations of outgoing network requests (HTTP, TCP, etc.) made by authentik to configurable endpoints through objects such as OAuth Sources, SSO Providers, and others are not validated. Depending on your threat model, these requests should be restricted at the network level using appropriate firewall or network policies.
|
||||
|
||||
## Disclosure process
|
||||
|
||||
1. Report from Github or Issue is reported via Email as listed above.
|
||||
|
||||
@@ -1,28 +1,20 @@
|
||||
"""authentik root module"""
|
||||
|
||||
from functools import lru_cache
|
||||
from os import environ
|
||||
from typing import Optional
|
||||
|
||||
VERSION = "2026.5.0-rc1"
|
||||
__version__ = "2023.10.7"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
||||
|
||||
@lru_cache
|
||||
def authentik_version() -> str:
|
||||
return VERSION
|
||||
|
||||
|
||||
@lru_cache
|
||||
def authentik_build_hash(fallback: str | None = None) -> str:
|
||||
def get_build_hash(fallback: Optional[str] = None) -> str:
|
||||
"""Get build hash"""
|
||||
build_hash = environ.get(ENV_GIT_HASH_KEY, fallback if fallback else "")
|
||||
return fallback if build_hash == "" and fallback else build_hash
|
||||
|
||||
|
||||
@lru_cache
|
||||
def authentik_full_version() -> str:
|
||||
def get_full_version() -> str:
|
||||
"""Get full version, with build hash appended"""
|
||||
version = authentik_version()
|
||||
if (build_hash := authentik_build_hash()) != "":
|
||||
return f"{version}+{build_hash}"
|
||||
version = __version__
|
||||
if (build_hash := get_build_hash()) != "":
|
||||
version += "." + build_hash
|
||||
return version
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
"""Meta API"""
|
||||
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.fields import CharField
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
@@ -8,8 +7,8 @@ from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.lib.api import Models
|
||||
from authentik.lib.utils.reflection import get_apps
|
||||
from authentik.policies.event_matcher.models import model_choices
|
||||
|
||||
|
||||
class AppSerializer(PassiveSerializer):
|
||||
@@ -42,6 +41,6 @@ class ModelViewSet(ViewSet):
|
||||
def list(self, request: Request) -> Response:
|
||||
"""Read-only view list all installed models"""
|
||||
data = []
|
||||
for name, label in Models.choices:
|
||||
for name, label in model_choices():
|
||||
data.append({"name": name, "label": label})
|
||||
return Response(AppSerializer(data, many=True).data)
|
||||
|
||||
78
authentik/admin/api/metrics.py
Normal file
78
authentik/admin/api/metrics.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""authentik administration metrics"""
|
||||
from datetime import timedelta
|
||||
|
||||
from django.db.models.functions import ExtractHour
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_field
|
||||
from guardian.shortcuts import get_objects_for_user
|
||||
from rest_framework.fields import IntegerField, SerializerMethodField
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.events.models import EventAction
|
||||
|
||||
|
||||
class CoordinateSerializer(PassiveSerializer):
|
||||
"""Coordinates for diagrams"""
|
||||
|
||||
x_cord = IntegerField(read_only=True)
|
||||
y_cord = IntegerField(read_only=True)
|
||||
|
||||
|
||||
class LoginMetricsSerializer(PassiveSerializer):
|
||||
"""Login Metrics per 1h"""
|
||||
|
||||
logins = SerializerMethodField()
|
||||
logins_failed = SerializerMethodField()
|
||||
authorizations = SerializerMethodField()
|
||||
|
||||
@extend_schema_field(CoordinateSerializer(many=True))
|
||||
def get_logins(self, _):
|
||||
"""Get successful logins per 8 hours for the last 7 days"""
|
||||
user = self.context["user"]
|
||||
return (
|
||||
get_objects_for_user(user, "authentik_events.view_event").filter(
|
||||
action=EventAction.LOGIN
|
||||
)
|
||||
# 3 data points per day, so 8 hour spans
|
||||
.get_events_per(timedelta(days=7), ExtractHour, 7 * 3)
|
||||
)
|
||||
|
||||
@extend_schema_field(CoordinateSerializer(many=True))
|
||||
def get_logins_failed(self, _):
|
||||
"""Get failed logins per 8 hours for the last 7 days"""
|
||||
user = self.context["user"]
|
||||
return (
|
||||
get_objects_for_user(user, "authentik_events.view_event").filter(
|
||||
action=EventAction.LOGIN_FAILED
|
||||
)
|
||||
# 3 data points per day, so 8 hour spans
|
||||
.get_events_per(timedelta(days=7), ExtractHour, 7 * 3)
|
||||
)
|
||||
|
||||
@extend_schema_field(CoordinateSerializer(many=True))
|
||||
def get_authorizations(self, _):
|
||||
"""Get successful authorizations per 8 hours for the last 7 days"""
|
||||
user = self.context["user"]
|
||||
return (
|
||||
get_objects_for_user(user, "authentik_events.view_event").filter(
|
||||
action=EventAction.AUTHORIZE_APPLICATION
|
||||
)
|
||||
# 3 data points per day, so 8 hour spans
|
||||
.get_events_per(timedelta(days=7), ExtractHour, 7 * 3)
|
||||
)
|
||||
|
||||
|
||||
class AdministrationMetricsViewSet(APIView):
|
||||
"""Login Metrics per 1h"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(responses={200: LoginMetricsSerializer(many=False)})
|
||||
def get(self, request: Request) -> Response:
|
||||
"""Login Metrics per 1h"""
|
||||
serializer = LoginMetricsSerializer(True)
|
||||
serializer.context["user"] = request.user
|
||||
return Response(serializer.data)
|
||||
@@ -1,77 +1,53 @@
|
||||
"""authentik administration overview"""
|
||||
|
||||
import platform
|
||||
from datetime import datetime
|
||||
from ssl import OPENSSL_VERSION
|
||||
from sys import version as python_version
|
||||
from typing import TypedDict
|
||||
|
||||
from cryptography.hazmat.backends.openssl.backend import backend
|
||||
from django.conf import settings
|
||||
from django.utils.timezone import now
|
||||
from django.views.debug import SafeExceptionReporterFilter
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from gunicorn import version_info as gunicorn_version
|
||||
from rest_framework.fields import SerializerMethodField
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from authentik import authentik_full_version
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.utils.reflection import get_env
|
||||
from authentik.outposts.apps import MANAGED_OUTPOST
|
||||
from authentik.outposts.models import Outpost
|
||||
from authentik.rbac.permissions import HasPermission
|
||||
|
||||
|
||||
def fips_enabled():
|
||||
try:
|
||||
from authentik.enterprise.license import LicenseKey
|
||||
|
||||
return backend._fips_enabled if LicenseKey.get_total().status().is_valid else None
|
||||
except ModuleNotFoundError:
|
||||
return None
|
||||
|
||||
|
||||
class RuntimeDict(TypedDict):
|
||||
"""Runtime information"""
|
||||
|
||||
python_version: str
|
||||
gunicorn_version: str
|
||||
environment: str
|
||||
architecture: str
|
||||
platform: str
|
||||
uname: str
|
||||
openssl_version: str
|
||||
openssl_fips_enabled: bool | None
|
||||
authentik_version: str
|
||||
|
||||
|
||||
class SystemInfoSerializer(PassiveSerializer):
|
||||
class SystemSerializer(PassiveSerializer):
|
||||
"""Get system information."""
|
||||
|
||||
http_headers = SerializerMethodField()
|
||||
http_host = SerializerMethodField()
|
||||
http_is_secure = SerializerMethodField()
|
||||
runtime = SerializerMethodField()
|
||||
brand = SerializerMethodField()
|
||||
tenant = SerializerMethodField()
|
||||
server_time = SerializerMethodField()
|
||||
embedded_outpost_disabled = SerializerMethodField()
|
||||
embedded_outpost_host = SerializerMethodField()
|
||||
|
||||
def get_http_headers(self, request: Request) -> dict[str, str]:
|
||||
"""Get HTTP Request headers"""
|
||||
headers = {}
|
||||
raw_session = request._request.COOKIES.get(settings.SESSION_COOKIE_NAME)
|
||||
for key, value in request.META.items():
|
||||
if not isinstance(value, str):
|
||||
continue
|
||||
actual_value = value
|
||||
if raw_session is not None and raw_session in actual_value:
|
||||
actual_value = actual_value.replace(
|
||||
raw_session, SafeExceptionReporterFilter.cleansed_substitute
|
||||
)
|
||||
headers[key] = actual_value
|
||||
headers[key] = value
|
||||
return headers
|
||||
|
||||
def get_http_host(self, request: Request) -> str:
|
||||
@@ -85,28 +61,22 @@ class SystemInfoSerializer(PassiveSerializer):
|
||||
def get_runtime(self, request: Request) -> RuntimeDict:
|
||||
"""Get versions"""
|
||||
return {
|
||||
"architecture": platform.machine(),
|
||||
"authentik_version": authentik_full_version(),
|
||||
"environment": get_env(),
|
||||
"openssl_fips_enabled": fips_enabled(),
|
||||
"openssl_version": OPENSSL_VERSION,
|
||||
"platform": platform.platform(),
|
||||
"python_version": python_version,
|
||||
"gunicorn_version": ".".join(str(x) for x in gunicorn_version),
|
||||
"environment": get_env(),
|
||||
"architecture": platform.machine(),
|
||||
"platform": platform.platform(),
|
||||
"uname": " ".join(platform.uname()),
|
||||
}
|
||||
|
||||
def get_brand(self, request: Request) -> str:
|
||||
"""Currently active brand"""
|
||||
return str(request._request.brand)
|
||||
def get_tenant(self, request: Request) -> str:
|
||||
"""Currently active tenant"""
|
||||
return str(request._request.tenant)
|
||||
|
||||
def get_server_time(self, request: Request) -> datetime:
|
||||
"""Current server time"""
|
||||
return now()
|
||||
|
||||
def get_embedded_outpost_disabled(self, request: Request) -> bool:
|
||||
"""Whether the embedded outpost is disabled"""
|
||||
return CONFIG.get_bool("outposts.disable_embedded_outpost", False)
|
||||
|
||||
def get_embedded_outpost_host(self, request: Request) -> str:
|
||||
"""Get the FQDN configured on the embedded outpost"""
|
||||
outposts = Outpost.objects.filter(managed=MANAGED_OUTPOST)
|
||||
@@ -121,14 +91,14 @@ class SystemView(APIView):
|
||||
permission_classes = [HasPermission("authentik_rbac.view_system_info")]
|
||||
pagination_class = None
|
||||
filter_backends = []
|
||||
serializer_class = SystemInfoSerializer
|
||||
serializer_class = SystemSerializer
|
||||
|
||||
@extend_schema(responses={200: SystemInfoSerializer(many=False)})
|
||||
@extend_schema(responses={200: SystemSerializer(many=False)})
|
||||
def get(self, request: Request) -> Response:
|
||||
"""Get system information."""
|
||||
return Response(SystemInfoSerializer(request).data)
|
||||
return Response(SystemSerializer(request).data)
|
||||
|
||||
@extend_schema(responses={200: SystemInfoSerializer(many=False)})
|
||||
@extend_schema(responses={200: SystemSerializer(many=False)})
|
||||
def post(self, request: Request) -> Response:
|
||||
"""Get system information."""
|
||||
return Response(SystemInfoSerializer(request).data)
|
||||
return Response(SystemSerializer(request).data)
|
||||
|
||||
134
authentik/admin/api/tasks.py
Normal file
134
authentik/admin/api/tasks.py
Normal file
@@ -0,0 +1,134 @@
|
||||
"""Tasks API"""
|
||||
from importlib import import_module
|
||||
|
||||
from django.contrib import messages
|
||||
from django.http.response import Http404
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import (
|
||||
CharField,
|
||||
ChoiceField,
|
||||
DateTimeField,
|
||||
ListField,
|
||||
SerializerMethodField,
|
||||
)
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ViewSet
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.api.decorators import permission_required
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.events.monitored_tasks import TaskInfo, TaskResultStatus
|
||||
from authentik.rbac.permissions import HasPermission
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class TaskSerializer(PassiveSerializer):
|
||||
"""Serialize TaskInfo and TaskResult"""
|
||||
|
||||
task_name = CharField()
|
||||
task_description = CharField()
|
||||
task_finish_timestamp = DateTimeField(source="finish_time")
|
||||
task_duration = SerializerMethodField()
|
||||
|
||||
status = ChoiceField(
|
||||
source="result.status.name",
|
||||
choices=[(x.name, x.name) for x in TaskResultStatus],
|
||||
)
|
||||
messages = ListField(source="result.messages")
|
||||
|
||||
def get_task_duration(self, instance: TaskInfo) -> int:
|
||||
"""Get the duration a task took to run"""
|
||||
return max(instance.finish_timestamp - instance.start_timestamp, 0)
|
||||
|
||||
def to_representation(self, instance: TaskInfo):
|
||||
"""When a new version of authentik adds fields to TaskInfo,
|
||||
the API will fail with an AttributeError, as the classes
|
||||
are pickled in cache. In that case, just delete the info"""
|
||||
try:
|
||||
return super().to_representation(instance)
|
||||
# pylint: disable=broad-except
|
||||
except Exception: # pragma: no cover
|
||||
if isinstance(self.instance, list):
|
||||
for inst in self.instance:
|
||||
inst.delete()
|
||||
else:
|
||||
self.instance.delete()
|
||||
return {}
|
||||
|
||||
|
||||
class TaskViewSet(ViewSet):
|
||||
"""Read-only view set that returns all background tasks"""
|
||||
|
||||
permission_classes = [HasPermission("authentik_rbac.view_system_tasks")]
|
||||
serializer_class = TaskSerializer
|
||||
|
||||
@extend_schema(
|
||||
responses={
|
||||
200: TaskSerializer(many=False),
|
||||
404: OpenApiResponse(description="Task not found"),
|
||||
},
|
||||
parameters=[
|
||||
OpenApiParameter(
|
||||
"id",
|
||||
type=OpenApiTypes.STR,
|
||||
location=OpenApiParameter.PATH,
|
||||
required=True,
|
||||
),
|
||||
],
|
||||
)
|
||||
def retrieve(self, request: Request, pk=None) -> Response:
|
||||
"""Get a single system task"""
|
||||
task = TaskInfo.by_name(pk)
|
||||
if not task:
|
||||
raise Http404
|
||||
return Response(TaskSerializer(task, many=False).data)
|
||||
|
||||
@extend_schema(responses={200: TaskSerializer(many=True)})
|
||||
def list(self, request: Request) -> Response:
|
||||
"""List system tasks"""
|
||||
tasks = sorted(TaskInfo.all().values(), key=lambda task: task.task_name)
|
||||
return Response(TaskSerializer(tasks, many=True).data)
|
||||
|
||||
@permission_required(None, ["authentik_rbac.run_system_tasks"])
|
||||
@extend_schema(
|
||||
request=OpenApiTypes.NONE,
|
||||
responses={
|
||||
204: OpenApiResponse(description="Task retried successfully"),
|
||||
404: OpenApiResponse(description="Task not found"),
|
||||
500: OpenApiResponse(description="Failed to retry task"),
|
||||
},
|
||||
parameters=[
|
||||
OpenApiParameter(
|
||||
"id",
|
||||
type=OpenApiTypes.STR,
|
||||
location=OpenApiParameter.PATH,
|
||||
required=True,
|
||||
),
|
||||
],
|
||||
)
|
||||
@action(detail=True, methods=["post"])
|
||||
def retry(self, request: Request, pk=None) -> Response:
|
||||
"""Retry task"""
|
||||
task = TaskInfo.by_name(pk)
|
||||
if not task:
|
||||
raise Http404
|
||||
try:
|
||||
task_module = import_module(task.task_call_module)
|
||||
task_func = getattr(task_module, task.task_call_func)
|
||||
LOGGER.debug("Running task", task=task_func)
|
||||
task_func.delay(*task.task_call_args, **task.task_call_kwargs)
|
||||
messages.success(
|
||||
self.request,
|
||||
_("Successfully re-scheduled Task %(name)s!" % {"name": task.task_name}),
|
||||
)
|
||||
return Response(status=204)
|
||||
except (ImportError, AttributeError): # pragma: no cover
|
||||
LOGGER.warning("Failed to run task, remove state", task=task)
|
||||
# if we get an import error, the module path has probably changed
|
||||
task.delete()
|
||||
return Response(status=500)
|
||||
@@ -1,7 +1,5 @@
|
||||
"""authentik administration overview"""
|
||||
|
||||
from django.core.cache import cache
|
||||
from django_tenants.utils import get_public_schema_name
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from packaging.version import parse
|
||||
from rest_framework.fields import SerializerMethodField
|
||||
@@ -10,11 +8,9 @@ from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from authentik import authentik_build_hash, authentik_version
|
||||
from authentik.admin.tasks import VERSION_CACHE_KEY, VERSION_NULL, update_latest_version
|
||||
from authentik import __version__, get_build_hash
|
||||
from authentik.admin.tasks import VERSION_CACHE_KEY, update_latest_version
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.outposts.models import Outpost
|
||||
from authentik.tenants.utils import get_current_tenant
|
||||
|
||||
|
||||
class VersionSerializer(PassiveSerializer):
|
||||
@@ -22,46 +18,29 @@ class VersionSerializer(PassiveSerializer):
|
||||
|
||||
version_current = SerializerMethodField()
|
||||
version_latest = SerializerMethodField()
|
||||
version_latest_valid = SerializerMethodField()
|
||||
build_hash = SerializerMethodField()
|
||||
outdated = SerializerMethodField()
|
||||
outpost_outdated = SerializerMethodField()
|
||||
|
||||
def get_build_hash(self, _) -> str:
|
||||
"""Get build hash, if version is not latest or released"""
|
||||
return authentik_build_hash()
|
||||
return get_build_hash()
|
||||
|
||||
def get_version_current(self, _) -> str:
|
||||
"""Get current version"""
|
||||
return authentik_version()
|
||||
return __version__
|
||||
|
||||
def get_version_latest(self, _) -> str:
|
||||
"""Get latest version from cache"""
|
||||
if get_current_tenant().schema_name != get_public_schema_name():
|
||||
return authentik_version()
|
||||
version_in_cache = cache.get(VERSION_CACHE_KEY)
|
||||
if not version_in_cache: # pragma: no cover
|
||||
update_latest_version.send()
|
||||
return authentik_version()
|
||||
update_latest_version.delay()
|
||||
return __version__
|
||||
return version_in_cache
|
||||
|
||||
def get_version_latest_valid(self, _) -> bool:
|
||||
"""Check if latest version is valid"""
|
||||
return cache.get(VERSION_CACHE_KEY) != VERSION_NULL
|
||||
|
||||
def get_outdated(self, instance) -> bool:
|
||||
"""Check if we're running the latest version"""
|
||||
return parse(self.get_version_current(instance)) < parse(self.get_version_latest(instance))
|
||||
|
||||
def get_outpost_outdated(self, _) -> bool:
|
||||
"""Check if any outpost is outdated/has a version mismatch"""
|
||||
any_outdated = False
|
||||
for outpost in Outpost.objects.all():
|
||||
for state in outpost.state:
|
||||
if state.version_outdated:
|
||||
any_outdated = True
|
||||
return any_outdated
|
||||
|
||||
|
||||
class VersionView(APIView):
|
||||
"""Get running and latest version."""
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
from rest_framework.permissions import IsAdminUser
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
from authentik.admin.models import VersionHistory
|
||||
from authentik.core.api.utils import ModelSerializer
|
||||
|
||||
|
||||
class VersionHistorySerializer(ModelSerializer):
|
||||
"""VersionHistory Serializer"""
|
||||
|
||||
class Meta:
|
||||
model = VersionHistory
|
||||
fields = [
|
||||
"id",
|
||||
"timestamp",
|
||||
"version",
|
||||
"build",
|
||||
]
|
||||
|
||||
|
||||
class VersionHistoryViewSet(ReadOnlyModelViewSet):
|
||||
"""VersionHistory Viewset"""
|
||||
|
||||
queryset = VersionHistory.objects.all()
|
||||
serializer_class = VersionHistorySerializer
|
||||
permission_classes = [IsAdminUser]
|
||||
filterset_fields = [
|
||||
"version",
|
||||
"build",
|
||||
]
|
||||
search_fields = ["version", "build"]
|
||||
ordering = ["-timestamp"]
|
||||
pagination_class = None
|
||||
25
authentik/admin/api/workers.py
Normal file
25
authentik/admin/api/workers.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""authentik administration overview"""
|
||||
from django.conf import settings
|
||||
from drf_spectacular.utils import extend_schema, inline_serializer
|
||||
from rest_framework.fields import IntegerField
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from authentik.rbac.permissions import HasPermission
|
||||
from authentik.root.celery import CELERY_APP
|
||||
|
||||
|
||||
class WorkerView(APIView):
|
||||
"""Get currently connected worker count."""
|
||||
|
||||
permission_classes = [HasPermission("authentik_rbac.view_system_info")]
|
||||
|
||||
@extend_schema(responses=inline_serializer("Workers", fields={"count": IntegerField()}))
|
||||
def get(self, request: Request) -> Response:
|
||||
"""Get currently connected worker count."""
|
||||
count = len(CELERY_APP.control.ping(timeout=0.5))
|
||||
# In debug we run with `task_always_eager`, so tasks are ran on the main process
|
||||
if settings.DEBUG: # pragma: no cover
|
||||
count += 1
|
||||
return Response({"count": count})
|
||||
@@ -1,13 +1,10 @@
|
||||
"""authentik admin app config"""
|
||||
|
||||
from prometheus_client import Info
|
||||
from prometheus_client import Gauge, Info
|
||||
|
||||
from authentik.blueprints.apps import ManagedAppConfig
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.utils.time import fqdn_rand
|
||||
from authentik.tasks.schedules.common import ScheduleSpec
|
||||
|
||||
PROM_INFO = Info("authentik_version", "Currently running authentik version")
|
||||
GAUGE_WORKERS = Gauge("authentik_admin_workers", "Currently connected workers")
|
||||
|
||||
|
||||
class AuthentikAdminConfig(ManagedAppConfig):
|
||||
@@ -18,30 +15,6 @@ class AuthentikAdminConfig(ManagedAppConfig):
|
||||
verbose_name = "authentik Admin"
|
||||
default = True
|
||||
|
||||
@ManagedAppConfig.reconcile_global
|
||||
def clear_update_notifications(self):
|
||||
"""Clear update notifications on startup if the notification was for the version
|
||||
we're running now."""
|
||||
from packaging.version import parse
|
||||
|
||||
from authentik.admin.tasks import LOCAL_VERSION
|
||||
from authentik.events.models import EventAction, Notification
|
||||
|
||||
for notification in Notification.objects.filter(event__action=EventAction.UPDATE_AVAILABLE):
|
||||
if "new_version" not in notification.event.context:
|
||||
continue
|
||||
notification_version = notification.event.context["new_version"]
|
||||
if LOCAL_VERSION >= parse(notification_version):
|
||||
notification.delete()
|
||||
|
||||
@property
|
||||
def global_schedule_specs(self) -> list[ScheduleSpec]:
|
||||
from authentik.admin.tasks import update_latest_version
|
||||
|
||||
return [
|
||||
ScheduleSpec(
|
||||
actor=update_latest_version,
|
||||
crontab=f"{fqdn_rand('admin_latest_version')} * * * *",
|
||||
paused=CONFIG.get_bool("disable_update_check"),
|
||||
),
|
||||
]
|
||||
def reconcile_load_admin_signals(self):
|
||||
"""Load admin signals"""
|
||||
self.import_module("authentik.admin.signals")
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import gettext as _
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from guardian.shortcuts import get_objects_for_user
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.fields import BooleanField, CharField, ChoiceField, FileField
|
||||
from rest_framework.parsers import MultiPartParser
|
||||
from rest_framework.permissions import SAFE_METHODS
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from authentik.admin.files.backends.base import get_content_type
|
||||
from authentik.admin.files.fields import FileField as AkFileField
|
||||
from authentik.admin.files.manager import get_file_manager
|
||||
from authentik.admin.files.usage import FileApiUsage
|
||||
from authentik.admin.files.validation import validate_upload_file_name
|
||||
from authentik.api.validation import validate
|
||||
from authentik.core.api.used_by import DeleteAction, UsedBySerializer
|
||||
from authentik.core.api.utils import PassiveSerializer, ThemedUrlsSerializer
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.lib.utils.reflection import get_apps
|
||||
from authentik.rbac.permissions import HasPermission
|
||||
|
||||
MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024 # 25MB
|
||||
|
||||
|
||||
class FileView(APIView):
|
||||
pagination_class = None
|
||||
parser_classes = [MultiPartParser]
|
||||
|
||||
def get_permissions(self):
|
||||
return [
|
||||
HasPermission(
|
||||
"authentik_rbac.view_media_files"
|
||||
if self.request.method in SAFE_METHODS
|
||||
else "authentik_rbac.manage_media_files"
|
||||
)()
|
||||
]
|
||||
|
||||
class FileListParameters(PassiveSerializer):
|
||||
usage = ChoiceField(choices=list(FileApiUsage), default=FileApiUsage.MEDIA.value)
|
||||
search = CharField(required=False)
|
||||
manageable_only = BooleanField(required=False, default=False)
|
||||
|
||||
class FileListSerializer(PassiveSerializer):
|
||||
name = CharField()
|
||||
mime_type = CharField()
|
||||
url = CharField()
|
||||
themed_urls = ThemedUrlsSerializer(required=False, allow_null=True)
|
||||
|
||||
@extend_schema(
|
||||
parameters=[FileListParameters],
|
||||
responses={200: FileListSerializer(many=True)},
|
||||
)
|
||||
@validate(FileListParameters, location="query")
|
||||
def get(self, request: Request, query: FileListParameters) -> Response:
|
||||
"""List files from storage backend."""
|
||||
params = query.validated_data
|
||||
|
||||
try:
|
||||
usage = FileApiUsage(params.get("usage", FileApiUsage.MEDIA.value))
|
||||
except ValueError as exc:
|
||||
raise ValidationError(
|
||||
f"Invalid usage parameter provided: {params.get('usage')}"
|
||||
) from exc
|
||||
|
||||
# Backend is source of truth - list all files from storage
|
||||
manager = get_file_manager(usage)
|
||||
files = manager.list_files(manageable_only=params.get("manageable_only", False))
|
||||
search_query = params.get("search", "")
|
||||
if search_query:
|
||||
files = filter(lambda file: search_query in file.lower(), files)
|
||||
files = [
|
||||
FileView.FileListSerializer(
|
||||
data={
|
||||
"name": file,
|
||||
"url": manager.file_url(file, request),
|
||||
"mime_type": get_content_type(file),
|
||||
"themed_urls": manager.themed_urls(file, request),
|
||||
}
|
||||
)
|
||||
for file in files
|
||||
]
|
||||
for file in files:
|
||||
file.is_valid(raise_exception=True)
|
||||
|
||||
return Response([file.data for file in files])
|
||||
|
||||
class FileUploadSerializer(PassiveSerializer):
|
||||
file = FileField(required=True)
|
||||
name = CharField(required=False, allow_blank=True)
|
||||
usage = CharField(required=False, default=FileApiUsage.MEDIA.value)
|
||||
|
||||
@extend_schema(
|
||||
request=FileUploadSerializer,
|
||||
responses={200: None},
|
||||
)
|
||||
@validate(FileUploadSerializer)
|
||||
def post(self, request: Request, body: FileUploadSerializer) -> Response:
|
||||
"""Upload file to storage backend."""
|
||||
file = body.validated_data["file"]
|
||||
name = body.validated_data.get("name", "").strip()
|
||||
usage_value = body.validated_data.get("usage", FileApiUsage.MEDIA.value)
|
||||
|
||||
# Validate file size and type
|
||||
if file.size > MAX_FILE_SIZE_BYTES:
|
||||
raise ValidationError(
|
||||
{
|
||||
"file": [
|
||||
_(
|
||||
f"File size ({file.size}B) exceeds maximum allowed "
|
||||
f"size ({MAX_FILE_SIZE_BYTES}B)."
|
||||
)
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
try:
|
||||
usage = FileApiUsage(usage_value)
|
||||
except ValueError as exc:
|
||||
raise ValidationError(f"Invalid usage parameter provided: {usage_value}") from exc
|
||||
|
||||
# Use original filename
|
||||
if not name:
|
||||
name = file.name
|
||||
|
||||
# Sanitize path to prevent directory traversal
|
||||
validate_upload_file_name(name, ValidationError)
|
||||
|
||||
manager = get_file_manager(usage)
|
||||
|
||||
# Check if file already exists
|
||||
if manager.file_exists(name):
|
||||
raise ValidationError({"name": ["A file with this name already exists."]})
|
||||
|
||||
# Save to backend
|
||||
with manager.save_file_stream(name) as f:
|
||||
f.write(file.read())
|
||||
|
||||
Event.new(
|
||||
EventAction.MODEL_CREATED,
|
||||
model={
|
||||
"app": "authentik_admin_files",
|
||||
"model_name": "File",
|
||||
"pk": name,
|
||||
"name": name,
|
||||
"usage": usage.value,
|
||||
"mime_type": get_content_type(name),
|
||||
},
|
||||
).from_http(request)
|
||||
|
||||
return Response()
|
||||
|
||||
class FileDeleteParameters(PassiveSerializer):
|
||||
name = CharField()
|
||||
usage = ChoiceField(choices=list(FileApiUsage), default=FileApiUsage.MEDIA.value)
|
||||
|
||||
@extend_schema(
|
||||
parameters=[FileDeleteParameters],
|
||||
responses={200: None},
|
||||
)
|
||||
@validate(FileDeleteParameters, location="query")
|
||||
def delete(self, request: Request, query: FileDeleteParameters) -> Response:
|
||||
"""Delete file from storage backend."""
|
||||
params = query.validated_data
|
||||
|
||||
validate_upload_file_name(params.get("name", ""), ValidationError)
|
||||
|
||||
try:
|
||||
usage = FileApiUsage(params.get("usage", FileApiUsage.MEDIA.value))
|
||||
except ValueError as exc:
|
||||
raise ValidationError(
|
||||
f"Invalid usage parameter provided: {params.get('usage')}"
|
||||
) from exc
|
||||
|
||||
manager = get_file_manager(usage)
|
||||
|
||||
# Delete from backend
|
||||
manager.delete_file(params.get("name"))
|
||||
|
||||
# Audit log for file deletion
|
||||
Event.new(
|
||||
EventAction.MODEL_DELETED,
|
||||
model={
|
||||
"app": "authentik_admin_files",
|
||||
"model_name": "File",
|
||||
"pk": params.get("name"),
|
||||
"name": params.get("name"),
|
||||
"usage": usage.value,
|
||||
},
|
||||
).from_http(request)
|
||||
|
||||
return Response()
|
||||
|
||||
|
||||
class FileUsedByView(APIView):
|
||||
pagination_class = None
|
||||
|
||||
def get_permissions(self):
|
||||
return [
|
||||
HasPermission(
|
||||
"authentik_rbac.view_media_files"
|
||||
if self.request.method in SAFE_METHODS
|
||||
else "authentik_rbac.manage_media_files"
|
||||
)()
|
||||
]
|
||||
|
||||
class FileUsedByParameters(PassiveSerializer):
|
||||
name = CharField()
|
||||
|
||||
@extend_schema(
|
||||
parameters=[FileUsedByParameters],
|
||||
responses={200: UsedBySerializer(many=True)},
|
||||
)
|
||||
@validate(FileUsedByParameters, location="query")
|
||||
def get(self, request: Request, query: FileUsedByParameters) -> Response:
|
||||
params = query.validated_data
|
||||
|
||||
models_and_fields = {}
|
||||
for app in get_apps():
|
||||
for model in app.get_models():
|
||||
if model._meta.abstract:
|
||||
continue
|
||||
for field in model._meta.get_fields():
|
||||
if isinstance(field, AkFileField):
|
||||
models_and_fields.setdefault(model, []).append(field.name)
|
||||
|
||||
used_by = []
|
||||
|
||||
for model, fields in models_and_fields.items():
|
||||
app = model._meta.app_label
|
||||
model_name = model._meta.model_name
|
||||
|
||||
q = Q()
|
||||
for field in fields:
|
||||
q |= Q(**{field: params.get("name")})
|
||||
|
||||
objs = get_objects_for_user(
|
||||
request.user, f"{app}.view_{model_name}", model.objects.all()
|
||||
)
|
||||
objs = objs.filter(q)
|
||||
for obj in objs:
|
||||
serializer = UsedBySerializer(
|
||||
data={
|
||||
"app": model._meta.app_label,
|
||||
"model_name": model._meta.model_name,
|
||||
"pk": str(obj.pk),
|
||||
"name": str(obj),
|
||||
"action": DeleteAction.LEFT_DANGLING,
|
||||
}
|
||||
)
|
||||
serializer.is_valid()
|
||||
used_by.append(serializer.data)
|
||||
|
||||
return Response(used_by)
|
||||
@@ -1,8 +0,0 @@
|
||||
from authentik.blueprints.apps import ManagedAppConfig
|
||||
|
||||
|
||||
class AuthentikFilesConfig(ManagedAppConfig):
|
||||
name = "authentik.admin.files"
|
||||
label = "authentik_admin_files"
|
||||
verbose_name = "authentik Files"
|
||||
default = True
|
||||
@@ -1,213 +0,0 @@
|
||||
import mimetypes
|
||||
from collections.abc import Callable, Generator, Iterator
|
||||
from typing import cast
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.http.request import HttpRequest
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.admin.files.usage import FileUsage
|
||||
|
||||
CACHE_PREFIX = "goauthentik.io/admin/files"
|
||||
LOGGER = get_logger()
|
||||
|
||||
# Theme variable placeholder for theme-specific files like logo-%(theme)s.png
|
||||
THEME_VARIABLE = "%(theme)s"
|
||||
|
||||
|
||||
def get_content_type(name: str) -> str:
|
||||
"""Get MIME type for a file based on its extension."""
|
||||
content_type, _ = mimetypes.guess_type(name)
|
||||
return content_type or "application/octet-stream"
|
||||
|
||||
|
||||
def get_valid_themes() -> list[str]:
|
||||
"""Get valid themes that can be substituted for %(theme)s."""
|
||||
from authentik.brands.api import Themes
|
||||
|
||||
return [t.value for t in Themes if t != Themes.AUTOMATIC]
|
||||
|
||||
|
||||
def has_theme_variable(name: str) -> bool:
|
||||
"""Check if filename contains %(theme)s variable."""
|
||||
return THEME_VARIABLE in name
|
||||
|
||||
|
||||
def substitute_theme(name: str, theme: str) -> str:
|
||||
"""Replace %(theme)s with the given theme."""
|
||||
return name.replace(THEME_VARIABLE, theme)
|
||||
|
||||
|
||||
class Backend:
|
||||
"""
|
||||
Base class for file storage backends.
|
||||
|
||||
Class attributes:
|
||||
allowed_usages: List of usages that can be used with this backend
|
||||
"""
|
||||
|
||||
allowed_usages: list[FileUsage]
|
||||
|
||||
def __init__(self, usage: FileUsage):
|
||||
"""
|
||||
Initialize backend for the given usage type.
|
||||
|
||||
Args:
|
||||
usage: FileUsage type enum value
|
||||
"""
|
||||
self.usage = usage
|
||||
LOGGER.debug(
|
||||
"Initializing storage backend",
|
||||
backend=self.__class__.__name__,
|
||||
usage=usage.value,
|
||||
)
|
||||
|
||||
def supports_file(self, name: str) -> bool:
|
||||
"""
|
||||
Check if this backend can handle the given file path.
|
||||
|
||||
Args:
|
||||
name: File path to check
|
||||
|
||||
Returns:
|
||||
True if this backend supports this file path
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def list_files(self) -> Generator[str]:
|
||||
"""
|
||||
List all files stored in this backend.
|
||||
|
||||
Yields:
|
||||
Relative file paths
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def file_url(
|
||||
self,
|
||||
name: str,
|
||||
request: HttpRequest | None = None,
|
||||
use_cache: bool = True,
|
||||
) -> str:
|
||||
"""
|
||||
Get URL for accessing the file.
|
||||
|
||||
Args:
|
||||
file_path: Relative file path
|
||||
request: Optional Django HttpRequest for fully qualified URL building
|
||||
use_cache: whether to retrieve the URL from cache
|
||||
|
||||
Returns:
|
||||
URL to access the file (may be relative or absolute depending on backend)
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def themed_urls(
|
||||
self,
|
||||
name: str,
|
||||
request: HttpRequest | None = None,
|
||||
use_cache: bool = True,
|
||||
) -> dict[str, str] | None:
|
||||
"""
|
||||
Get URLs for each theme variant when filename contains %(theme)s.
|
||||
|
||||
Args:
|
||||
name: File path potentially containing %(theme)s
|
||||
request: Optional Django HttpRequest for URL building
|
||||
|
||||
Returns:
|
||||
Dict mapping theme to URL if %(theme)s present, None otherwise
|
||||
"""
|
||||
if not has_theme_variable(name):
|
||||
return None
|
||||
|
||||
return {
|
||||
theme: self.file_url(substitute_theme(name, theme), request, use_cache=use_cache)
|
||||
for theme in get_valid_themes()
|
||||
}
|
||||
|
||||
|
||||
class ManageableBackend(Backend):
|
||||
"""
|
||||
Base class for manageable file storage backends.
|
||||
|
||||
Class attributes:
|
||||
name: Canonical name of the storage backend, for use in configuration.
|
||||
"""
|
||||
|
||||
name: str
|
||||
|
||||
@property
|
||||
def manageable(self) -> bool:
|
||||
"""
|
||||
Whether this backend can actually be used for management.
|
||||
|
||||
Used only for management check, not for created the backend
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def save_file(self, name: str, content: bytes) -> None:
|
||||
"""
|
||||
Save file content to storage.
|
||||
|
||||
Args:
|
||||
file_path: Relative file path
|
||||
content: File content as bytes
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def save_file_stream(self, name: str) -> Iterator:
|
||||
"""
|
||||
Context manager for streaming file writes.
|
||||
|
||||
Args:
|
||||
file_path: Relative file path
|
||||
|
||||
Returns:
|
||||
Context manager that yields a writable file-like object
|
||||
|
||||
FileUsage:
|
||||
with backend.save_file_stream("output.csv") as f:
|
||||
f.write(b"data...")
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def delete_file(self, name: str) -> None:
|
||||
"""
|
||||
Delete file from storage.
|
||||
|
||||
Args:
|
||||
file_path: Relative file path
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def file_exists(self, name: str) -> bool:
|
||||
"""
|
||||
Check if a file exists.
|
||||
|
||||
Args:
|
||||
file_path: Relative file path
|
||||
|
||||
Returns:
|
||||
True if file exists, False otherwise
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _cache_get_or_set(
|
||||
self,
|
||||
name: str,
|
||||
request: HttpRequest | None,
|
||||
default: Callable[[str, HttpRequest | None], str],
|
||||
timeout: int,
|
||||
) -> str:
|
||||
timeout_ignore = 60
|
||||
timeout = int(timeout * 0.67)
|
||||
if timeout < timeout_ignore:
|
||||
timeout = 0
|
||||
|
||||
request_key = "None"
|
||||
if request is not None:
|
||||
request_key = f"{request.build_absolute_uri('/')}"
|
||||
cache_key = f"{CACHE_PREFIX}/{self.name}/{self.usage}/{request_key}/{name}"
|
||||
|
||||
return cast(str, cache.get_or_set(cache_key, lambda: default(name, request), timeout))
|
||||
@@ -1,131 +0,0 @@
|
||||
import os
|
||||
from collections.abc import Generator, Iterator
|
||||
from contextlib import contextmanager
|
||||
from datetime import timedelta
|
||||
from hashlib import sha256
|
||||
from pathlib import Path
|
||||
|
||||
import jwt
|
||||
from django.conf import settings
|
||||
from django.db import connection
|
||||
from django.http.request import HttpRequest
|
||||
from django.utils.timezone import now
|
||||
|
||||
from authentik.admin.files.backends.base import ManageableBackend
|
||||
from authentik.admin.files.usage import FileUsage
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
|
||||
|
||||
class FileBackend(ManageableBackend):
|
||||
"""Local filesystem backend for file storage.
|
||||
|
||||
Stores files in a local directory structure:
|
||||
- Path: {base_dir}/{usage}/{schema}/{filename}
|
||||
- Supports full file management (upload, delete, list)
|
||||
- Used when storage.backend=file (default)
|
||||
"""
|
||||
|
||||
name = "file"
|
||||
allowed_usages = list(FileUsage) # All usages
|
||||
|
||||
@property
|
||||
def _base_dir(self) -> Path:
|
||||
return Path(
|
||||
CONFIG.get(
|
||||
f"storage.{self.usage.value}.{self.name}.path",
|
||||
CONFIG.get(f"storage.{self.name}.path", "./data"),
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
def base_path(self) -> Path:
|
||||
"""Path structure: {base_dir}/{usage}/{schema}"""
|
||||
return self._base_dir / self.usage.value / connection.schema_name
|
||||
|
||||
@property
|
||||
def manageable(self) -> bool:
|
||||
# Check _base_dir (the mount point, e.g. /data) rather than base_path
|
||||
# (which includes usage/schema subdirs, e.g. /data/media/public).
|
||||
# The subdirectories are created on first file write via mkdir(parents=True)
|
||||
# in save_file(), so requiring them to exist beforehand would prevent
|
||||
# file creation on fresh installs.
|
||||
return (
|
||||
self._base_dir.exists()
|
||||
and (self._base_dir.is_mount() or (self._base_dir / self.usage.value).is_mount())
|
||||
or (settings.DEBUG or settings.TEST)
|
||||
)
|
||||
|
||||
def supports_file(self, name: str) -> bool:
|
||||
"""We support all files"""
|
||||
return True
|
||||
|
||||
def list_files(self) -> Generator[str]:
|
||||
"""List all files returning relative paths from base_path."""
|
||||
for root, _, files in os.walk(self.base_path):
|
||||
for file in files:
|
||||
full_path = Path(root) / file
|
||||
rel_path = full_path.relative_to(self.base_path)
|
||||
yield str(rel_path)
|
||||
|
||||
def file_url(
|
||||
self,
|
||||
name: str,
|
||||
request: HttpRequest | None = None,
|
||||
use_cache: bool = True,
|
||||
) -> str:
|
||||
"""Get URL for accessing the file."""
|
||||
expires_in = timedelta_from_string(
|
||||
CONFIG.get(
|
||||
f"storage.{self.usage.value}.{self.name}.url_expiry",
|
||||
CONFIG.get(f"storage.{self.name}.url_expiry", "minutes=15"),
|
||||
)
|
||||
)
|
||||
|
||||
def _file_url(name: str, request: HttpRequest | None) -> str:
|
||||
prefix = CONFIG.get("web.path", "/")[:-1]
|
||||
path = f"{self.usage.value}/{connection.schema_name}/{name}"
|
||||
token = jwt.encode(
|
||||
payload={
|
||||
"path": path,
|
||||
"exp": now() + expires_in,
|
||||
"nbf": now() - timedelta(seconds=15),
|
||||
},
|
||||
key=sha256(f"{settings.SECRET_KEY}:{self.usage}".encode()).hexdigest(),
|
||||
algorithm="HS256",
|
||||
)
|
||||
url = f"{prefix}/files/{path}?token={token}"
|
||||
if request is None:
|
||||
return url
|
||||
return request.build_absolute_uri(url)
|
||||
|
||||
if use_cache:
|
||||
timeout = int(expires_in.total_seconds())
|
||||
return self._cache_get_or_set(name, request, _file_url, timeout)
|
||||
else:
|
||||
return _file_url(name, request)
|
||||
|
||||
def save_file(self, name: str, content: bytes) -> None:
|
||||
"""Save file to local filesystem."""
|
||||
path = self.base_path / Path(name)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(path, "w+b") as f:
|
||||
f.write(content)
|
||||
|
||||
@contextmanager
|
||||
def save_file_stream(self, name: str) -> Iterator:
|
||||
"""Context manager for streaming file writes to local filesystem."""
|
||||
path = self.base_path / Path(name)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(path, "wb") as f:
|
||||
yield f
|
||||
|
||||
def delete_file(self, name: str) -> None:
|
||||
"""Delete file from local filesystem."""
|
||||
path = self.base_path / Path(name)
|
||||
path.unlink(missing_ok=True)
|
||||
|
||||
def file_exists(self, name: str) -> bool:
|
||||
"""Check if a file exists."""
|
||||
path = self.base_path / Path(name)
|
||||
return path.exists()
|
||||
@@ -1,71 +0,0 @@
|
||||
from collections.abc import Generator
|
||||
|
||||
from django.http.request import HttpRequest
|
||||
|
||||
from authentik.admin.files.backends.base import Backend
|
||||
from authentik.admin.files.usage import FileUsage
|
||||
|
||||
EXTERNAL_URL_SCHEMES = ["http:", "https://"]
|
||||
FONT_AWESOME_SCHEME = "fa://"
|
||||
|
||||
|
||||
class PassthroughBackend(Backend):
|
||||
"""Passthrough backend for external URLs and special schemes.
|
||||
|
||||
Handles external resources that aren't stored in authentik:
|
||||
- Font Awesome icons (fa://...)
|
||||
- HTTP/HTTPS URLs (http://..., https://...)
|
||||
|
||||
Files that are "managed" by this backend are just passed through as-is.
|
||||
No upload, delete, or listing operations are supported.
|
||||
Only accessible through resolve_file_url when an external URL is detected.
|
||||
"""
|
||||
|
||||
allowed_usages = [FileUsage.MEDIA]
|
||||
|
||||
def supports_file(self, name: str) -> bool:
|
||||
"""Check if file path is an external URL or Font Awesome icon."""
|
||||
if name.startswith(FONT_AWESOME_SCHEME):
|
||||
return True
|
||||
|
||||
for scheme in EXTERNAL_URL_SCHEMES:
|
||||
if name.startswith(scheme):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def list_files(self) -> Generator[str]:
|
||||
"""External files cannot be listed."""
|
||||
yield from []
|
||||
|
||||
def file_url(
|
||||
self,
|
||||
name: str,
|
||||
request: HttpRequest | None = None,
|
||||
use_cache: bool = True,
|
||||
) -> str:
|
||||
"""Return the URL as-is for passthrough files."""
|
||||
return name
|
||||
|
||||
def themed_urls(
|
||||
self,
|
||||
name: str,
|
||||
request: HttpRequest | None = None,
|
||||
use_cache: bool = True,
|
||||
) -> dict[str, str] | None:
|
||||
"""Support themed URLs for external URLs with %(theme)s placeholder.
|
||||
|
||||
If the external URL contains %(theme)s, substitute it for each theme.
|
||||
We can't verify that themed variants exist at the external location,
|
||||
but we trust the user to provide valid URLs.
|
||||
"""
|
||||
from authentik.admin.files.backends.base import (
|
||||
get_valid_themes,
|
||||
has_theme_variable,
|
||||
substitute_theme,
|
||||
)
|
||||
|
||||
if not has_theme_variable(name):
|
||||
return None
|
||||
|
||||
return {theme: substitute_theme(name, theme) for theme in get_valid_themes()}
|
||||
@@ -1,270 +0,0 @@
|
||||
from collections.abc import Generator, Iterator
|
||||
from contextlib import contextmanager
|
||||
from tempfile import SpooledTemporaryFile
|
||||
from urllib.parse import urlsplit, urlunsplit
|
||||
|
||||
import boto3
|
||||
from botocore.config import Config
|
||||
from botocore.exceptions import ClientError
|
||||
from django.db import connection
|
||||
from django.http.request import HttpRequest
|
||||
|
||||
from authentik.admin.files.backends.base import ManageableBackend, get_content_type
|
||||
from authentik.admin.files.usage import FileUsage
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
|
||||
|
||||
class S3Backend(ManageableBackend):
|
||||
"""S3-compatible object storage backend.
|
||||
|
||||
Stores files in s3-compatible storage:
|
||||
- Key prefix: {usage}/{schema}/{filename}
|
||||
- Supports full file management (upload, delete, list)
|
||||
- Generates presigned URLs for file access
|
||||
- Used when storage.backend=s3
|
||||
"""
|
||||
|
||||
allowed_usages = list(FileUsage) # All usages
|
||||
name = "s3"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._config = {}
|
||||
self._session = None
|
||||
|
||||
def _get_config(self, key: str, default: str | None) -> tuple[str | None, bool]:
|
||||
unset = object()
|
||||
current = self._config.get(key, unset)
|
||||
refreshed = CONFIG.refresh(
|
||||
f"storage.{self.usage.value}.{self.name}.{key}",
|
||||
CONFIG.refresh(f"storage.{self.name}.{key}", default),
|
||||
)
|
||||
if current is unset:
|
||||
current = refreshed
|
||||
self._config[key] = refreshed
|
||||
return (refreshed, current != refreshed)
|
||||
|
||||
@property
|
||||
def base_path(self) -> str:
|
||||
"""S3 key prefix: {usage}/{schema}/"""
|
||||
return f"{self.usage.value}/{connection.schema_name}"
|
||||
|
||||
@property
|
||||
def bucket_name(self) -> str:
|
||||
return CONFIG.get(
|
||||
f"storage.{self.usage.value}.{self.name}.bucket_name",
|
||||
CONFIG.get(f"storage.{self.name}.bucket_name"),
|
||||
)
|
||||
|
||||
@property
|
||||
def session(self) -> boto3.Session:
|
||||
"""Create boto3 session with configured credentials."""
|
||||
session_profile, session_profile_r = self._get_config("session_profile", None)
|
||||
if session_profile is not None:
|
||||
if session_profile_r or self._session is None:
|
||||
self._session = boto3.Session(profile_name=session_profile)
|
||||
return self._session
|
||||
else:
|
||||
return self._session
|
||||
else:
|
||||
access_key, access_key_r = self._get_config("access_key", None)
|
||||
secret_key, secret_key_r = self._get_config("secret_key", None)
|
||||
session_token, session_token_r = self._get_config("session_token", None)
|
||||
if access_key_r or secret_key_r or session_token_r or self._session is None:
|
||||
self._session = boto3.Session(
|
||||
aws_access_key_id=access_key,
|
||||
aws_secret_access_key=secret_key,
|
||||
aws_session_token=session_token,
|
||||
)
|
||||
return self._session
|
||||
else:
|
||||
return self._session
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
"""Create S3 client with configured endpoint and region."""
|
||||
endpoint_url = CONFIG.get(
|
||||
f"storage.{self.usage.value}.{self.name}.endpoint",
|
||||
CONFIG.get(f"storage.{self.name}.endpoint", None),
|
||||
)
|
||||
use_ssl = CONFIG.get(
|
||||
f"storage.{self.usage.value}.{self.name}.use_ssl",
|
||||
CONFIG.get(f"storage.{self.name}.use_ssl", True),
|
||||
)
|
||||
region_name = CONFIG.get(
|
||||
f"storage.{self.usage.value}.{self.name}.region",
|
||||
CONFIG.get(f"storage.{self.name}.region", None),
|
||||
)
|
||||
addressing_style = CONFIG.get(
|
||||
f"storage.{self.usage.value}.{self.name}.addressing_style",
|
||||
CONFIG.get(f"storage.{self.name}.addressing_style", "auto"),
|
||||
)
|
||||
signature_version = CONFIG.get(
|
||||
f"storage.{self.usage.value}.{self.name}.signature_version",
|
||||
CONFIG.get(f"storage.{self.name}.signature_version", "s3v4"),
|
||||
)
|
||||
# Keep signature_version pass-through and let boto3/botocore handle it.
|
||||
# In boto3's S3 configuration docs, `s3v4` (default) and deprecated `s3`
|
||||
# are the documented values:
|
||||
# https://github.com/boto/boto3/blob/791a3e8f36d83664a47b4281a0586b3546cef3ec/docs/source/guide/configuration.rst?plain=1#L398-L407
|
||||
# Botocore also supports additional signer names, so we intentionally do
|
||||
# not enforce a restricted allowlist here.
|
||||
|
||||
return self.session.client(
|
||||
"s3",
|
||||
endpoint_url=endpoint_url,
|
||||
use_ssl=use_ssl,
|
||||
region_name=region_name,
|
||||
config=Config(
|
||||
signature_version=signature_version, s3={"addressing_style": addressing_style}
|
||||
),
|
||||
)
|
||||
|
||||
@property
|
||||
def manageable(self) -> bool:
|
||||
return True
|
||||
|
||||
def supports_file(self, name: str) -> bool:
|
||||
"""We support all files"""
|
||||
return True
|
||||
|
||||
def list_files(self) -> Generator[str]:
|
||||
"""List all files returning relative paths from base_path."""
|
||||
paginator = self.client.get_paginator("list_objects_v2")
|
||||
pages = paginator.paginate(Bucket=self.bucket_name, Prefix=f"{self.base_path}/")
|
||||
|
||||
for page in pages:
|
||||
for obj in page.get("Contents", []):
|
||||
key = obj["Key"]
|
||||
# Remove base path prefix to get relative path
|
||||
rel_path = key.removeprefix(f"{self.base_path}/")
|
||||
if rel_path: # Skip if it's just the directory itself
|
||||
yield rel_path
|
||||
|
||||
def file_url(
|
||||
self,
|
||||
name: str,
|
||||
request: HttpRequest | None = None,
|
||||
use_cache: bool = True,
|
||||
) -> str:
|
||||
"""Generate presigned URL for file access."""
|
||||
use_https = CONFIG.get_bool(
|
||||
f"storage.{self.usage.value}.{self.name}.secure_urls",
|
||||
CONFIG.get_bool(f"storage.{self.name}.secure_urls", True),
|
||||
)
|
||||
|
||||
expires_in = int(
|
||||
timedelta_from_string(
|
||||
CONFIG.get(
|
||||
f"storage.{self.usage.value}.{self.name}.url_expiry",
|
||||
CONFIG.get(f"storage.{self.name}.url_expiry", "minutes=15"),
|
||||
)
|
||||
).total_seconds()
|
||||
)
|
||||
|
||||
def _file_url(name: str, request: HttpRequest | None) -> str:
|
||||
client = self.client
|
||||
params = {
|
||||
"Bucket": self.bucket_name,
|
||||
"Key": f"{self.base_path}/{name}",
|
||||
}
|
||||
|
||||
operation_name = "GetObject"
|
||||
operation_model = client.meta.service_model.operation_model(operation_name)
|
||||
request_dict = client._convert_to_request_dict(
|
||||
params,
|
||||
operation_model,
|
||||
endpoint_url=client.meta.endpoint_url,
|
||||
context={"is_presign_request": True},
|
||||
)
|
||||
|
||||
# Support custom domain for S3-compatible storage (so not AWS)
|
||||
# Well, can't you do custom domains on AWS as well?
|
||||
custom_domain = CONFIG.get(
|
||||
f"storage.{self.usage.value}.{self.name}.custom_domain",
|
||||
CONFIG.get(f"storage.{self.name}.custom_domain", None),
|
||||
)
|
||||
if custom_domain:
|
||||
scheme = "https" if use_https else "http"
|
||||
path = request_dict["url_path"]
|
||||
|
||||
# When using path-style addressing, the presigned URL contains the bucket
|
||||
# name in the path (e.g., /bucket-name/key). Since custom_domain must
|
||||
# include the bucket name (per docs), strip it from the path to avoid
|
||||
# duplication. See: https://github.com/goauthentik/authentik/issues/19521
|
||||
# Check with trailing slash to ensure exact bucket name match
|
||||
if path.startswith(f"/{self.bucket_name}/"):
|
||||
path = path.removeprefix(f"/{self.bucket_name}")
|
||||
|
||||
# Normalize to avoid double slashes
|
||||
custom_domain = custom_domain.rstrip("/")
|
||||
if not path.startswith("/"):
|
||||
path = f"/{path}"
|
||||
|
||||
custom_base = urlsplit(f"{scheme}://{custom_domain}")
|
||||
|
||||
# Sign the final public URL instead of signing the internal S3 endpoint and
|
||||
# rewriting it afterwards. Presigned SigV4 URLs include the host header in the
|
||||
# canonical request, so post-sign host changes break strict backends like RustFS.
|
||||
public_path = f"{custom_base.path.rstrip('/')}{path}" if custom_base.path else path
|
||||
request_dict["url_path"] = public_path
|
||||
request_dict["url"] = urlunsplit(
|
||||
(custom_base.scheme, custom_base.netloc, public_path, "", "")
|
||||
)
|
||||
|
||||
return client._request_signer.generate_presigned_url(
|
||||
request_dict,
|
||||
operation_name,
|
||||
expires_in=expires_in,
|
||||
)
|
||||
|
||||
if use_cache:
|
||||
return self._cache_get_or_set(name, request, _file_url, expires_in)
|
||||
else:
|
||||
return _file_url(name, request)
|
||||
|
||||
def save_file(self, name: str, content: bytes) -> None:
|
||||
"""Save file to S3."""
|
||||
self.client.put_object(
|
||||
Bucket=self.bucket_name,
|
||||
Key=f"{self.base_path}/{name}",
|
||||
Body=content,
|
||||
ACL="private",
|
||||
ContentType=get_content_type(name),
|
||||
)
|
||||
|
||||
@contextmanager
|
||||
def save_file_stream(self, name: str) -> Iterator:
|
||||
"""Context manager for streaming file writes to S3."""
|
||||
# Keep files in memory up to 5 MB
|
||||
with SpooledTemporaryFile(max_size=5 * 1024 * 1024, suffix=".S3File") as file:
|
||||
yield file
|
||||
file.seek(0)
|
||||
self.client.upload_fileobj(
|
||||
Fileobj=file,
|
||||
Bucket=self.bucket_name,
|
||||
Key=f"{self.base_path}/{name}",
|
||||
ExtraArgs={
|
||||
"ACL": "private",
|
||||
"ContentType": get_content_type(name),
|
||||
},
|
||||
)
|
||||
|
||||
def delete_file(self, name: str) -> None:
|
||||
"""Delete file from S3."""
|
||||
self.client.delete_object(
|
||||
Bucket=self.bucket_name,
|
||||
Key=f"{self.base_path}/{name}",
|
||||
)
|
||||
|
||||
def file_exists(self, name: str) -> bool:
|
||||
"""Check if a file exists in S3."""
|
||||
try:
|
||||
self.client.head_object(
|
||||
Bucket=self.bucket_name,
|
||||
Key=f"{self.base_path}/{name}",
|
||||
)
|
||||
return True
|
||||
except ClientError:
|
||||
return False
|
||||
@@ -1,58 +0,0 @@
|
||||
from collections.abc import Generator
|
||||
from pathlib import Path
|
||||
|
||||
from django.http.request import HttpRequest
|
||||
|
||||
from authentik.admin.files.backends.base import Backend
|
||||
from authentik.admin.files.usage import FileUsage
|
||||
from authentik.lib.config import CONFIG
|
||||
|
||||
STATIC_ASSETS_BASE_DIR = Path("web/dist")
|
||||
STATIC_ASSETS_DIRS = [Path(p) for p in ("assets/icons", "assets/images")]
|
||||
STATIC_ASSETS_SOURCES_DIR = Path("web/authentik/sources")
|
||||
STATIC_FILE_EXTENSIONS = [".svg", ".png", ".jpg", ".jpeg"]
|
||||
STATIC_PATH_PREFIX = "/static"
|
||||
|
||||
|
||||
class StaticBackend(Backend):
|
||||
"""Read-only backend for static files from web/dist/assets.
|
||||
|
||||
- Used for serving built-in static assets like icons and images.
|
||||
- Files cannot be uploaded or deleted through this backend.
|
||||
- Only accessible through resolve_file_url when a static path is detected.
|
||||
"""
|
||||
|
||||
allowed_usages = [FileUsage.MEDIA]
|
||||
|
||||
def supports_file(self, name: str) -> bool:
|
||||
"""Check if file path is a static path."""
|
||||
return name.startswith(STATIC_PATH_PREFIX)
|
||||
|
||||
def list_files(self) -> Generator[str]:
|
||||
"""List all static files."""
|
||||
# List built-in source icons
|
||||
if STATIC_ASSETS_SOURCES_DIR.exists():
|
||||
for file_path in STATIC_ASSETS_SOURCES_DIR.iterdir():
|
||||
if file_path.is_file() and (file_path.suffix in STATIC_FILE_EXTENSIONS):
|
||||
yield f"{STATIC_PATH_PREFIX}/authentik/sources/{file_path.name}"
|
||||
|
||||
# List other static assets
|
||||
for dir in STATIC_ASSETS_DIRS:
|
||||
dist_dir = STATIC_ASSETS_BASE_DIR / dir
|
||||
if dist_dir.exists():
|
||||
for file_path in dist_dir.rglob("*"):
|
||||
if file_path.is_file() and (file_path.suffix in STATIC_FILE_EXTENSIONS):
|
||||
yield f"{STATIC_PATH_PREFIX}/dist/{dir}/{file_path.name}"
|
||||
|
||||
def file_url(
|
||||
self,
|
||||
name: str,
|
||||
request: HttpRequest | None = None,
|
||||
use_cache: bool = True,
|
||||
) -> str:
|
||||
"""Get URL for static file."""
|
||||
prefix = CONFIG.get("web.path", "/")[:-1]
|
||||
url = f"{prefix}{name}"
|
||||
if request is None:
|
||||
return url
|
||||
return request.build_absolute_uri(url)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user