Compare commits
6 Commits
fix/issue_
...
typescript
| Author | SHA1 | Date | |
|---|---|---|---|
| ff3e59c8e2 | |||
| 0e57e06191 | |||
| 40ab2bccfd | |||
| efc0697483 | |||
| 9c150d74c4 | |||
| d421b25760 |
@ -10,6 +10,9 @@ insert_final_newline = true
|
||||
[*.html]
|
||||
indent_size = 2
|
||||
|
||||
[schemas/*.json]
|
||||
indent_size = 2
|
||||
|
||||
[*.{yaml,yml}]
|
||||
indent_size = 2
|
||||
|
||||
|
||||
22
.github/ISSUE_TEMPLATE/docs_issue.md
vendored
22
.github/ISSUE_TEMPLATE/docs_issue.md
vendored
@ -1,22 +0,0 @@
|
||||
---
|
||||
name: Documentation issue
|
||||
about: Suggest an improvement or report a problem
|
||||
title: ""
|
||||
labels: documentation
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
**Do you see an area that can be clarified or expanded, a technical inaccuracy, or a broken link? Please describe.**
|
||||
A clear and concise description of what the problem is, or where the document can be improved. Ex. I believe we need more details about [...]
|
||||
|
||||
**Provide the URL or link to the exact page in the documentation to which you are referring.**
|
||||
If there are multiple pages, list them all, and be sure to state the header or section where the content is.
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the documentation issue here.
|
||||
|
||||
**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).
|
||||
@ -44,6 +44,7 @@ if is_release:
|
||||
]
|
||||
if not prerelease:
|
||||
image_tags += [
|
||||
f"{name}:latest",
|
||||
f"{name}:{version_family}",
|
||||
]
|
||||
else:
|
||||
|
||||
2
.github/workflows/ci-outpost.yml
vendored
2
.github/workflows/ci-outpost.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
||||
- name: Generate API
|
||||
run: make gen-client-go
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v7
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout 5000s --verbose
|
||||
|
||||
23
.gitignore
vendored
23
.gitignore
vendored
@ -33,6 +33,7 @@ eggs/
|
||||
lib64/
|
||||
parts/
|
||||
dist/
|
||||
out/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
@ -212,3 +213,25 @@ source_docs/
|
||||
|
||||
### Docker ###
|
||||
docker-compose.override.yml
|
||||
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules/
|
||||
|
||||
|
||||
# Wireit's cache
|
||||
.wireit
|
||||
|
||||
custom-elements.json
|
||||
|
||||
|
||||
### Development ###
|
||||
.drafts
|
||||
|
||||
47
.prettierignore
Normal file
47
.prettierignore
Normal file
@ -0,0 +1,47 @@
|
||||
# Prettier Ignorefile
|
||||
|
||||
## Static Files
|
||||
**/LICENSE
|
||||
|
||||
authentik/stages/**/*
|
||||
|
||||
## Build asset directories
|
||||
coverage
|
||||
dist
|
||||
out
|
||||
.docusaurus
|
||||
website/docs/developer-docs/api/**/*
|
||||
|
||||
## Environment
|
||||
*.env
|
||||
|
||||
## Secrets
|
||||
*.secrets
|
||||
|
||||
## Yarn
|
||||
.yarn/**/*
|
||||
|
||||
## Node
|
||||
node_modules
|
||||
coverage
|
||||
|
||||
## 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*
|
||||
|
||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@ -17,6 +17,6 @@
|
||||
"ms-python.vscode-pylance",
|
||||
"redhat.vscode-yaml",
|
||||
"Tobermory.es6-string-html",
|
||||
"unifiedjs.vscode-mdx",
|
||||
"unifiedjs.vscode-mdx"
|
||||
]
|
||||
}
|
||||
|
||||
57
.vscode/settings.json
vendored
57
.vscode/settings.json
vendored
@ -16,7 +16,7 @@
|
||||
],
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"typescript.preferences.importModuleSpecifierEnding": "index",
|
||||
"typescript.tsdk": "./web/node_modules/typescript/lib",
|
||||
"typescript.tsdk": "./node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"yaml.schemas": {
|
||||
"./blueprints/schema.json": "blueprints/**/*.yaml"
|
||||
@ -30,7 +30,56 @@
|
||||
}
|
||||
],
|
||||
"go.testFlags": ["-count=1"],
|
||||
"github-actions.workflows.pinned.workflows": [
|
||||
".github/workflows/ci-main.yml"
|
||||
]
|
||||
"github-actions.workflows.pinned.workflows": [".github/workflows/ci-main.yml"],
|
||||
|
||||
"eslint.useFlatConfig": true,
|
||||
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"*.cjs": "*.d.cts",
|
||||
"package.json": "package-lock.json, yarn.lock, .yarnrc, .yarnrc.yml, .yarn, .nvmrc, .node-version",
|
||||
"tsconfig.json": "tsconfig.*.json, jsconfig.json"
|
||||
},
|
||||
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/*.code-search": true,
|
||||
"**/dist": true,
|
||||
"**/out": true,
|
||||
"**/package-lock.json": true
|
||||
},
|
||||
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[shellscript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.removeUnusedImports": "explicit"
|
||||
},
|
||||
// We use Prettier for formatting, but specifying these settings
|
||||
// will ensure that VS Code's IntelliSense doesn't autocomplete unformatted code.
|
||||
"javascript.format.semicolons": "insert",
|
||||
"typescript.format.semicolons": "insert",
|
||||
"javascript.preferences.quoteStyle": "double",
|
||||
"typescript.preferences.quoteStyle": "double"
|
||||
}
|
||||
|
||||
40
.vscode/tasks.json
vendored
40
.vscode/tasks.json
vendored
@ -4,12 +4,7 @@
|
||||
{
|
||||
"label": "authentik/core: make",
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"make",
|
||||
"lint-fix",
|
||||
"lint"
|
||||
],
|
||||
"args": ["run", "make", "lint-fix", "lint"],
|
||||
"presentation": {
|
||||
"panel": "new"
|
||||
},
|
||||
@ -18,11 +13,7 @@
|
||||
{
|
||||
"label": "authentik/core: run",
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"ak",
|
||||
"server"
|
||||
],
|
||||
"args": ["run", "ak", "server"],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
@ -32,17 +23,13 @@
|
||||
{
|
||||
"label": "authentik/web: make",
|
||||
"command": "make",
|
||||
"args": [
|
||||
"web"
|
||||
],
|
||||
"args": ["web"],
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "authentik/web: watch",
|
||||
"command": "make",
|
||||
"args": [
|
||||
"web-watch"
|
||||
],
|
||||
"args": ["web-watch"],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
@ -52,26 +39,19 @@
|
||||
{
|
||||
"label": "authentik: install",
|
||||
"command": "make",
|
||||
"args": [
|
||||
"install",
|
||||
"-j4"
|
||||
],
|
||||
"args": ["install", "-j4"],
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "authentik/website: make",
|
||||
"command": "make",
|
||||
"args": [
|
||||
"website"
|
||||
],
|
||||
"args": ["website"],
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "authentik/website: watch",
|
||||
"command": "make",
|
||||
"args": [
|
||||
"website-watch"
|
||||
],
|
||||
"args": ["website-watch"],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"panel": "dedicated",
|
||||
@ -81,11 +61,7 @@
|
||||
{
|
||||
"label": "authentik/api: generate",
|
||||
"command": "uv",
|
||||
"args": [
|
||||
"run",
|
||||
"make",
|
||||
"gen"
|
||||
],
|
||||
"args": ["run", "make", "gen"],
|
||||
"group": "build"
|
||||
}
|
||||
]
|
||||
|
||||
@ -94,7 +94,7 @@ RUN --mount=type=secret,id=GEOIPUPDATE_ACCOUNT_ID \
|
||||
/bin/sh -c "/usr/bin/entry.sh || echo 'Failed to get GeoIP database, disabling'; exit 0"
|
||||
|
||||
# Stage 5: Download uv
|
||||
FROM ghcr.io/astral-sh/uv:0.6.10 AS uv
|
||||
FROM ghcr.io/astral-sh/uv:0.6.9 AS uv
|
||||
# Stage 6: Base python image
|
||||
FROM ghcr.io/goauthentik/fips-python:3.12.8-slim-bookworm-fips AS python-base
|
||||
|
||||
|
||||
12
Makefile
12
Makefile
@ -133,16 +133,14 @@ gen-client-ts: gen-clean-ts ## Build and install the authentik API for Typescri
|
||||
--rm -v ${PWD}:/local \
|
||||
--user ${UID}:${GID} \
|
||||
docker.io/openapitools/openapi-generator-cli:v7.11.0 generate \
|
||||
-i /local/schema.yml \
|
||||
-g typescript-fetch \
|
||||
-o /local/${GEN_API_TS} \
|
||||
-c /local/scripts/api-ts-config.yaml \
|
||||
--input-spec /local/schema.yml \
|
||||
--generator-name typescript-fetch \
|
||||
--output /local/${GEN_API_TS} \
|
||||
--config /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_API_TS} && npm i
|
||||
\cp -rf ./${GEN_API_TS}/* web/node_modules/@goauthentik/api
|
||||
npm install
|
||||
|
||||
gen-client-py: gen-clean-py ## Build and install the authentik API for Python
|
||||
docker run \
|
||||
|
||||
@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django_filters.filters import BooleanFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from rest_framework import mixins
|
||||
from rest_framework.fields import SerializerMethodField
|
||||
from rest_framework.fields import ReadOnlyField, SerializerMethodField
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from authentik.core.api.object_types import TypesMixin
|
||||
@ -18,10 +18,10 @@ from authentik.core.models import Provider
|
||||
class ProviderSerializer(ModelSerializer, MetaNameSerializer):
|
||||
"""Provider Serializer"""
|
||||
|
||||
assigned_application_slug = SerializerMethodField()
|
||||
assigned_application_name = SerializerMethodField()
|
||||
assigned_backchannel_application_slug = SerializerMethodField()
|
||||
assigned_backchannel_application_name = SerializerMethodField()
|
||||
assigned_application_slug = ReadOnlyField(source="application.slug")
|
||||
assigned_application_name = ReadOnlyField(source="application.name")
|
||||
assigned_backchannel_application_slug = ReadOnlyField(source="backchannel_application.slug")
|
||||
assigned_backchannel_application_name = ReadOnlyField(source="backchannel_application.name")
|
||||
|
||||
component = SerializerMethodField()
|
||||
|
||||
@ -31,38 +31,6 @@ class ProviderSerializer(ModelSerializer, MetaNameSerializer):
|
||||
return ""
|
||||
return obj.component
|
||||
|
||||
def get_assigned_application_slug(self, obj: Provider) -> str:
|
||||
"""Get application slug, return empty string if no application exists"""
|
||||
try:
|
||||
return obj.application.slug
|
||||
except Provider.application.RelatedObjectDoesNotExist:
|
||||
return ""
|
||||
|
||||
def get_assigned_application_name(self, obj: Provider) -> str:
|
||||
"""Get application name, return empty string if no application exists"""
|
||||
try:
|
||||
return obj.application.name
|
||||
except Provider.application.RelatedObjectDoesNotExist:
|
||||
return ""
|
||||
|
||||
def get_assigned_backchannel_application_slug(self, obj: Provider) -> str:
|
||||
"""Get backchannel application slug.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
"""
|
||||
if not obj.backchannel_application:
|
||||
return ""
|
||||
return obj.backchannel_application.slug or ""
|
||||
|
||||
def get_assigned_backchannel_application_name(self, obj: Provider) -> str:
|
||||
"""Get backchannel application name.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
"""
|
||||
if not obj.backchannel_application:
|
||||
return ""
|
||||
return obj.backchannel_application.name or ""
|
||||
|
||||
class Meta:
|
||||
model = Provider
|
||||
fields = [
|
||||
|
||||
@ -48,7 +48,6 @@ LOGGER = get_logger()
|
||||
|
||||
PLAN_CONTEXT_SOURCE_GROUPS = "source_groups"
|
||||
SESSION_KEY_SOURCE_FLOW_STAGES = "authentik/flows/source_flow_stages"
|
||||
SESSION_KEY_SOURCE_FLOW_CONTEXT = "authentik/flows/source_flow_context"
|
||||
SESSION_KEY_OVERRIDE_FLOW_TOKEN = "authentik/flows/source_override_flow_token" # nosec
|
||||
|
||||
|
||||
@ -262,7 +261,6 @@ class SourceFlowManager:
|
||||
plan.append_stage(stage)
|
||||
for stage in self.request.session.get(SESSION_KEY_SOURCE_FLOW_STAGES, []):
|
||||
plan.append_stage(stage)
|
||||
plan.context.update(self.request.session.get(SESSION_KEY_SOURCE_FLOW_CONTEXT, {}))
|
||||
return plan.to_redirect(self.request, flow)
|
||||
|
||||
def handle_auth(
|
||||
|
||||
@ -133,8 +133,6 @@ class TestApplicationsAPI(APITestCase):
|
||||
"provider_obj": {
|
||||
"assigned_application_name": "allowed",
|
||||
"assigned_application_slug": "allowed",
|
||||
"assigned_backchannel_application_name": "",
|
||||
"assigned_backchannel_application_slug": "",
|
||||
"authentication_flow": None,
|
||||
"invalidation_flow": None,
|
||||
"authorization_flow": str(self.provider.authorization_flow.pk),
|
||||
@ -188,8 +186,6 @@ class TestApplicationsAPI(APITestCase):
|
||||
"provider_obj": {
|
||||
"assigned_application_name": "allowed",
|
||||
"assigned_application_slug": "allowed",
|
||||
"assigned_backchannel_application_name": "",
|
||||
"assigned_backchannel_application_slug": "",
|
||||
"authentication_flow": None,
|
||||
"invalidation_flow": None,
|
||||
"authorization_flow": str(self.provider.authorization_flow.pk),
|
||||
|
||||
@ -3,8 +3,7 @@
|
||||
from django.urls import reverse
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from authentik.core.api.providers import ProviderSerializer
|
||||
from authentik.core.models import Application, PropertyMapping, Provider
|
||||
from authentik.core.models import PropertyMapping
|
||||
from authentik.core.tests.utils import create_test_admin_user
|
||||
|
||||
|
||||
@ -25,51 +24,3 @@ class TestProvidersAPI(APITestCase):
|
||||
reverse("authentik_api:provider-types"),
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_provider_serializer_without_application(self):
|
||||
"""Test that Provider serializer handles missing application gracefully"""
|
||||
# Create a provider without an application
|
||||
provider = Provider.objects.create(name="test-provider")
|
||||
|
||||
serializer = ProviderSerializer(instance=provider)
|
||||
serialized_data = serializer.data
|
||||
|
||||
# Check that fields return empty strings when no application exists
|
||||
self.assertEqual(serialized_data["assigned_application_slug"], "")
|
||||
self.assertEqual(serialized_data["assigned_application_name"], "")
|
||||
self.assertEqual(serialized_data["assigned_backchannel_application_slug"], "")
|
||||
self.assertEqual(serialized_data["assigned_backchannel_application_name"], "")
|
||||
|
||||
def test_provider_serializer_with_application(self):
|
||||
"""Test that Provider serializer correctly includes application data"""
|
||||
# Create an application
|
||||
app = Application.objects.create(name="Test App", slug="test-app")
|
||||
|
||||
# Create a provider with an application
|
||||
provider = Provider.objects.create(name="test-provider-with-app")
|
||||
app.provider = provider
|
||||
app.save()
|
||||
|
||||
serializer = ProviderSerializer(instance=provider)
|
||||
serialized_data = serializer.data
|
||||
|
||||
# Check that fields return correct values when application exists
|
||||
self.assertEqual(serialized_data["assigned_application_slug"], "test-app")
|
||||
self.assertEqual(serialized_data["assigned_application_name"], "Test App")
|
||||
self.assertEqual(serialized_data["assigned_backchannel_application_slug"], "")
|
||||
self.assertEqual(serialized_data["assigned_backchannel_application_name"], "")
|
||||
|
||||
def test_provider_api_response(self):
|
||||
"""Test that the API response includes empty strings for missing applications"""
|
||||
# Create a provider without an application
|
||||
provider = Provider.objects.create(name="test-provider-api")
|
||||
|
||||
response = self.client.get(
|
||||
reverse("authentik_api:provider-detail", kwargs={"pk": provider.pk}),
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data["assigned_application_slug"], "")
|
||||
self.assertEqual(response.data["assigned_application_name"], "")
|
||||
self.assertEqual(response.data["assigned_backchannel_application_slug"], "")
|
||||
self.assertEqual(response.data["assigned_backchannel_application_name"], "")
|
||||
|
||||
@ -11,14 +11,13 @@ from guardian.shortcuts import get_anonymous_user
|
||||
from authentik.core.models import Source, User
|
||||
from authentik.core.sources.flow_manager import (
|
||||
SESSION_KEY_OVERRIDE_FLOW_TOKEN,
|
||||
SESSION_KEY_SOURCE_FLOW_CONTEXT,
|
||||
SESSION_KEY_SOURCE_FLOW_STAGES,
|
||||
)
|
||||
from authentik.core.types import UILoginButton
|
||||
from authentik.enterprise.stages.source.models import SourceStage
|
||||
from authentik.flows.challenge import Challenge, ChallengeResponse
|
||||
from authentik.flows.models import FlowToken, in_memory_stage
|
||||
from authentik.flows.planner import PLAN_CONTEXT_IS_REDIRECTED, PLAN_CONTEXT_IS_RESTORED
|
||||
from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED
|
||||
from authentik.flows.stage import ChallengeStageView, StageView
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
|
||||
@ -54,9 +53,6 @@ class SourceStageView(ChallengeStageView):
|
||||
resume_token = self.create_flow_token()
|
||||
self.request.session[SESSION_KEY_OVERRIDE_FLOW_TOKEN] = resume_token
|
||||
self.request.session[SESSION_KEY_SOURCE_FLOW_STAGES] = [in_memory_stage(SourceStageFinal)]
|
||||
self.request.session[SESSION_KEY_SOURCE_FLOW_CONTEXT] = {
|
||||
PLAN_CONTEXT_IS_REDIRECTED: self.executor.flow,
|
||||
}
|
||||
return self.login_button.challenge
|
||||
|
||||
def create_flow_token(self) -> FlowToken:
|
||||
|
||||
@ -1,20 +1,5 @@
|
||||
# authentik configuration
|
||||
#
|
||||
# https://docs.goauthentik.io/docs/install-config/configuration/
|
||||
#
|
||||
# To override the settings in this file, run the following command from the repository root:
|
||||
#
|
||||
# ```shell
|
||||
# make gen-dev-config
|
||||
# ```
|
||||
#
|
||||
# You may edit the generated file to override the configuration below.
|
||||
#
|
||||
# When making modifying the default configuration file,
|
||||
# ensure that the corresponding documentation is updated to match.
|
||||
#
|
||||
# @see {@link ../../website/docs/install-config/configuration/configuration.mdx Configuration documentation} for more information.
|
||||
|
||||
# update website/docs/install-config/configuration/configuration.mdx
|
||||
# This is the default configuration file
|
||||
postgresql:
|
||||
host: localhost
|
||||
name: authentik
|
||||
|
||||
@ -13,7 +13,6 @@ from paramiko.ssh_exception import SSHException
|
||||
from structlog.stdlib import get_logger
|
||||
from yaml import safe_dump
|
||||
|
||||
from authentik import __version__
|
||||
from authentik.outposts.apps import MANAGED_OUTPOST
|
||||
from authentik.outposts.controllers.base import BaseClient, BaseController, ControllerException
|
||||
from authentik.outposts.docker_ssh import DockerInlineSSH, SSHManagedExternallyException
|
||||
@ -185,7 +184,7 @@ class DockerController(BaseController):
|
||||
try:
|
||||
self.client.images.pull(image)
|
||||
except DockerException: # pragma: no cover
|
||||
image = f"ghcr.io/goauthentik/{self.outpost.type}:{__version__}"
|
||||
image = f"ghcr.io/goauthentik/{self.outpost.type}:latest"
|
||||
self.client.images.pull(image)
|
||||
return image
|
||||
|
||||
|
||||
@ -74,8 +74,6 @@ class TestEndpointsAPI(APITestCase):
|
||||
"component": "ak-provider-rac-form",
|
||||
"assigned_application_slug": self.app.slug,
|
||||
"assigned_application_name": self.app.name,
|
||||
"assigned_backchannel_application_slug": "",
|
||||
"assigned_backchannel_application_name": "",
|
||||
"verbose_name": "RAC Provider",
|
||||
"verbose_name_plural": "RAC Providers",
|
||||
"meta_model_name": "authentik_providers_rac.racprovider",
|
||||
@ -126,8 +124,6 @@ class TestEndpointsAPI(APITestCase):
|
||||
"component": "ak-provider-rac-form",
|
||||
"assigned_application_slug": self.app.slug,
|
||||
"assigned_application_name": self.app.name,
|
||||
"assigned_backchannel_application_slug": "",
|
||||
"assigned_backchannel_application_name": "",
|
||||
"connection_expiry": "hours=8",
|
||||
"delete_token_on_disconnect": False,
|
||||
"verbose_name": "RAC Provider",
|
||||
@ -157,8 +153,6 @@ class TestEndpointsAPI(APITestCase):
|
||||
"component": "ak-provider-rac-form",
|
||||
"assigned_application_slug": self.app.slug,
|
||||
"assigned_application_name": self.app.name,
|
||||
"assigned_backchannel_application_slug": "",
|
||||
"assigned_backchannel_application_name": "",
|
||||
"connection_expiry": "hours=8",
|
||||
"delete_token_on_disconnect": False,
|
||||
"verbose_name": "RAC Provider",
|
||||
|
||||
@ -323,12 +323,7 @@
|
||||
"multiValued": false,
|
||||
"description": "Indicates whether or not an attribute is modifiable.",
|
||||
"required": false,
|
||||
"canonicalValues": [
|
||||
"readOnly",
|
||||
"readWrite",
|
||||
"immutable",
|
||||
"writeOnly"
|
||||
],
|
||||
"canonicalValues": ["readOnly", "readWrite", "immutable", "writeOnly"],
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
@ -340,12 +335,7 @@
|
||||
"multiValued": false,
|
||||
"description": "Indicates when an attribute is returned in a response (e.g., to a query).",
|
||||
"required": false,
|
||||
"canonicalValues": [
|
||||
"always",
|
||||
"never",
|
||||
"default",
|
||||
"request"
|
||||
],
|
||||
"canonicalValues": ["always", "never", "default", "request"],
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
@ -369,12 +359,7 @@
|
||||
"multiValued": true,
|
||||
"description": "Used only with an attribute of type 'reference'. Specifies a SCIM resourceType that a reference attribute MAY refer to, e.g., 'User'.",
|
||||
"required": false,
|
||||
"canonicalValues": [
|
||||
"resource",
|
||||
"external",
|
||||
"uri",
|
||||
"url"
|
||||
],
|
||||
"canonicalValues": ["resource", "external", "uri", "url"],
|
||||
"caseExact": false,
|
||||
"mutability": "readOnly",
|
||||
"returned": "default",
|
||||
|
||||
@ -8,7 +8,7 @@ from django.core.mail.backends.locmem import EmailBackend
|
||||
from django.urls import reverse
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow, create_test_user
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.flows.markers import StageMarker
|
||||
from authentik.flows.models import FlowDesignation, FlowStageBinding
|
||||
@ -67,36 +67,6 @@ class TestEmailStageSending(FlowTestCase):
|
||||
self.assertEqual(event.context["to_email"], [f"{self.user.name} <{self.user.email}>"])
|
||||
self.assertEqual(event.context["from_email"], "system@authentik.local")
|
||||
|
||||
def test_newlines_long_name(self):
|
||||
"""Test with pending user"""
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
|
||||
long_user = create_test_user()
|
||||
long_user.name = "Test User\r\n Many Words\r\n"
|
||||
long_user.save()
|
||||
plan.context[PLAN_CONTEXT_PENDING_USER] = long_user
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
Event.objects.filter(action=EventAction.EMAIL_SENT).delete()
|
||||
|
||||
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
|
||||
with patch(
|
||||
"authentik.stages.email.models.EmailStage.backend_class",
|
||||
PropertyMock(return_value=EmailBackend),
|
||||
):
|
||||
response = self.client.post(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(
|
||||
response,
|
||||
self.flow,
|
||||
response_errors={
|
||||
"non_field_errors": [{"string": "email-sent", "code": "email-sent"}]
|
||||
},
|
||||
)
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertEqual(mail.outbox[0].subject, "authentik")
|
||||
self.assertEqual(mail.outbox[0].to, [f"Test User Many Words <{long_user.email}>"])
|
||||
|
||||
def test_pending_fake_user(self):
|
||||
"""Test with pending (fake) user"""
|
||||
self.flow.designation = FlowDesignation.RECOVERY
|
||||
|
||||
@ -32,14 +32,7 @@ class TemplateEmailMessage(EmailMultiAlternatives):
|
||||
sanitized_to = []
|
||||
# Ensure that all recipients are valid
|
||||
for recipient_name, recipient_email in to:
|
||||
# Remove any newline characters from name and email before sanitizing
|
||||
clean_name = (
|
||||
recipient_name.replace("\n", " ").replace("\r", " ") if recipient_name else ""
|
||||
)
|
||||
clean_email = (
|
||||
recipient_email.replace("\n", "").replace("\r", "") if recipient_email else ""
|
||||
)
|
||||
sanitized_to.append(sanitize_address((clean_name, clean_email), "utf-8"))
|
||||
sanitized_to.append(sanitize_address((recipient_name, recipient_email), "utf-8"))
|
||||
super().__init__(to=sanitized_to, **kwargs)
|
||||
if not template_name:
|
||||
return
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
10
eslint.config.mjs
Normal file
10
eslint.config.mjs
Normal file
@ -0,0 +1,10 @@
|
||||
import { createESLintPackageConfig } from "@goauthentik/eslint-config";
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* ESLint configuration for authentik's monorepo.
|
||||
*/
|
||||
const ESLintConfig = createESLintPackageConfig();
|
||||
|
||||
export default ESLintConfig;
|
||||
@ -162,14 +162,13 @@ func (c *Config) parseScheme(rawVal string) string {
|
||||
if err != nil {
|
||||
return rawVal
|
||||
}
|
||||
switch u.Scheme {
|
||||
case "env":
|
||||
if u.Scheme == "env" {
|
||||
e, ok := os.LookupEnv(u.Host)
|
||||
if ok {
|
||||
return e
|
||||
}
|
||||
return u.RawQuery
|
||||
case "file":
|
||||
} else if u.Scheme == "file" {
|
||||
d, err := os.ReadFile(u.Path)
|
||||
if err != nil {
|
||||
return u.RawQuery
|
||||
|
||||
@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestConfigEnv(t *testing.T) {
|
||||
assert.NoError(t, os.Setenv("AUTHENTIK_SECRET_KEY", "bar"))
|
||||
os.Setenv("AUTHENTIK_SECRET_KEY", "bar")
|
||||
cfg = nil
|
||||
if err := Get().fromEnv(); err != nil {
|
||||
panic(err)
|
||||
@ -19,8 +19,8 @@ func TestConfigEnv(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConfigEnv_Scheme(t *testing.T) {
|
||||
assert.NoError(t, os.Setenv("foo", "bar"))
|
||||
assert.NoError(t, os.Setenv("AUTHENTIK_SECRET_KEY", "env://foo"))
|
||||
os.Setenv("foo", "bar")
|
||||
os.Setenv("AUTHENTIK_SECRET_KEY", "env://foo")
|
||||
cfg = nil
|
||||
if err := Get().fromEnv(); err != nil {
|
||||
panic(err)
|
||||
@ -33,15 +33,13 @@ func TestConfigEnv_File(t *testing.T) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
assert.NoError(t, os.Remove(file.Name()))
|
||||
}()
|
||||
defer os.Remove(file.Name())
|
||||
_, err = file.Write([]byte("bar"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
assert.NoError(t, os.Setenv("AUTHENTIK_SECRET_KEY", fmt.Sprintf("file://%s", file.Name())))
|
||||
os.Setenv("AUTHENTIK_SECRET_KEY", fmt.Sprintf("file://%s", file.Name()))
|
||||
cfg = nil
|
||||
if err := Get().fromEnv(); err != nil {
|
||||
panic(err)
|
||||
|
||||
@ -35,7 +35,7 @@ func EnableDebugServer() {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
_, err = fmt.Fprintf(w, "<a href='%[1]s'>%[1]s</a><br>", tpl)
|
||||
_, err = w.Write([]byte(fmt.Sprintf("<a href='%[1]s'>%[1]s</a><br>", tpl)))
|
||||
if err != nil {
|
||||
l.WithError(err).Warning("failed to write index")
|
||||
return nil
|
||||
|
||||
@ -44,11 +44,10 @@ func New(healthcheck func() bool) *GoUnicorn {
|
||||
signal.Notify(c, syscall.SIGHUP, syscall.SIGUSR2)
|
||||
go func() {
|
||||
for sig := range c {
|
||||
switch sig {
|
||||
case syscall.SIGHUP:
|
||||
if sig == syscall.SIGHUP {
|
||||
g.log.Info("SIGHUP received, forwarding to gunicorn")
|
||||
g.Reload()
|
||||
case syscall.SIGUSR2:
|
||||
} else if sig == syscall.SIGUSR2 {
|
||||
g.log.Info("SIGUSR2 received, restarting gunicorn")
|
||||
g.Restart()
|
||||
}
|
||||
|
||||
@ -35,19 +35,13 @@ func Paginator[Tobj any, Treq any, Tres PaginatorResponse[Tobj]](
|
||||
req PaginatorRequest[Treq, Tres],
|
||||
opts PaginatorOptions,
|
||||
) ([]Tobj, error) {
|
||||
if opts.Logger == nil {
|
||||
opts.Logger = log.NewEntry(log.StandardLogger())
|
||||
}
|
||||
var bfreq, cfreq interface{}
|
||||
fetchOffset := func(page int32) (Tres, error) {
|
||||
bfreq = req.Page(page)
|
||||
cfreq = bfreq.(PaginatorRequest[Treq, Tres]).PageSize(int32(opts.PageSize))
|
||||
res, hres, err := cfreq.(PaginatorRequest[Treq, Tres]).Execute()
|
||||
res, _, err := cfreq.(PaginatorRequest[Treq, Tres]).Execute()
|
||||
if err != nil {
|
||||
opts.Logger.WithError(err).WithField("page", page).Warning("failed to fetch page")
|
||||
if hres != nil && hres.StatusCode >= 400 && hres.StatusCode < 500 {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
@ -57,9 +51,6 @@ func Paginator[Tobj any, Treq any, Tres PaginatorResponse[Tobj]](
|
||||
for {
|
||||
apiObjects, err := fetchOffset(page)
|
||||
if err != nil {
|
||||
if page == 1 {
|
||||
return objects, err
|
||||
}
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -1,64 +1,5 @@
|
||||
package ak
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"goauthentik.io/api/v3"
|
||||
)
|
||||
|
||||
type fakeAPIType struct{}
|
||||
|
||||
type fakeAPIResponse struct {
|
||||
results []fakeAPIType
|
||||
pagination api.Pagination
|
||||
}
|
||||
|
||||
func (fapi *fakeAPIResponse) GetResults() []fakeAPIType { return fapi.results }
|
||||
func (fapi *fakeAPIResponse) GetPagination() api.Pagination { return fapi.pagination }
|
||||
|
||||
type fakeAPIRequest struct {
|
||||
res *fakeAPIResponse
|
||||
http *http.Response
|
||||
err error
|
||||
}
|
||||
|
||||
func (fapi *fakeAPIRequest) Page(page int32) *fakeAPIRequest { return fapi }
|
||||
func (fapi *fakeAPIRequest) PageSize(size int32) *fakeAPIRequest { return fapi }
|
||||
func (fapi *fakeAPIRequest) Execute() (*fakeAPIResponse, *http.Response, error) {
|
||||
return fapi.res, fapi.http, fapi.err
|
||||
}
|
||||
|
||||
func Test_Simple(t *testing.T) {
|
||||
req := &fakeAPIRequest{
|
||||
res: &fakeAPIResponse{
|
||||
results: []fakeAPIType{
|
||||
{},
|
||||
},
|
||||
pagination: api.Pagination{
|
||||
TotalPages: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
res, err := Paginator(req, PaginatorOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, res, 1)
|
||||
}
|
||||
|
||||
func Test_BadRequest(t *testing.T) {
|
||||
req := &fakeAPIRequest{
|
||||
http: &http.Response{
|
||||
StatusCode: 400,
|
||||
},
|
||||
err: errors.New("foo"),
|
||||
}
|
||||
res, err := Paginator(req, PaginatorOptions{})
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, []fakeAPIType{}, res)
|
||||
}
|
||||
|
||||
// func Test_PaginatorCompile(t *testing.T) {
|
||||
// req := api.ApiCoreUsersListRequest{}
|
||||
// Paginator(req, PaginatorOptions{
|
||||
|
||||
@ -148,8 +148,7 @@ func (ac *APIController) startWSHandler() {
|
||||
"outpost_type": ac.Server.Type(),
|
||||
"uuid": ac.instanceUUID.String(),
|
||||
}).Set(1)
|
||||
switch wsMsg.Instruction {
|
||||
case WebsocketInstructionTriggerUpdate:
|
||||
if wsMsg.Instruction == WebsocketInstructionTriggerUpdate {
|
||||
time.Sleep(ac.reloadOffset)
|
||||
logger.Debug("Got update trigger...")
|
||||
err := ac.OnRefresh()
|
||||
@ -164,7 +163,7 @@ func (ac *APIController) startWSHandler() {
|
||||
"build": constants.BUILD(""),
|
||||
}).SetToCurrentTime()
|
||||
}
|
||||
case WebsocketInstructionProviderSpecific:
|
||||
} else if wsMsg.Instruction == WebsocketInstructionProviderSpecific {
|
||||
for _, h := range ac.wsHandlers {
|
||||
h(context.Background(), wsMsg.Args)
|
||||
}
|
||||
|
||||
@ -66,12 +66,7 @@ func (ls *LDAPServer) StartLDAPServer() error {
|
||||
return err
|
||||
}
|
||||
proxyListener := &proxyproto.Listener{Listener: ln, ConnPolicy: utils.GetProxyConnectionPolicy()}
|
||||
defer func() {
|
||||
err := proxyListener.Close()
|
||||
if err != nil {
|
||||
ls.log.WithError(err).Warning("failed to close proxy listener")
|
||||
}
|
||||
}()
|
||||
defer proxyListener.Close()
|
||||
|
||||
ls.log.WithField("listen", listen).Info("Starting LDAP server")
|
||||
err = ls.s.Serve(proxyListener)
|
||||
|
||||
@ -49,12 +49,7 @@ func (ls *LDAPServer) StartLDAPTLSServer() error {
|
||||
}
|
||||
|
||||
proxyListener := &proxyproto.Listener{Listener: ln, ConnPolicy: utils.GetProxyConnectionPolicy()}
|
||||
defer func() {
|
||||
err := proxyListener.Close()
|
||||
if err != nil {
|
||||
ls.log.WithError(err).Warning("failed to close proxy listener")
|
||||
}
|
||||
}()
|
||||
defer proxyListener.Close()
|
||||
|
||||
tln := tls.NewListener(proxyListener, tlsConfig)
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
|
||||
|
||||
entries := make([]*ldap.Entry, 0)
|
||||
|
||||
scope := req.Scope
|
||||
scope := req.SearchRequest.Scope
|
||||
needUsers, needGroups := ms.si.GetNeededObjects(scope, req.BaseDN, req.FilterObjectClass)
|
||||
|
||||
if scope >= 0 && strings.EqualFold(req.BaseDN, baseDN) {
|
||||
|
||||
@ -56,7 +56,7 @@ func GetOIDCEndpoint(p api.ProxyOutpostConfig, authentikHost string, embedded bo
|
||||
if !embedded && hostBrowser == "" {
|
||||
return ep
|
||||
}
|
||||
var newHost = aku
|
||||
var newHost *url.URL = aku
|
||||
var newBrowserHost *url.URL
|
||||
if embedded {
|
||||
if authentikHost == "" {
|
||||
|
||||
@ -130,12 +130,7 @@ func (ps *ProxyServer) ServeHTTP() {
|
||||
return
|
||||
}
|
||||
proxyListener := &proxyproto.Listener{Listener: listener, ConnPolicy: utils.GetProxyConnectionPolicy()}
|
||||
defer func() {
|
||||
err := proxyListener.Close()
|
||||
if err != nil {
|
||||
ps.log.WithError(err).Warning("failed to close proxy listener")
|
||||
}
|
||||
}()
|
||||
defer proxyListener.Close()
|
||||
|
||||
ps.log.WithField("listen", listenAddress).Info("Starting HTTP server")
|
||||
ps.serve(proxyListener)
|
||||
@ -154,12 +149,7 @@ func (ps *ProxyServer) ServeHTTPS() {
|
||||
return
|
||||
}
|
||||
proxyListener := &proxyproto.Listener{Listener: web.TCPKeepAliveListener{TCPListener: ln.(*net.TCPListener)}, ConnPolicy: utils.GetProxyConnectionPolicy()}
|
||||
defer func() {
|
||||
err := proxyListener.Close()
|
||||
if err != nil {
|
||||
ps.log.WithError(err).Warning("failed to close proxy listener")
|
||||
}
|
||||
}()
|
||||
defer proxyListener.Close()
|
||||
|
||||
tlsListener := tls.NewListener(proxyListener, tlsConfig)
|
||||
ps.log.WithField("listen", listenAddress).Info("Starting HTTPS server")
|
||||
|
||||
@ -72,13 +72,11 @@ func (s *RedisStore) New(r *http.Request, name string) (*sessions.Session, error
|
||||
session.ID = c.Value
|
||||
|
||||
err = s.load(r.Context(), session)
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return session, nil
|
||||
}
|
||||
return session, err
|
||||
if err == nil {
|
||||
session.IsNew = false
|
||||
} else if err == redis.Nil {
|
||||
err = nil // no data stored
|
||||
}
|
||||
session.IsNew = false
|
||||
return session, err
|
||||
}
|
||||
|
||||
|
||||
@ -156,12 +156,7 @@ func (ws *WebServer) listenPlain() {
|
||||
return
|
||||
}
|
||||
proxyListener := &proxyproto.Listener{Listener: ln, ConnPolicy: utils.GetProxyConnectionPolicy()}
|
||||
defer func() {
|
||||
err := proxyListener.Close()
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to close proxy listener")
|
||||
}
|
||||
}()
|
||||
defer proxyListener.Close()
|
||||
|
||||
ws.log.WithField("listen", config.Get().Listen.HTTP).Info("Starting HTTP server")
|
||||
ws.serve(proxyListener)
|
||||
|
||||
@ -46,12 +46,7 @@ func (ws *WebServer) listenTLS() {
|
||||
return
|
||||
}
|
||||
proxyListener := &proxyproto.Listener{Listener: web.TCPKeepAliveListener{TCPListener: ln.(*net.TCPListener)}, ConnPolicy: utils.GetProxyConnectionPolicy()}
|
||||
defer func() {
|
||||
err := proxyListener.Close()
|
||||
if err != nil {
|
||||
ws.log.WithError(err).Warning("failed to close proxy listener")
|
||||
}
|
||||
}()
|
||||
defer proxyListener.Close()
|
||||
|
||||
tlsListener := tls.NewListener(proxyListener, tlsConfig)
|
||||
ws.log.WithField("listen", config.Get().Listen.HTTPS).Info("Starting HTTPS server")
|
||||
|
||||
8
lifecycle/aws/package-lock.json
generated
8
lifecycle/aws/package-lock.json
generated
@ -9,7 +9,7 @@
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1006.0",
|
||||
"aws-cdk": "^2.1005.0",
|
||||
"cross-env": "^7.0.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -17,9 +17,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk": {
|
||||
"version": "2.1006.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1006.0.tgz",
|
||||
"integrity": "sha512-6qYnCt4mBN+3i/5F+FC2yMETkDHY/IL7gt3EuqKVPcaAO4jU7oXfVSlR60CYRkZWL4fnAurUV14RkJuJyVG/IA==",
|
||||
"version": "2.1005.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1005.0.tgz",
|
||||
"integrity": "sha512-4ejfGGrGCEl0pg1xcqkxK0lpBEZqNI48wtrXhk6dYOFYPYMZtqn1kdla29ONN+eO2unewkNF4nLP1lPYhlf9Pg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
{
|
||||
"name": "@goauthentik/lifecycle-aws",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"aws-cfn": "cross-env CI=false cdk synth --version-reporting=false > template.yaml"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1005.0",
|
||||
"cross-env": "^7.0.3"
|
||||
},
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1006.0",
|
||||
"cross-env": "^7.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
43195
package-lock.json
generated
43195
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
37
package.json
37
package.json
@ -1,5 +1,38 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"name": "@goauthentik/universe",
|
||||
"version": "2025.2.2",
|
||||
"private": true
|
||||
"description": "Monorepo for authentik.",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"compile": "NODE_OPTIONS=\"--max-old-space-size=3000\" tsc -b",
|
||||
"compile:clean": "node ./sdk/out/scripts/clean.js",
|
||||
"lint": "run-s lint:prettier:check lint:eslint:check",
|
||||
"lint:eslint:check": "eslint .",
|
||||
"lint:eslint:fix": "eslint --fix .",
|
||||
"lint:fix": "run-s lint:prettier:fix lint:eslint:fix",
|
||||
"lint:prettier": "eslint .",
|
||||
"lint:prettier:check": "prettier --cache --check -u .",
|
||||
"lint:prettier:fix": "prettier --cache --write -u ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@eslint/js": "^9.11.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"@typescript-eslint/eslint-plugin": "^8.28.0",
|
||||
"@typescript-eslint/parser": "^8.28.0",
|
||||
"eslint": "^9.23.0",
|
||||
"eslint-plugin-lit": "^2.0.0",
|
||||
"eslint-plugin-wc": "^3.0.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.5.3",
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.29.0",
|
||||
"zx": "^8.4.1"
|
||||
},
|
||||
"workspaces": [
|
||||
"gen-ts-api",
|
||||
"web",
|
||||
"website",
|
||||
"packages/*"
|
||||
],
|
||||
"prettier": "@goauthentik/prettier-config"
|
||||
}
|
||||
|
||||
11
packages/eslint-config/@types/eslint-plugin-react-hooks.d.ts
vendored
Normal file
11
packages/eslint-config/@types/eslint-plugin-react-hooks.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @file TypeScript type definitions for eslint-plugin-react-hooks
|
||||
*/
|
||||
declare module "eslint-plugin-react-hooks" {
|
||||
import { ESLint } from "eslint";
|
||||
// We have to do this because ESLint aliases the namespace and class simultaneously.
|
||||
type PluginInstance = ESLint.Plugin;
|
||||
const Plugin: PluginInstance;
|
||||
|
||||
export default Plugin;
|
||||
}
|
||||
11
packages/eslint-config/@types/eslint-plugin-react.d.ts
vendored
Normal file
11
packages/eslint-config/@types/eslint-plugin-react.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* @file TypeScript type definitions for eslint-plugin-react
|
||||
*/
|
||||
declare module "eslint-plugin-react" {
|
||||
import { ESLint } from "eslint";
|
||||
// We have to do this because ESLint aliases the namespace and class simultaneously.
|
||||
type PluginInstance = ESLint.Plugin;
|
||||
const Plugin: PluginInstance;
|
||||
|
||||
export default Plugin;
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2024 Authentik Security, Inc.
|
||||
Copyright (c) 2025 Authentik Security, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
5
packages/eslint-config/README.md
Normal file
5
packages/eslint-config/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# `@goauthentik/eslint-config`
|
||||
|
||||
This package contains the ESLint configuration used by authentik.
|
||||
While it is possible to use this configuration outside of our projects,
|
||||
you may find that it is not as useful as other popular configurations.
|
||||
72
packages/eslint-config/index.js
Normal file
72
packages/eslint-config/index.js
Normal file
@ -0,0 +1,72 @@
|
||||
import eslint from "@eslint/js";
|
||||
import { javaScriptConfig } from "@goauthentik/eslint-config/javascript-config";
|
||||
import { reactConfig } from "@goauthentik/eslint-config/react-config";
|
||||
import { typescriptConfig } from "@goauthentik/eslint-config/typescript-config";
|
||||
import * as litconf from "eslint-plugin-lit";
|
||||
import * as wcconf from "eslint-plugin-wc";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @typedef ESLintPackageConfigOptions Options for creating package ESLint configuration.
|
||||
* @property {string[]} [ignorePatterns] Override ignore patterns for ESLint.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {string[]} Default Ignore patterns for ESLint.
|
||||
*/
|
||||
export const DefaultIgnorePatterns = [
|
||||
// ---
|
||||
"**/*.md",
|
||||
"**/out",
|
||||
"**/dist",
|
||||
"**/.wireit",
|
||||
"website/build/**",
|
||||
"website/.docusaurus/**",
|
||||
"**/node_modules",
|
||||
"**/coverage",
|
||||
"**/storybook-static",
|
||||
"**/locale-codes.ts",
|
||||
"**/src/locales",
|
||||
"**/gen-ts-api",
|
||||
];
|
||||
|
||||
/**
|
||||
* Given a preferred package name, creates a ESLint configuration object.
|
||||
*
|
||||
* @param {ESLintPackageConfigOptions} options The preferred package configuration options.
|
||||
*
|
||||
* @returns The ESLint configuration object.
|
||||
*/
|
||||
export function createESLintPackageConfig({ ignorePatterns = DefaultIgnorePatterns } = {}) {
|
||||
return tseslint.config(
|
||||
{
|
||||
ignores: ignorePatterns,
|
||||
},
|
||||
|
||||
eslint.configs.recommended,
|
||||
javaScriptConfig,
|
||||
|
||||
wcconf.configs["flat/recommended"],
|
||||
litconf.configs["flat/recommended"],
|
||||
|
||||
...tseslint.configs.recommended,
|
||||
|
||||
...typescriptConfig,
|
||||
|
||||
...reactConfig,
|
||||
|
||||
{
|
||||
rules: {
|
||||
"no-console": "off",
|
||||
},
|
||||
files: [
|
||||
// ---
|
||||
"**/scripts/**/*",
|
||||
"**/test/**/*",
|
||||
"**/tests/**/*",
|
||||
],
|
||||
},
|
||||
);
|
||||
}
|
||||
143
packages/eslint-config/javascript-config.js
Normal file
143
packages/eslint-config/javascript-config.js
Normal file
@ -0,0 +1,143 @@
|
||||
// @ts-check
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
const MAX_DEPTH = 4;
|
||||
const MAX_NESTED_CALLBACKS = 4;
|
||||
const MAX_PARAMS = 5;
|
||||
|
||||
/**
|
||||
* ESLint configuration for JavaScript authentik projects.
|
||||
*/
|
||||
export const javaScriptConfig = tseslint.config({
|
||||
rules: {
|
||||
// TODO: Clean up before enabling.
|
||||
"accessor-pairs": "off",
|
||||
"array-callback-return": "error",
|
||||
"block-scoped-var": "error",
|
||||
"consistent-return": ["error", { treatUndefinedAsUnspecified: false }],
|
||||
"consistent-this": ["error", "that"],
|
||||
"curly": "off",
|
||||
"dot-notation": [
|
||||
"error",
|
||||
{
|
||||
allowKeywords: true,
|
||||
},
|
||||
],
|
||||
"eqeqeq": "error",
|
||||
"func-names": ["error", "as-needed"],
|
||||
"guard-for-in": "error",
|
||||
"max-depth": ["error", MAX_DEPTH],
|
||||
"max-nested-callbacks": ["error", MAX_NESTED_CALLBACKS],
|
||||
"max-params": ["error", MAX_PARAMS],
|
||||
// TODO: Clean up before enabling.
|
||||
// "new-cap": "error",
|
||||
"no-alert": "error",
|
||||
"no-array-constructor": "error",
|
||||
"no-bitwise": [
|
||||
"error",
|
||||
{
|
||||
allow: ["~"],
|
||||
int32Hint: true,
|
||||
},
|
||||
],
|
||||
"no-caller": "error",
|
||||
"no-case-declarations": "error",
|
||||
"no-class-assign": "error",
|
||||
"no-cond-assign": "error",
|
||||
"no-const-assign": "error",
|
||||
"no-constant-condition": "error",
|
||||
"no-control-regex": "error",
|
||||
"no-debugger": "error",
|
||||
"no-delete-var": "error",
|
||||
"no-div-regex": "error",
|
||||
"no-dupe-args": "error",
|
||||
"no-dupe-keys": "error",
|
||||
"no-duplicate-case": "error",
|
||||
"no-else-return": "error",
|
||||
"no-empty": "error",
|
||||
"no-empty-character-class": "error",
|
||||
"no-empty-function": ["error", { allow: ["constructors"] }],
|
||||
"no-labels": "error",
|
||||
"no-eq-null": "error",
|
||||
"no-eval": "error",
|
||||
"no-ex-assign": "error",
|
||||
"no-extend-native": "error",
|
||||
"no-extra-bind": "error",
|
||||
"no-extra-boolean-cast": "error",
|
||||
"no-extra-label": "error",
|
||||
"no-fallthrough": "error",
|
||||
"no-func-assign": "error",
|
||||
"no-implied-eval": "error",
|
||||
"no-implicit-coercion": "error",
|
||||
"no-implicit-globals": "error",
|
||||
"no-inner-declarations": ["error", "functions"],
|
||||
"no-invalid-regexp": "error",
|
||||
"no-irregular-whitespace": "error",
|
||||
"no-iterator": "error",
|
||||
"no-label-var": "error",
|
||||
"no-lone-blocks": "error",
|
||||
"no-lonely-if": "error",
|
||||
"no-loop-func": "error",
|
||||
"no-multi-str": "error",
|
||||
// TODO: Clean up before enabling.
|
||||
"no-negated-condition": "off",
|
||||
"no-new": "error",
|
||||
"no-new-func": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-obj-calls": "error",
|
||||
"no-octal": "error",
|
||||
"no-octal-escape": "error",
|
||||
"no-param-reassign": ["error", { props: false }],
|
||||
"no-proto": "error",
|
||||
"no-redeclare": "error",
|
||||
"no-regex-spaces": "error",
|
||||
"no-restricted-syntax": ["error", "WithStatement"],
|
||||
"no-script-url": "error",
|
||||
"no-self-assign": "error",
|
||||
"no-self-compare": "error",
|
||||
"no-sequences": "error",
|
||||
// TODO: Clean up before enabling.
|
||||
// "no-shadow": "error",
|
||||
"no-shadow-restricted-names": "error",
|
||||
"no-sparse-arrays": "error",
|
||||
"no-this-before-super": "error",
|
||||
"no-throw-literal": "error",
|
||||
"no-trailing-spaces": "off", // Handled by Prettier.
|
||||
"no-undef": "off",
|
||||
"no-undef-init": "off",
|
||||
"no-unexpected-multiline": "error",
|
||||
"no-useless-constructor": "error",
|
||||
"no-unmodified-loop-condition": "error",
|
||||
"no-unneeded-ternary": "error",
|
||||
"no-unreachable": "error",
|
||||
"no-unused-expressions": "error",
|
||||
"no-unused-labels": "error",
|
||||
"no-use-before-define": "error",
|
||||
"no-useless-call": "error",
|
||||
"no-dupe-class-members": "error",
|
||||
"no-var": "error",
|
||||
"no-void": "error",
|
||||
"no-with": "error",
|
||||
"prefer-arrow-callback": "error",
|
||||
"prefer-const": "error",
|
||||
"prefer-rest-params": "error",
|
||||
"prefer-spread": "error",
|
||||
"prefer-template": "error",
|
||||
"radix": "error",
|
||||
"require-yield": "error",
|
||||
"strict": ["error", "global"],
|
||||
"use-isnan": "error",
|
||||
"valid-typeof": "error",
|
||||
"vars-on-top": "error",
|
||||
"yoda": ["error", "never"],
|
||||
|
||||
"no-console": ["error", { allow: ["debug", "warn", "error"] }],
|
||||
// SonarJS is not yet compatible with ESLint 9. Commenting these out
|
||||
// until it is.
|
||||
// "sonarjs/cognitive-complexity": ["off", MAX_COGNITIVE_COMPLEXITY],
|
||||
// "sonarjs/no-duplicate-string": "off",
|
||||
// "sonarjs/no-nested-template-literals": "off",
|
||||
},
|
||||
});
|
||||
|
||||
export default javaScriptConfig;
|
||||
53
packages/eslint-config/package.json
Normal file
53
packages/eslint-config/package.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "@goauthentik/eslint-config",
|
||||
"version": "1.0.0",
|
||||
"description": "authentik's ESLint config",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": "./index.js",
|
||||
"types": "./out/index.d.ts"
|
||||
},
|
||||
"./react-config": {
|
||||
"import": "./react-config.js",
|
||||
"types": "./out/react-config.d.ts"
|
||||
},
|
||||
"./javascript-config": {
|
||||
"import": "./javascript-config.js",
|
||||
"types": "./out/javascript-config.d.ts"
|
||||
},
|
||||
"./typescript-config": {
|
||||
"import": "./typescript-config.js",
|
||||
"types": "./out/typescript-config.d.ts"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint": "^9.23.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"eslint-plugin-react-hooks": "^5.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@goauthentik/tsconfig": "1.0.0",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.29.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.29.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"react": "^18.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
},
|
||||
"types": "./out/index.d.ts",
|
||||
"prettier": "@goauthentik/prettier-config",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
34
packages/eslint-config/react-config.js
vendored
Normal file
34
packages/eslint-config/react-config.js
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
import reactPlugin from "eslint-plugin-react";
|
||||
import hooksPlugin from "eslint-plugin-react-hooks";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
/**
|
||||
* ESLint configuration for React authentik projects.
|
||||
*/
|
||||
export const reactConfig = tseslint.config({
|
||||
settings: {
|
||||
react: {
|
||||
version: "detect",
|
||||
},
|
||||
},
|
||||
|
||||
plugins: {
|
||||
"react": reactPlugin,
|
||||
"react-hooks": hooksPlugin,
|
||||
},
|
||||
|
||||
rules: {
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-hooks/exhaustive-deps": "warn",
|
||||
|
||||
"react/jsx-uses-react": 0,
|
||||
|
||||
"react/display-name": "off",
|
||||
"react/jsx-curly-brace-presence": "error",
|
||||
"react/jsx-no-leaked-render": "error",
|
||||
"react/prop-types": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
},
|
||||
});
|
||||
|
||||
export default reactConfig;
|
||||
8
packages/eslint-config/tsconfig.json
Normal file
8
packages/eslint-config/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@goauthentik/tsconfig",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"checkJs": true,
|
||||
"emitDeclarationOnly": true
|
||||
}
|
||||
}
|
||||
35
packages/eslint-config/typescript-config.js
Normal file
35
packages/eslint-config/typescript-config.js
Normal file
@ -0,0 +1,35 @@
|
||||
// @ts-check
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
/**
|
||||
* ESLint configuration for TypeScript authentik projects.
|
||||
*/
|
||||
export const typescriptConfig = tseslint.config({
|
||||
rules: {
|
||||
"@typescript-eslint/ban-ts-comment": [
|
||||
"error",
|
||||
{
|
||||
"ts-expect-error": "allow-with-description",
|
||||
"ts-ignore": true,
|
||||
"ts-nocheck": "allow-with-description",
|
||||
"ts-check": false,
|
||||
"minimumDescriptionLength": 5,
|
||||
},
|
||||
],
|
||||
"no-use-before-define": "off",
|
||||
"@typescript-eslint/no-use-before-define": "error",
|
||||
"no-invalid-this": "off",
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/no-namespace": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export default typescriptConfig;
|
||||
18
packages/monorepo/LICENSE.txt
Normal file
18
packages/monorepo/LICENSE.txt
Normal file
@ -0,0 +1,18 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2025 Authentik Security, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
5
packages/monorepo/README.md
Normal file
5
packages/monorepo/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# `@goauthentik/monorepo`
|
||||
|
||||
This package contains utility scripts common to all TypeScript and JavaScript packages in the
|
||||
`@goauthentik` monorepo.
|
||||
|
||||
17
packages/monorepo/constants.js
Normal file
17
packages/monorepo/constants.js
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @file Constants for JavaScript and TypeScript files.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* The current Node.js environment, defaulting to "development" when not set.
|
||||
*
|
||||
* Note, this should only be used during the build process.
|
||||
*
|
||||
* If you need to check the environment at runtime, use `process.env.NODE_ENV` to
|
||||
* ensure that module tree-shaking works correctly.
|
||||
*
|
||||
*/
|
||||
export const NodeEnvironment = /** @type {'development' | 'production'} */ (
|
||||
process.env.NODE_ENV || "development"
|
||||
);
|
||||
4
packages/monorepo/index.js
Normal file
4
packages/monorepo/index.js
Normal file
@ -0,0 +1,4 @@
|
||||
export * from "./paths.js";
|
||||
export * from "./constants.js";
|
||||
export * from "./version.js";
|
||||
export * from "./scripting.js";
|
||||
19
packages/monorepo/package.json
Normal file
19
packages/monorepo/package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@goauthentik/monorepo",
|
||||
"version": "1.0.0",
|
||||
"description": "Utilities for the authentik monorepo.",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": "./index.js",
|
||||
"types": "./out/index.d.ts"
|
||||
}
|
||||
},
|
||||
"types": "./out/index.d.ts",
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
}
|
||||
}
|
||||
30
packages/monorepo/paths.js
Normal file
30
packages/monorepo/paths.js
Normal file
@ -0,0 +1,30 @@
|
||||
import { createRequire } from "node:module";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/**
|
||||
* @typedef {'~authentik'} MonoRepoRoot
|
||||
*/
|
||||
|
||||
/**
|
||||
* The root of the authentik monorepo.
|
||||
*/
|
||||
export const MonoRepoRoot = /** @type {MonoRepoRoot} */ (resolve(__dirname, "..", ".."));
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
/**
|
||||
* Resolve a package name to its location in the monorepo to the single node_modules directory.
|
||||
* @param {string} packageName
|
||||
* @returns {string} The resolved path to the package.
|
||||
* @throws {Error} If the package cannot be resolved.
|
||||
*/
|
||||
export function resolvePackage(packageName) {
|
||||
const packageJSONPath = require.resolve(join(packageName, "package.json"), {
|
||||
paths: [MonoRepoRoot],
|
||||
});
|
||||
|
||||
return dirname(packageJSONPath);
|
||||
}
|
||||
40
packages/monorepo/scripting.js
Normal file
40
packages/monorepo/scripting.js
Normal file
@ -0,0 +1,40 @@
|
||||
import { createRequire } from "module";
|
||||
import * as path from "path";
|
||||
import * as process from "process";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
/**
|
||||
* Predicate to determine if a module was run directly, i.e. not imported.
|
||||
*
|
||||
* @param {ImportMeta} meta The `import.meta` object of the module.
|
||||
*
|
||||
* @return {boolean} Whether the module was run directly.
|
||||
*/
|
||||
export function isMain(meta) {
|
||||
// Are we not in a module context?
|
||||
if (!meta) return false;
|
||||
|
||||
const relativeScriptPath = process.argv[1];
|
||||
|
||||
if (!relativeScriptPath) return false;
|
||||
|
||||
const require = createRequire(meta.url);
|
||||
const absoluteScriptPath = require.resolve(relativeScriptPath);
|
||||
|
||||
const modulePath = fileURLToPath(meta.url);
|
||||
|
||||
const scriptExtension = path.extname(absoluteScriptPath);
|
||||
|
||||
if (scriptExtension) {
|
||||
return modulePath === absoluteScriptPath;
|
||||
}
|
||||
|
||||
const moduleExtension = path.extname(modulePath);
|
||||
|
||||
if (moduleExtension) {
|
||||
return absoluteScriptPath === modulePath.slice(0, -moduleExtension.length);
|
||||
}
|
||||
|
||||
// If both are without extension, compare them directly.
|
||||
return modulePath === absoluteScriptPath;
|
||||
}
|
||||
0
packages/monorepo/scripts.js
Normal file
0
packages/monorepo/scripts.js
Normal file
9
packages/monorepo/tsconfig.json
Normal file
9
packages/monorepo/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@goauthentik/tsconfig",
|
||||
"compilerOptions": {
|
||||
"resolveJsonModule": true,
|
||||
"baseUrl": ".",
|
||||
"checkJs": true,
|
||||
"emitDeclarationOnly": true
|
||||
}
|
||||
}
|
||||
45
packages/monorepo/version.js
Normal file
45
packages/monorepo/version.js
Normal file
@ -0,0 +1,45 @@
|
||||
import { execSync } from "node:child_process";
|
||||
|
||||
import PackageJSON from "../../package.json" with { type: "json" };
|
||||
import { MonoRepoRoot } from "./paths.js";
|
||||
|
||||
/**
|
||||
* The current version of authentik in SemVer format.
|
||||
*
|
||||
*/
|
||||
export const AuthentikVersion = /**@type {`${number}.${number}.${number}`} */ (PackageJSON.version);
|
||||
|
||||
/**
|
||||
* Reads the last commit hash from the current git repository.
|
||||
*/
|
||||
export function readGitBuildHash() {
|
||||
try {
|
||||
const commit = execSync("git rev-parse HEAD", {
|
||||
encoding: "utf8",
|
||||
cwd: MonoRepoRoot,
|
||||
})
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
return commit;
|
||||
} catch (_error) {
|
||||
console.debug("Git commit could not be read.");
|
||||
}
|
||||
|
||||
return process.env.GIT_BUILD_HASH || "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the build identifier for the current environment.
|
||||
*
|
||||
* This must match the behavior defined in authentik's server-side `get_full_version` function.
|
||||
*
|
||||
* @see {@link "authentik\_\_init\_\_.py"}
|
||||
*/
|
||||
export function readBuildIdentifier() {
|
||||
const { GIT_BUILD_HASH = "d72def036820985a909266e8167ccb8087c7ce32" } = process.env;
|
||||
|
||||
if (!GIT_BUILD_HASH) return AuthentikVersion;
|
||||
|
||||
return [AuthentikVersion, GIT_BUILD_HASH].join("+");
|
||||
}
|
||||
18
packages/prettier-config/LICENSE.txt
Normal file
18
packages/prettier-config/LICENSE.txt
Normal file
@ -0,0 +1,18 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2025 Authentik Security, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
5
packages/prettier-config/README.md
Normal file
5
packages/prettier-config/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# `@goauthentik/prettier-config`
|
||||
|
||||
This package contains the Prettier configuration used by authentik.
|
||||
While it is possible to use this configuration outside of our projects,
|
||||
you may find that it is not as useful as other popular configurations.
|
||||
80
packages/prettier-config/config.js
Normal file
80
packages/prettier-config/config.js
Normal file
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @file Prettier configuration for authentik.
|
||||
*
|
||||
* @import { Config as PrettierConfig } from "prettier";
|
||||
* @import { PluginConfig as SortPluginConfig } from "@trivago/prettier-plugin-sort-imports";
|
||||
*
|
||||
* @typedef {object} PackageJSONPluginConfig
|
||||
* @property {string[]} [packageSortOrder] Custom ordering array.
|
||||
*/
|
||||
|
||||
/**
|
||||
* authentik Prettier configuration.
|
||||
*
|
||||
* @type {PrettierConfig & SortPluginConfig & PackageJSONPluginConfig}
|
||||
* @internal
|
||||
*/
|
||||
export const AuthentikPrettierConfig = {
|
||||
arrowParens: "always",
|
||||
bracketSpacing: true,
|
||||
embeddedLanguageFormatting: "auto",
|
||||
htmlWhitespaceSensitivity: "css",
|
||||
insertPragma: false,
|
||||
jsxSingleQuote: false,
|
||||
printWidth: 100,
|
||||
proseWrap: "preserve",
|
||||
quoteProps: "consistent",
|
||||
requirePragma: false,
|
||||
semi: true,
|
||||
singleQuote: false,
|
||||
tabWidth: 4,
|
||||
trailingComma: "all",
|
||||
useTabs: false,
|
||||
vueIndentScriptAndStyle: false,
|
||||
plugins: ["prettier-plugin-packagejson", "@trivago/prettier-plugin-sort-imports"],
|
||||
importOrder: ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
|
||||
importOrderSeparation: true,
|
||||
importOrderSortSpecifiers: true,
|
||||
importOrderParserPlugins: ["typescript", "jsx", "classProperties", "decorators-legacy"],
|
||||
overrides: [
|
||||
{
|
||||
files: "schemas/**/*.json",
|
||||
options: {
|
||||
tabWidth: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: "tsconfig.json",
|
||||
options: {
|
||||
trailingComma: "none",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: "package.json",
|
||||
options: {
|
||||
packageSortOrder: [
|
||||
// ---
|
||||
"name",
|
||||
"version",
|
||||
"description",
|
||||
"license",
|
||||
"private",
|
||||
"author",
|
||||
"authors",
|
||||
"scripts",
|
||||
"main",
|
||||
"type",
|
||||
"exports",
|
||||
"imports",
|
||||
"dependencies",
|
||||
"devDependencies",
|
||||
"peerDependencies",
|
||||
"optionalDependencies",
|
||||
"wireit",
|
||||
"resolutions",
|
||||
"engines",
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
20
packages/prettier-config/format.js
Normal file
20
packages/prettier-config/format.js
Normal file
@ -0,0 +1,20 @@
|
||||
import { format } from "prettier";
|
||||
|
||||
import { AuthentikPrettierConfig } from "./config.js";
|
||||
|
||||
/**
|
||||
* Format using Prettier.
|
||||
*
|
||||
* Defaults to using the TypeScript parser.
|
||||
*
|
||||
* @category Formatting
|
||||
* @param {string} fileContents The contents of the file to format.
|
||||
*
|
||||
* @returns {Promise<string>} The formatted file contents.
|
||||
*/
|
||||
export function formatWithPrettier(fileContents) {
|
||||
return format(fileContents, {
|
||||
...AuthentikPrettierConfig,
|
||||
parser: "typescript",
|
||||
});
|
||||
}
|
||||
6
packages/prettier-config/index.js
Normal file
6
packages/prettier-config/index.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { AuthentikPrettierConfig } from "./config.js";
|
||||
|
||||
export * from "./config.js";
|
||||
export * from "./format.js";
|
||||
|
||||
export default AuthentikPrettierConfig;
|
||||
27
packages/prettier-config/package.json
Normal file
27
packages/prettier-config/package.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "@goauthentik/prettier-config",
|
||||
"version": "1.0.0",
|
||||
"description": "authentik's Prettier config",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": "./index.js",
|
||||
"types": "./out/index.d.ts"
|
||||
}
|
||||
},
|
||||
"types": "./out/index.d.ts",
|
||||
"peerDependencies": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"prettier-plugin-packagejson": "^2.5.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
8
packages/prettier-config/tsconfig.json
Normal file
8
packages/prettier-config/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@goauthentik/tsconfig",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"checkJs": true,
|
||||
"emitDeclarationOnly": true
|
||||
}
|
||||
}
|
||||
18
packages/tsconfig/LICENSE.txt
Normal file
18
packages/tsconfig/LICENSE.txt
Normal file
@ -0,0 +1,18 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2025 Authentik Security, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
6
packages/tsconfig/README.md
Normal file
6
packages/tsconfig/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# `@goauthentik/tsconfig`
|
||||
|
||||
This package contains the TypeScript configuration used by authentik TypeScript projects.
|
||||
|
||||
While it is possible to use this configuration outside of our projects,
|
||||
you may find that it is not as useful as other popular configurations.
|
||||
18
packages/tsconfig/package.json
Normal file
18
packages/tsconfig/package.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "@goauthentik/tsconfig",
|
||||
"version": "1.0.0",
|
||||
"description": "authentik's s base TypeScript configuration.",
|
||||
"keywords": [
|
||||
"tsconfig",
|
||||
"typescript"
|
||||
],
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"main": "tsconfig.json",
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
29
packages/tsconfig/tsconfig.json
Normal file
29
packages/tsconfig/tsconfig.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"alwaysStrict": true,
|
||||
"baseUrl": ".",
|
||||
"composite": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"esModuleInterop": false,
|
||||
"isolatedModules": true,
|
||||
"incremental": true,
|
||||
"jsx": "react-jsx",
|
||||
"lib": ["ESNext"],
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"newLine": "lf",
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitOverride": false,
|
||||
"outDir": "${configDir}/out",
|
||||
"pretty": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"target": "ESNext",
|
||||
"useUnknownInCatchVariables": true
|
||||
}
|
||||
}
|
||||
@ -103,7 +103,7 @@ dev = [
|
||||
|
||||
[tool.uv.sources]
|
||||
django-tenants = { git = "https://github.com/rissson/django-tenants.git", branch = "authentik-fixes" }
|
||||
opencontainers = { git = "https://github.com/BeryJu/oci-python", rev = "c791b19056769cd67957322806809ab70f5bead8" }
|
||||
opencontainers = { git = "https://github.com/vsoch/oci-python", rev = "20d69d9cc50a0fef31605b46f06da0c94f1ec3cf" }
|
||||
|
||||
[project.scripts]
|
||||
ak = "lifecycle.ak:main"
|
||||
|
||||
142
schema.yml
142
schema.yml
@ -44141,17 +44141,11 @@ components:
|
||||
readOnly: true
|
||||
assigned_backchannel_application_slug:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application slug.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_name:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application name.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
verbose_name:
|
||||
type: string
|
||||
@ -45681,27 +45675,19 @@ components:
|
||||
readOnly: true
|
||||
assigned_application_slug:
|
||||
type: string
|
||||
description: Get application slug, return empty string if no application
|
||||
exists
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_application_name:
|
||||
type: string
|
||||
description: Get application name, return empty string if no application
|
||||
exists
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_slug:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application slug.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_name:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application name.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
verbose_name:
|
||||
type: string
|
||||
@ -46409,17 +46395,11 @@ components:
|
||||
readOnly: true
|
||||
assigned_backchannel_application_slug:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application slug.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_name:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application name.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
verbose_name:
|
||||
type: string
|
||||
@ -47042,27 +47022,19 @@ components:
|
||||
readOnly: true
|
||||
assigned_application_slug:
|
||||
type: string
|
||||
description: Get application slug, return empty string if no application
|
||||
exists
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_application_name:
|
||||
type: string
|
||||
description: Get application name, return empty string if no application
|
||||
exists
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_slug:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application slug.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_name:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application name.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
verbose_name:
|
||||
type: string
|
||||
@ -53876,27 +53848,19 @@ components:
|
||||
readOnly: true
|
||||
assigned_application_slug:
|
||||
type: string
|
||||
description: Get application slug, return empty string if no application
|
||||
exists
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_application_name:
|
||||
type: string
|
||||
description: Get application name, return empty string if no application
|
||||
exists
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_slug:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application slug.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_name:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application name.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
verbose_name:
|
||||
type: string
|
||||
@ -54125,27 +54089,19 @@ components:
|
||||
readOnly: true
|
||||
assigned_application_slug:
|
||||
type: string
|
||||
description: Get application slug, return empty string if no application
|
||||
exists
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_application_name:
|
||||
type: string
|
||||
description: Get application name, return empty string if no application
|
||||
exists
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_slug:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application slug.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_name:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application name.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
verbose_name:
|
||||
type: string
|
||||
@ -54452,27 +54408,19 @@ components:
|
||||
readOnly: true
|
||||
assigned_application_slug:
|
||||
type: string
|
||||
description: Get application slug, return empty string if no application
|
||||
exists
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_application_name:
|
||||
type: string
|
||||
description: Get application name, return empty string if no application
|
||||
exists
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_slug:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application slug.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_name:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application name.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
verbose_name:
|
||||
type: string
|
||||
@ -54625,27 +54573,19 @@ components:
|
||||
readOnly: true
|
||||
assigned_application_slug:
|
||||
type: string
|
||||
description: Get application slug, return empty string if no application
|
||||
exists
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_application_name:
|
||||
type: string
|
||||
description: Get application name, return empty string if no application
|
||||
exists
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_slug:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application slug.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_name:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application name.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
verbose_name:
|
||||
type: string
|
||||
@ -55245,27 +55185,19 @@ components:
|
||||
readOnly: true
|
||||
assigned_application_slug:
|
||||
type: string
|
||||
description: Get application slug, return empty string if no application
|
||||
exists
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_application_name:
|
||||
type: string
|
||||
description: Get application name, return empty string if no application
|
||||
exists
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_slug:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application slug.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_name:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application name.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
verbose_name:
|
||||
type: string
|
||||
@ -55968,17 +55900,11 @@ components:
|
||||
readOnly: true
|
||||
assigned_backchannel_application_slug:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application slug.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Internal application name, used in URLs.
|
||||
readOnly: true
|
||||
assigned_backchannel_application_name:
|
||||
type: string
|
||||
description: |-
|
||||
Get backchannel application name.
|
||||
|
||||
Returns an empty string if no backchannel application exists.
|
||||
description: Application's display Name.
|
||||
readOnly: true
|
||||
verbose_name:
|
||||
type: string
|
||||
|
||||
@ -28,10 +28,7 @@
|
||||
"type": {
|
||||
"description": "A label indicating the type of resource, e.g., 'User' or 'Group'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"User",
|
||||
"Group"
|
||||
],
|
||||
"enum": ["User", "Group"],
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
@ -39,7 +36,5 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"displayName"
|
||||
]
|
||||
"required": ["displayName"]
|
||||
}
|
||||
|
||||
@ -51,10 +51,7 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"schema",
|
||||
"required"
|
||||
],
|
||||
"required": ["schema", "required"],
|
||||
"additionalProperties": true
|
||||
}
|
||||
],
|
||||
@ -62,11 +59,6 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"endpoint",
|
||||
"schema",
|
||||
"schemaExtensions"
|
||||
],
|
||||
"required": ["name", "endpoint", "schema", "schemaExtensions"],
|
||||
"additionalProperties": true
|
||||
}
|
||||
|
||||
@ -21,9 +21,7 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"supported"
|
||||
],
|
||||
"required": ["supported"],
|
||||
"readOnly": true
|
||||
},
|
||||
"bulk": {
|
||||
@ -36,9 +34,7 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"supported"
|
||||
],
|
||||
"required": ["supported"],
|
||||
"readOnly": true
|
||||
},
|
||||
"filter": {
|
||||
@ -56,9 +52,7 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"supported"
|
||||
],
|
||||
"required": ["supported"],
|
||||
"readOnly": true
|
||||
},
|
||||
"changePassword": {
|
||||
@ -71,9 +65,7 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"supported"
|
||||
],
|
||||
"required": ["supported"],
|
||||
"readOnly": true
|
||||
},
|
||||
"sort": {
|
||||
@ -86,9 +78,7 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"supported"
|
||||
],
|
||||
"required": ["supported"],
|
||||
"readOnly": true
|
||||
},
|
||||
"authenticationSchemes": {
|
||||
@ -119,21 +109,11 @@
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"description"
|
||||
],
|
||||
"required": ["name", "description"],
|
||||
"readOnly": true
|
||||
},
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"patch",
|
||||
"bulk",
|
||||
"filter",
|
||||
"changePassword",
|
||||
"sort",
|
||||
"authenticationSchemes"
|
||||
]
|
||||
"required": ["patch", "bulk", "filter", "changePassword", "sort", "authenticationSchemes"]
|
||||
}
|
||||
|
||||
@ -99,11 +99,7 @@
|
||||
"type": {
|
||||
"description": "A label indicating the attribute's function, e.g., 'work' or 'home'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"work",
|
||||
"home",
|
||||
"other"
|
||||
]
|
||||
"enum": ["work", "home", "other"]
|
||||
},
|
||||
"primary": {
|
||||
"description": "A Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g., the preferred mailing address or primary email address. The primary attribute value 'true' MUST appear no more than once.",
|
||||
@ -129,14 +125,7 @@
|
||||
"type": {
|
||||
"description": "A label indicating the attribute's function, e.g., 'work', 'home', 'mobile'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"work",
|
||||
"home",
|
||||
"mobile",
|
||||
"fax",
|
||||
"pager",
|
||||
"other"
|
||||
]
|
||||
"enum": ["work", "home", "mobile", "fax", "pager", "other"]
|
||||
},
|
||||
"primary": {
|
||||
"description": "A Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g., the preferred phone number or primary phone number. The primary attribute value 'true' MUST appear no more than once.",
|
||||
@ -162,16 +151,7 @@
|
||||
"type": {
|
||||
"description": "A label indicating the attribute's function, e.g., 'aim', 'gtalk', 'xmpp'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"aim",
|
||||
"gtalk",
|
||||
"icq",
|
||||
"xmpp",
|
||||
"msn",
|
||||
"skype",
|
||||
"qq",
|
||||
"yahoo"
|
||||
]
|
||||
"enum": ["aim", "gtalk", "icq", "xmpp", "msn", "skype", "qq", "yahoo"]
|
||||
},
|
||||
"primary": {
|
||||
"description": "A Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g., the preferred messenger or primary messenger. The primary attribute value 'true' MUST appear no more than once.",
|
||||
@ -198,10 +178,7 @@
|
||||
"type": {
|
||||
"description": "A label indicating the attribute's function, i.e., 'photo' or 'thumbnail'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"photo",
|
||||
"thumbnail"
|
||||
]
|
||||
"enum": ["photo", "thumbnail"]
|
||||
},
|
||||
"primary": {
|
||||
"description": "A Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g., the preferred photo or thumbnail. The primary attribute value 'true' MUST appear no more than once.",
|
||||
@ -243,11 +220,7 @@
|
||||
"type": {
|
||||
"description": "A label indicating the attribute's function, e.g., 'work' or 'home'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"work",
|
||||
"home",
|
||||
"other"
|
||||
]
|
||||
"enum": ["work", "home", "other"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -277,10 +250,7 @@
|
||||
"type": {
|
||||
"description": "A label indicating the attribute's function, e.g., 'direct' or 'indirect'.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"direct",
|
||||
"indirect"
|
||||
],
|
||||
"enum": ["direct", "indirect"],
|
||||
"readOnly": true
|
||||
}
|
||||
},
|
||||
@ -366,7 +336,5 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"userName"
|
||||
]
|
||||
"required": ["userName"]
|
||||
}
|
||||
|
||||
@ -5,85 +5,45 @@ from yaml import safe_dump
|
||||
|
||||
from authentik.lib.generators import generate_id
|
||||
|
||||
|
||||
def generate_local_config():
|
||||
"""Generate a local development configuration"""
|
||||
# TODO: This should be generated and validated against a schema, such as Pydantic.
|
||||
|
||||
return {
|
||||
"debug": True,
|
||||
"log_level": "debug",
|
||||
"secret_key": generate_id(),
|
||||
"postgresql": {
|
||||
"user": "postgres",
|
||||
},
|
||||
"outposts": {
|
||||
"container_image_base": "ghcr.io/goauthentik/dev-%(type)s:gh-%(build_hash)s",
|
||||
"disable_embedded_outpost": False,
|
||||
},
|
||||
"blueprints_dir": "./blueprints",
|
||||
"cert_discovery_dir": "./certs",
|
||||
"events": {
|
||||
"processors": {
|
||||
"geoip": "tests/GeoLite2-City-Test.mmdb",
|
||||
"asn": "tests/GeoLite2-ASN-Test.mmdb",
|
||||
}
|
||||
},
|
||||
"storage": {
|
||||
"media": {
|
||||
"backend": "file",
|
||||
"s3": {
|
||||
"endpoint": "http://localhost:8020",
|
||||
"access_key": "accessKey1",
|
||||
"secret_key": "secretKey1",
|
||||
"bucket_name": "authentik-media",
|
||||
"custom_domain": "localhost:8020/authentik-media",
|
||||
"secure_urls": False,
|
||||
with open("local.env.yml", "w", encoding="utf-8") as _config:
|
||||
safe_dump(
|
||||
{
|
||||
"debug": True,
|
||||
"log_level": "debug",
|
||||
"secret_key": generate_id(),
|
||||
"postgresql": {
|
||||
"user": "postgres",
|
||||
},
|
||||
"outposts": {
|
||||
"container_image_base": "ghcr.io/goauthentik/dev-%(type)s:gh-%(build_hash)s",
|
||||
"disable_embedded_outpost": False,
|
||||
},
|
||||
"blueprints_dir": "./blueprints",
|
||||
"cert_discovery_dir": "./certs",
|
||||
"events": {
|
||||
"processors": {
|
||||
"geoip": "tests/GeoLite2-City-Test.mmdb",
|
||||
"asn": "tests/GeoLite2-ASN-Test.mmdb",
|
||||
}
|
||||
},
|
||||
"storage": {
|
||||
"media": {
|
||||
"backend": "file",
|
||||
"s3": {
|
||||
"endpoint": "http://localhost:8020",
|
||||
"access_key": "accessKey1",
|
||||
"secret_key": "secretKey1",
|
||||
"bucket_name": "authentik-media",
|
||||
"custom_domain": "localhost:8020/authentik-media",
|
||||
"secure_urls": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
"tenants": {
|
||||
"enabled": False,
|
||||
"api_key": generate_id(),
|
||||
},
|
||||
},
|
||||
"tenants": {
|
||||
"enabled": False,
|
||||
"api_key": generate_id(),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
config_file_name = "local.env.yml"
|
||||
|
||||
with open(config_file_name, "w", encoding="utf-8") as _config:
|
||||
_config.write(
|
||||
"""
|
||||
# Local authentik configuration overrides
|
||||
#
|
||||
# https://docs.goauthentik.io/docs/install-config/configuration/
|
||||
#
|
||||
# To regenerate this file, run the following command from the repository root:
|
||||
#
|
||||
# ```shell
|
||||
# make gen-dev-config
|
||||
# ```
|
||||
|
||||
"""
|
||||
)
|
||||
|
||||
safe_dump(
|
||||
generate_local_config(),
|
||||
_config,
|
||||
default_flow_style=False,
|
||||
)
|
||||
|
||||
print(
|
||||
f"""
|
||||
---
|
||||
|
||||
Generated configuration file: {config_file_name}
|
||||
|
||||
For more information on how to use this configuration, see:
|
||||
|
||||
https://docs.goauthentik.io/docs/install-config/configuration/
|
||||
|
||||
---
|
||||
"""
|
||||
_config,
|
||||
default_flow_style=False,
|
||||
)
|
||||
|
||||
26
tsconfig.json
Normal file
26
tsconfig.json
Normal file
@ -0,0 +1,26 @@
|
||||
// TypeScript Project Configuration
|
||||
{
|
||||
"watchOptions": {
|
||||
"excludeDirectories": [
|
||||
"**/.git", // Git
|
||||
"**/.yarn", // Yarn
|
||||
"**/.vscode", // VS Code
|
||||
"**/.vscode-test-web", // VS Code Web Test
|
||||
"**/dist", // Distributed build files
|
||||
"**/out", // Output build files
|
||||
"**/.drafts", // Drafts
|
||||
"**/.github", // GitHub
|
||||
"**/node_modules" // Node modules
|
||||
]
|
||||
},
|
||||
|
||||
// The root project has no sources of its own. By setting `files` to an empty
|
||||
// list, TS won't automatically include all sources below root (the default).
|
||||
"files": [],
|
||||
"references": [
|
||||
// Note that references are in the order we want them to be built.
|
||||
{ "path": "./packages/prettier-config" },
|
||||
{ "path": "./packages/eslint-config" },
|
||||
{ "path": "./web" }
|
||||
]
|
||||
}
|
||||
11
uv.lock
generated
11
uv.lock
generated
@ -302,7 +302,7 @@ requires-dist = [
|
||||
{ name = "ldap3" },
|
||||
{ name = "lxml" },
|
||||
{ name = "msgraph-sdk" },
|
||||
{ name = "opencontainers", git = "https://github.com/BeryJu/oci-python?rev=c791b19056769cd67957322806809ab70f5bead8" },
|
||||
{ name = "opencontainers", git = "https://github.com/vsoch/oci-python?rev=20d69d9cc50a0fef31605b46f06da0c94f1ec3cf" },
|
||||
{ name = "packaging" },
|
||||
{ name = "paramiko" },
|
||||
{ name = "psycopg", extras = ["c"] },
|
||||
@ -1216,12 +1216,9 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "durationpy"
|
||||
version = "0.9"
|
||||
version = "0.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/31/e9/f49c4e7fccb77fa5c43c2480e09a857a78b41e7331a75e128ed5df45c56b/durationpy-0.9.tar.gz", hash = "sha256:fd3feb0a69a0057d582ef643c355c40d2fa1c942191f914d12203b1a01ac722a", size = 3186 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/a3/ac312faeceffd2d8f86bc6dcb5c401188ba5a01bc88e69bed97578a0dfcd/durationpy-0.9-py3-none-any.whl", hash = "sha256:e65359a7af5cedad07fb77a2dd3f390f8eb0b74cb845589fa6c057086834dd38", size = 3461 },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8b/b8/074abdcc251bec87da6c5b19b88d7898ec7996c6780d40c6ac5000d3dd47/durationpy-0.7.tar.gz", hash = "sha256:8447c43df4f1a0b434e70c15a38d77f5c9bd17284bfc1ff1d430f233d5083732", size = 3168 }
|
||||
|
||||
[[package]]
|
||||
name = "email-validator"
|
||||
@ -2078,7 +2075,7 @@ wheels = [
|
||||
[[package]]
|
||||
name = "opencontainers"
|
||||
version = "0.0.14"
|
||||
source = { git = "https://github.com/BeryJu/oci-python?rev=c791b19056769cd67957322806809ab70f5bead8#c791b19056769cd67957322806809ab70f5bead8" }
|
||||
source = { git = "https://github.com/vsoch/oci-python?rev=20d69d9cc50a0fef31605b46f06da0c94f1ec3cf#20d69d9cc50a0fef31605b46f06da0c94f1ec3cf" }
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry-api"
|
||||
|
||||
10
web/.gitignore
vendored
10
web/.gitignore
vendored
@ -78,13 +78,8 @@ typings/
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Uncomment the public line if your project uses Gatsby
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# https://create-react-app.dev/docs/using-the-public-folder/#docsNav
|
||||
# public
|
||||
|
||||
# Storybook build outputs
|
||||
.out
|
||||
@ -109,8 +104,3 @@ temp/
|
||||
# End of https://www.gitignore.io/api/node
|
||||
api/**
|
||||
storybook-static/
|
||||
|
||||
# Wireit's cache
|
||||
.wireit
|
||||
|
||||
custom-elements.json
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
# don't ever lint node_modules
|
||||
node_modules
|
||||
# don't lint build output (make sure it's set to your correct build folder name)
|
||||
dist
|
||||
# don't lint nyc coverage output
|
||||
coverage
|
||||
# Import order matters
|
||||
poly.ts
|
||||
src/locale-codes.ts
|
||||
src/locales/
|
||||
storybook-static/
|
||||
# Prettier breaks the tsconfig file
|
||||
tsconfig.json
|
||||
.storybook/css-import-maps*
|
||||
package.json
|
||||
packages/**/package.json
|
||||
@ -1,23 +0,0 @@
|
||||
{
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"embeddedLanguageFormatting": "auto",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"insertPragma": false,
|
||||
"jsxSingleQuote": false,
|
||||
"printWidth": 100,
|
||||
"proseWrap": "preserve",
|
||||
"quoteProps": "consistent",
|
||||
"requirePragma": false,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "all",
|
||||
"useTabs": false,
|
||||
"vueIndentScriptAndStyle": false,
|
||||
"plugins": ["@trivago/prettier-plugin-sort-imports"],
|
||||
"importOrder": ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
|
||||
"importOrderSeparation": true,
|
||||
"importOrderSortSpecifiers": true,
|
||||
"importOrderParserPlugins": ["typescript", "jsx", "classProperties", "decorators-legacy"]
|
||||
}
|
||||
@ -1,13 +1,16 @@
|
||||
import replace from "@rollup/plugin-replace";
|
||||
import type { StorybookConfig } from "@storybook/web-components-vite";
|
||||
import { cwd } from "process";
|
||||
import { cwd } from "node:process";
|
||||
import modify from "rollup-plugin-modify";
|
||||
import postcssLit from "rollup-plugin-postcss-lit";
|
||||
import { mergeConfig } from "vite";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
|
||||
export const isProdBuild = process.env.NODE_ENV === "production";
|
||||
export const apiBasePath = process.env.AK_API_BASE_PATH || "";
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV || "development";
|
||||
const AK_API_BASE_PATH = process.env.AK_API_BASE_PATH || "";
|
||||
|
||||
const importInlinePatterns = [
|
||||
'import AKGlobal from "(\\.\\./)*common/styles/authentik\\.css',
|
||||
'import AKGlobal from "@goauthentik/common/styles/authentik\\.css',
|
||||
@ -53,8 +56,14 @@ const config: StorybookConfig = {
|
||||
autodocs: "tag",
|
||||
},
|
||||
async viteFinal(config) {
|
||||
return {
|
||||
...config,
|
||||
return mergeConfig(config, {
|
||||
define: {
|
||||
"process.env.NODE_ENV": JSON.stringify(NODE_ENV),
|
||||
"process.env.CWD": JSON.stringify(cwd()),
|
||||
"process.env.AK_API_BASE_PATH": JSON.stringify(AK_API_BASE_PATH),
|
||||
"process.env.WATCHER_URL": "",
|
||||
},
|
||||
|
||||
plugins: [
|
||||
modify({
|
||||
find: importInlineRegexp,
|
||||
@ -62,19 +71,10 @@ const config: StorybookConfig = {
|
||||
return `${match}?inline`;
|
||||
},
|
||||
}),
|
||||
replace({
|
||||
"process.env.NODE_ENV": JSON.stringify(
|
||||
isProdBuild ? "production" : "development",
|
||||
),
|
||||
"process.env.CWD": JSON.stringify(cwd()),
|
||||
"process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath),
|
||||
"preventAssignment": true,
|
||||
}),
|
||||
...config.plugins,
|
||||
postcssLit(),
|
||||
tsconfigPaths(),
|
||||
],
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
import eslint from "@eslint/js";
|
||||
import tsparser from "@typescript-eslint/parser";
|
||||
import litconf from "eslint-plugin-lit";
|
||||
import wcconf from "eslint-plugin-wc";
|
||||
import globals from "globals";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default [
|
||||
// You would not believe how much this change has frustrated users: ["if an ignores key is used
|
||||
// without any other keys in the configuration object, then the patterns act as global
|
||||
// ignores"](https://eslint.org/docs/latest/use/configure/ignore)
|
||||
{
|
||||
ignores: [
|
||||
"dist/",
|
||||
// don't lint the cache
|
||||
".wireit/",
|
||||
// let packages have their own configurations
|
||||
"packages/",
|
||||
// don't ever lint node_modules
|
||||
"node_modules/",
|
||||
".storybook/*",
|
||||
// don't lint build output (make sure it's set to your correct build folder name)
|
||||
// don't lint nyc coverage output
|
||||
"coverage/",
|
||||
"src/locale-codes.ts",
|
||||
"storybook-static/",
|
||||
"src/locales/",
|
||||
],
|
||||
},
|
||||
eslint.configs.recommended,
|
||||
wcconf.configs["flat/recommended"],
|
||||
litconf.configs["flat/recommended"],
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
languageOptions: {
|
||||
parser: tsparser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
sourceType: "module",
|
||||
},
|
||||
},
|
||||
files: ["src/**"],
|
||||
rules: {
|
||||
"lit/attribute-names": "off",
|
||||
// "lit/attribute-names": "error",
|
||||
"lit/no-private-properties": "error",
|
||||
// "lit/prefer-nothing": "warn",
|
||||
"lit/no-template-bind": "error",
|
||||
"no-unused-vars": "off",
|
||||
"no-console": ["error", { allow: ["debug", "warn", "error"] }],
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
parser: tsparser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 12,
|
||||
sourceType: "module",
|
||||
},
|
||||
globals: {
|
||||
...globals.nodeBuiltin,
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
files: ["scripts/**/*.mjs", "*.ts", "*.mjs"],
|
||||
rules: {
|
||||
"no-unused-vars": "off",
|
||||
// We WANT our scripts to output to the console!
|
||||
"no-console": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
6
web/package-lock.json
generated
6
web/package-lock.json
generated
@ -24760,9 +24760,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.4.15",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.15.tgz",
|
||||
"integrity": "sha512-6ANcZRivqL/4WtwPGTKNaosuNJr5tWiftOC7liM7G9+rMb8+oeJeyzymDu4rTN93seySBmbjSfsS3Vzr19KNtA==",
|
||||
"version": "5.4.14",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz",
|
||||
"integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
272
web/package.json
272
web/package.json
@ -1,138 +1,9 @@
|
||||
{
|
||||
"name": "@goauthentik/web",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@codemirror/lang-css": "^6.3.1",
|
||||
"@codemirror/lang-html": "^6.4.9",
|
||||
"@codemirror/lang-javascript": "^6.2.2",
|
||||
"@codemirror/lang-python": "^6.1.6",
|
||||
"@codemirror/lang-xml": "^6.1.0",
|
||||
"@codemirror/legacy-modes": "^6.4.1",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@floating-ui/dom": "^1.6.11",
|
||||
"@formatjs/intl-listformat": "^7.5.7",
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@goauthentik/api": "^2025.2.2-1742585853",
|
||||
"@lit-labs/ssr": "^3.2.2",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@lit/localize": "^0.12.2",
|
||||
"@lit/reactive-element": "^2.0.4",
|
||||
"@lit/task": "^1.0.1",
|
||||
"@mdx-js/mdx": "^3.1.0",
|
||||
"@open-wc/lit-helpers": "^0.7.0",
|
||||
"@patternfly/elements": "^4.0.2",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@spotlightjs/spotlight": "^2.4.2",
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"change-case": "^5.4.4",
|
||||
"chart.js": "^4.4.4",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"construct-style-sheets-polyfill": "^3.1.0",
|
||||
"core-js": "^3.38.1",
|
||||
"country-flag-icons": "^1.5.13",
|
||||
"date-fns": "^4.1.0",
|
||||
"deepmerge-ts": "^7.1.5",
|
||||
"dompurify": "^3.2.4",
|
||||
"fuse.js": "^7.0.0",
|
||||
"guacamole-common-js": "^1.5.0",
|
||||
"hastscript": "^9.0.1",
|
||||
"lit": "^3.2.0",
|
||||
"md-front-matter": "^1.0.4",
|
||||
"mermaid": "^11.4.1",
|
||||
"rapidoc": "^9.3.7",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"rehype-highlight": "^7.0.2",
|
||||
"rehype-mermaid": "^3.0.0",
|
||||
"rehype-parse": "^9.0.1",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
"remark-directive": "^4.0.0",
|
||||
"remark-frontmatter": "^5.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-mdx-frontmatter": "^5.0.0",
|
||||
"style-mod": "^4.1.2",
|
||||
"ts-pattern": "^5.4.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"webcomponent-qr-code": "^1.2.0",
|
||||
"yaml": "^2.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.11.1",
|
||||
"@hcaptcha/types": "^1.0.4",
|
||||
"@lit/localize-tools": "^0.8.0",
|
||||
"@rollup/plugin-replace": "^6.0.1",
|
||||
"@storybook/addon-essentials": "^8.3.4",
|
||||
"@storybook/addon-links": "^8.3.4",
|
||||
"@storybook/api": "^7.6.17",
|
||||
"@storybook/blocks": "^8.3.4",
|
||||
"@storybook/builder-vite": "^8.3.4",
|
||||
"@storybook/manager-api": "^8.3.4",
|
||||
"@storybook/web-components": "^8.3.4",
|
||||
"@storybook/web-components-vite": "^8.3.4",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@types/chart.js": "^2.9.41",
|
||||
"@types/codemirror": "^5.60.15",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/eslint__js": "^8.42.3",
|
||||
"@types/grecaptcha": "^3.0.9",
|
||||
"@types/guacamole-common-js": "^1.5.2",
|
||||
"@types/mocha": "^10.0.8",
|
||||
"@types/node": "^22.7.4",
|
||||
"@types/react": "^18.3.13",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.8.0",
|
||||
"@typescript-eslint/parser": "^8.8.0",
|
||||
"@wdio/browser-runner": "9.4",
|
||||
"@wdio/cli": "9.4",
|
||||
"@wdio/spec-reporter": "^9.1.2",
|
||||
"chromedriver": "^131.0.1",
|
||||
"esbuild": "^0.25.0",
|
||||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||
"esbuild-plugins-node-modules-polyfill": "^1.7.0",
|
||||
"eslint": "^9.11.1",
|
||||
"eslint-plugin-lit": "^1.15.0",
|
||||
"eslint-plugin-wc": "^2.1.1",
|
||||
"find-free-ports": "^3.1.1",
|
||||
"github-slugger": "^2.0.0",
|
||||
"glob": "^11.0.0",
|
||||
"globals": "^15.10.0",
|
||||
"knip": "^5.30.6",
|
||||
"lit-analyzer": "^2.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.3.3",
|
||||
"pseudolocale": "^2.1.0",
|
||||
"rollup-plugin-modify": "^3.0.0",
|
||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||
"storybook": "^8.3.4",
|
||||
"storybook-addon-mock": "^5.0.0",
|
||||
"syncpack": "^13.0.0",
|
||||
"turnstile-types": "^1.2.3",
|
||||
"typescript": "^5.6.2",
|
||||
"typescript-eslint": "^8.8.0",
|
||||
"vite-plugin-lit-css": "^2.0.0",
|
||||
"vite-tsconfig-paths": "^5.0.1",
|
||||
"wireit": "^0.14.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"license": "MIT",
|
||||
"optionalDependencies": {
|
||||
"@esbuild/darwin-arm64": "^0.24.0",
|
||||
"@esbuild/linux-amd64": "^0.18.11",
|
||||
"@esbuild/linux-arm64": "^0.24.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.23.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.23.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.23.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "wireit",
|
||||
"build-locales": "wireit",
|
||||
"build-locales:build": "wireit",
|
||||
"build-proxy": "wireit",
|
||||
"build:sfe": "wireit",
|
||||
"esbuild:watch": "node scripts/build-web.mjs --watch",
|
||||
@ -160,7 +31,138 @@
|
||||
"tsc": "wireit",
|
||||
"watch": "run-s build-locales esbuild:watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@goauthentik/monorepo": "1.0.0",
|
||||
"@hcaptcha/types": "^1.0.4",
|
||||
"@lit/localize-tools": "^0.8.0",
|
||||
"@rollup/plugin-replace": "^6.0.1",
|
||||
"@storybook/addon-essentials": "^8.3.4",
|
||||
"@storybook/addon-links": "^8.3.4",
|
||||
"@storybook/api": "^7.6.17",
|
||||
"@storybook/blocks": "^8.3.4",
|
||||
"@storybook/builder-vite": "^8.3.4",
|
||||
"@storybook/manager-api": "^8.3.4",
|
||||
"@storybook/web-components": "^8.3.4",
|
||||
"@storybook/web-components-vite": "^8.3.4",
|
||||
"@swc/helpers": "^0.5.15",
|
||||
"@types/chart.js": "^2.9.41",
|
||||
"@types/codemirror": "^5.60.15",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/grecaptcha": "^3.0.9",
|
||||
"@types/guacamole-common-js": "^1.5.2",
|
||||
"@types/mocha": "^10.0.8",
|
||||
"@types/node": "^22.7.4",
|
||||
"@types/react": "^18.3.13",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@wdio/browser-runner": "9.12.1",
|
||||
"@wdio/cli": "9.12.1",
|
||||
"@wdio/spec-reporter": "^9.11.0",
|
||||
"chromedriver": "^134.0.5",
|
||||
"esbuild": "^0.25.0",
|
||||
"esbuild-plugin-copy": "^2.1.1",
|
||||
"esbuild-plugin-es5": "^2.1.1",
|
||||
"esbuild-plugin-polyfill-node": "^0.3.0",
|
||||
"esbuild-plugins-node-modules-polyfill": "^1.7.0",
|
||||
"find-free-ports": "^3.1.1",
|
||||
"github-slugger": "^2.0.0",
|
||||
"glob": "^11.0.0",
|
||||
"globals": "^15.10.0",
|
||||
"knip": "^5.30.6",
|
||||
"lit-analyzer": "^2.0.3",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"pseudolocale": "^2.1.0",
|
||||
"rollup-plugin-modify": "^3.0.0",
|
||||
"rollup-plugin-postcss-lit": "^2.1.0",
|
||||
"storybook": "^8.3.4",
|
||||
"storybook-addon-mock": "^5.0.0",
|
||||
"syncpack": "^13.0.0",
|
||||
"turnstile-types": "^1.2.3",
|
||||
"vite-plugin-lit-css": "^2.0.0",
|
||||
"vite-tsconfig-paths": "^5.0.1",
|
||||
"wireit": "^0.14.9",
|
||||
"zx": "^8.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/lang-css": "^6.3.1",
|
||||
"@codemirror/lang-html": "^6.4.9",
|
||||
"@codemirror/lang-javascript": "^6.2.2",
|
||||
"@codemirror/lang-python": "^6.1.6",
|
||||
"@codemirror/lang-xml": "^6.1.0",
|
||||
"@codemirror/legacy-modes": "^6.4.1",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@floating-ui/dom": "^1.6.11",
|
||||
"@formatjs/intl-listformat": "^7.5.7",
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@goauthentik/api": "^2025.2.2-1742585853",
|
||||
"@lit-labs/ssr": "^3.2.2",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@lit/localize": "^0.12.2",
|
||||
"@lit/reactive-element": "^2.0.4",
|
||||
"@lit/task": "^1.0.1",
|
||||
"@mdx-js/mdx": "^3.1.0",
|
||||
"@open-wc/lit-helpers": "^0.7.0",
|
||||
"@patternfly/elements": "^4.0.2",
|
||||
"@patternfly/patternfly": "^4.224.2",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@spotlightjs/spotlight": "^2.4.2",
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"bootstrap": "^4.6.1",
|
||||
"change-case": "^5.4.4",
|
||||
"chart.js": "^4.4.4",
|
||||
"chartjs-adapter-date-fns": "^3.0.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"construct-style-sheets-polyfill": "^3.1.0",
|
||||
"core-js": "^3.38.1",
|
||||
"country-flag-icons": "^1.5.13",
|
||||
"date-fns": "^4.1.0",
|
||||
"deepmerge-ts": "^7.1.5",
|
||||
"dompurify": "^3.2.4",
|
||||
"formdata-polyfill": "^4.0.10",
|
||||
"fuse.js": "^7.0.0",
|
||||
"guacamole-common-js": "^1.5.0",
|
||||
"hastscript": "^9.0.1",
|
||||
"jquery": "^3.7.1",
|
||||
"lit": "^3.2.0",
|
||||
"md-front-matter": "^1.0.4",
|
||||
"mermaid": "^11.4.1",
|
||||
"rapidoc": "^9.3.7",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"rehype-highlight": "^7.0.2",
|
||||
"rehype-mermaid": "^3.0.0",
|
||||
"rehype-parse": "^9.0.1",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
"remark-directive": "^4.0.0",
|
||||
"remark-frontmatter": "^5.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"remark-mdx-frontmatter": "^5.0.0",
|
||||
"style-mod": "^4.1.2",
|
||||
"ts-pattern": "^5.4.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"weakmap-polyfill": "^2.0.4",
|
||||
"webcomponent-qr-code": "^1.2.0",
|
||||
"yaml": "^2.5.1"
|
||||
},
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
"./paths": "./paths.js",
|
||||
"./scripts/*": "./scripts/*.mjs"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/darwin-arm64": "^0.24.0",
|
||||
"@esbuild/linux-amd64": "^0.18.11",
|
||||
"@esbuild/linux-arm64": "^0.24.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.23.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.23.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"wireit": {
|
||||
"build": {
|
||||
"#comment": [
|
||||
@ -217,12 +219,6 @@
|
||||
"build-locales"
|
||||
]
|
||||
},
|
||||
"build-locales:build": {
|
||||
"command": "lit-localize build"
|
||||
},
|
||||
"build-locales:repair": {
|
||||
"command": "prettier --write ./src/locale-codes.ts"
|
||||
},
|
||||
"build-locales": {
|
||||
"command": "node scripts/build-locales.mjs",
|
||||
"files": [
|
||||
@ -376,9 +372,5 @@
|
||||
"lint:types"
|
||||
]
|
||||
}
|
||||
},
|
||||
"workspaces": [
|
||||
".",
|
||||
"./packages/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
{
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"embeddedLanguageFormatting": "auto",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"insertPragma": false,
|
||||
"jsxSingleQuote": false,
|
||||
"printWidth": 100,
|
||||
"proseWrap": "preserve",
|
||||
"quoteProps": "consistent",
|
||||
"requirePragma": false,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "all",
|
||||
"useTabs": false,
|
||||
"vueIndentScriptAndStyle": false,
|
||||
"plugins": ["@trivago/prettier-plugin-sort-imports"],
|
||||
"importOrder": ["^(@?)lit(.*)$", "\\.css$", "^@goauthentik/api$", "^[./]"],
|
||||
"importOrderSeparation": true,
|
||||
"importOrderSortSpecifiers": true,
|
||||
"importOrderParserPlugins": ["typescript", "classProperties", "decorators-legacy"]
|
||||
}
|
||||
@ -1,3 +1,6 @@
|
||||
/**
|
||||
* @file Simple Flow Executor entry point.
|
||||
*/
|
||||
import { fromByteArray } from "base64-js";
|
||||
import "formdata-polyfill";
|
||||
import $ from "jquery";
|
||||
@ -33,81 +36,6 @@ function ak(): GlobalAuthentik {
|
||||
).authentik;
|
||||
}
|
||||
|
||||
class SimpleFlowExecutor {
|
||||
challenge?: ChallengeTypes;
|
||||
flowSlug: string;
|
||||
container: HTMLDivElement;
|
||||
|
||||
constructor(container: HTMLDivElement) {
|
||||
this.flowSlug = window.location.pathname.split("/")[3];
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
get apiURL() {
|
||||
return `${ak().api.base}api/v3/flows/executor/${this.flowSlug}/?query=${encodeURIComponent(window.location.search.substring(1))}`;
|
||||
}
|
||||
|
||||
start() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: this.apiURL,
|
||||
success: (data) => {
|
||||
this.challenge = ChallengeTypesFromJSON(data);
|
||||
this.renderChallenge();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
submit(data: { [key: string]: unknown } | FormData) {
|
||||
$("button[type=submit]").addClass("disabled")
|
||||
.html(`<span class="spinner-border spinner-border-sm" aria-hidden="true"></span>
|
||||
<span role="status">Loading...</span>`);
|
||||
let finalData: { [key: string]: unknown } = {};
|
||||
if (data instanceof FormData) {
|
||||
finalData = {};
|
||||
data.forEach((value, key) => {
|
||||
finalData[key] = value;
|
||||
});
|
||||
} else {
|
||||
finalData = data;
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: this.apiURL,
|
||||
data: JSON.stringify(finalData),
|
||||
success: (data) => {
|
||||
this.challenge = ChallengeTypesFromJSON(data);
|
||||
this.renderChallenge();
|
||||
},
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
});
|
||||
}
|
||||
|
||||
renderChallenge() {
|
||||
switch (this.challenge?.component) {
|
||||
case "ak-stage-identification":
|
||||
new IdentificationStage(this, this.challenge).render();
|
||||
return;
|
||||
case "ak-stage-password":
|
||||
new PasswordStage(this, this.challenge).render();
|
||||
return;
|
||||
case "xak-flow-redirect":
|
||||
new RedirectStage(this, this.challenge).render();
|
||||
return;
|
||||
case "ak-stage-autosubmit":
|
||||
new AutosubmitStage(this, this.challenge).render();
|
||||
return;
|
||||
case "ak-stage-authenticator-validate":
|
||||
new AuthenticatorValidateStage(this, this.challenge).render();
|
||||
return;
|
||||
default:
|
||||
this.container.innerText = "Unsupported stage: " + this.challenge?.component;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface FlowInfoChallenge {
|
||||
flowInfo?: ContextualFlowInfo;
|
||||
responseErrors?: {
|
||||
@ -160,7 +88,7 @@ class Stage<T extends FlowInfoChallenge> {
|
||||
const IS_INVALID = "is-invalid";
|
||||
|
||||
class IdentificationStage extends Stage<IdentificationChallenge> {
|
||||
render() {
|
||||
override render() {
|
||||
this.html(`
|
||||
<form id="ident-form">
|
||||
<img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
|
||||
@ -270,10 +198,10 @@ export interface AuthAssertion {
|
||||
}
|
||||
|
||||
class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge> {
|
||||
deviceChallenge?: DeviceChallenge;
|
||||
declare deviceChallenge?: DeviceChallenge;
|
||||
|
||||
b64enc(buf: Uint8Array): string {
|
||||
return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
||||
return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/[=]/g, "");
|
||||
}
|
||||
|
||||
b64RawEnc(buf: Uint8Array): string {
|
||||
@ -392,8 +320,10 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
|
||||
render() {
|
||||
if (!this.deviceChallenge) {
|
||||
return this.renderChallengePicker();
|
||||
this.renderChallengePicker();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this.deviceChallenge.deviceClass) {
|
||||
case "static":
|
||||
case "totp":
|
||||
@ -523,5 +453,87 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
}
|
||||
}
|
||||
|
||||
const sfe = new SimpleFlowExecutor($("#flow-sfe-container")[0] as HTMLDivElement);
|
||||
class SimpleFlowExecutor {
|
||||
challenge?: ChallengeTypes;
|
||||
flowSlug: string;
|
||||
container: HTMLDivElement;
|
||||
|
||||
constructor(container: HTMLDivElement) {
|
||||
this.flowSlug = window.location.pathname.split("/")[3] || "";
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
get apiURL() {
|
||||
return `${ak().api.base}api/v3/flows/executor/${this.flowSlug}/?query=${encodeURIComponent(window.location.search.substring(1))}`;
|
||||
}
|
||||
|
||||
start() {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: this.apiURL,
|
||||
success: (data) => {
|
||||
this.challenge = ChallengeTypesFromJSON(data);
|
||||
this.renderChallenge();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
submit(payload: { [key: string]: unknown } | FormData) {
|
||||
$("button[type=submit]").addClass("disabled")
|
||||
.html(`<span class="spinner-border spinner-border-sm" aria-hidden="true"></span>
|
||||
<span role="status">Loading...</span>`);
|
||||
let finalData: { [key: string]: unknown } = {};
|
||||
if (payload instanceof FormData) {
|
||||
finalData = {};
|
||||
payload.forEach((value, key) => {
|
||||
finalData[key] = value;
|
||||
});
|
||||
} else {
|
||||
finalData = payload;
|
||||
}
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: this.apiURL,
|
||||
data: JSON.stringify(finalData),
|
||||
success: (data) => {
|
||||
this.challenge = ChallengeTypesFromJSON(data);
|
||||
this.renderChallenge();
|
||||
},
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
});
|
||||
}
|
||||
|
||||
renderChallenge() {
|
||||
switch (this.challenge?.component) {
|
||||
case "ak-stage-identification":
|
||||
new IdentificationStage(this, this.challenge).render();
|
||||
return;
|
||||
case "ak-stage-password":
|
||||
new PasswordStage(this, this.challenge).render();
|
||||
return;
|
||||
case "xak-flow-redirect":
|
||||
new RedirectStage(this, this.challenge).render();
|
||||
return;
|
||||
case "ak-stage-autosubmit":
|
||||
new AutosubmitStage(this, this.challenge).render();
|
||||
return;
|
||||
case "ak-stage-authenticator-validate":
|
||||
new AuthenticatorValidateStage(this, this.challenge).render();
|
||||
return;
|
||||
default:
|
||||
this.container.innerText = `Unsupported stage: ${this.challenge?.component}`;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [flowContainer] = $<HTMLDivElement>("#flow-sfe-container");
|
||||
|
||||
if (!flowContainer) {
|
||||
throw new Error("No flow container element found");
|
||||
}
|
||||
|
||||
const sfe = new SimpleFlowExecutor(flowContainer);
|
||||
|
||||
sfe.start();
|
||||
@ -1,68 +0,0 @@
|
||||
{
|
||||
"name": "@goauthentik/web-sfe",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@goauthentik/api": "^2024.6.0-1719577139",
|
||||
"base64-js": "^1.5.1",
|
||||
"bootstrap": "^4.6.1",
|
||||
"formdata-polyfill": "^4.0.10",
|
||||
"jquery": "^3.7.1",
|
||||
"weakmap-polyfill": "^2.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^28.0.0",
|
||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||
"@rollup/plugin-swc": "^0.4.0",
|
||||
"@swc/cli": "^0.4.0",
|
||||
"@swc/core": "^1.7.28",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@types/jquery": "^3.5.31",
|
||||
"lockfile-lint": "^4.14.0",
|
||||
"prettier": "^3.3.2",
|
||||
"rollup": "^4.23.0",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"wireit": "^0.14.9"
|
||||
},
|
||||
"license": "MIT",
|
||||
"optionalDependencies": {
|
||||
"@swc/core": "^1.7.28",
|
||||
"@swc/core-darwin-arm64": "^1.6.13",
|
||||
"@swc/core-darwin-x64": "^1.6.13",
|
||||
"@swc/core-linux-arm-gnueabihf": "^1.6.13",
|
||||
"@swc/core-linux-arm64-gnu": "^1.6.13",
|
||||
"@swc/core-linux-arm64-musl": "^1.6.13",
|
||||
"@swc/core-linux-x64-gnu": "^1.6.13",
|
||||
"@swc/core-linux-x64-musl": "^1.6.13",
|
||||
"@swc/core-win32-arm64-msvc": "^1.6.13",
|
||||
"@swc/core-win32-ia32-msvc": "^1.6.13",
|
||||
"@swc/core-win32-x64-msvc": "^1.6.13"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "wireit",
|
||||
"lint:lockfile": "wireit",
|
||||
"prettier": "prettier --write ./src ./tsconfig.json ./rollup.config.js ./package.json",
|
||||
"watch": "rollup -w -c rollup.config.js --bundleConfigAsCjs"
|
||||
},
|
||||
"wireit": {
|
||||
"build:sfe": {
|
||||
"command": "rollup -c rollup.config.js --bundleConfigAsCjs",
|
||||
"files": [
|
||||
"../../node_modules/bootstrap/dist/css/bootstrap.min.css",
|
||||
"src/index.ts"
|
||||
],
|
||||
"output": [
|
||||
"./dist/sfe/*"
|
||||
]
|
||||
},
|
||||
"build": {
|
||||
"command": "mkdir -p ../../dist/sfe && cp -r dist/sfe/* ../../dist/sfe",
|
||||
"dependencies": [
|
||||
"build:sfe"
|
||||
]
|
||||
},
|
||||
"lint:lockfile": {
|
||||
"command": "lockfile-lint --path package.json --type npm --allowed-hosts npm --validate-https"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
import commonjs from "@rollup/plugin-commonjs";
|
||||
import resolve from "@rollup/plugin-node-resolve";
|
||||
import swc from "@rollup/plugin-swc";
|
||||
import copy from "rollup-plugin-copy";
|
||||
|
||||
export default {
|
||||
input: "src/index.ts",
|
||||
output: {
|
||||
dir: "./dist/sfe",
|
||||
format: "cjs",
|
||||
},
|
||||
context: "window",
|
||||
plugins: [
|
||||
copy({
|
||||
targets: [
|
||||
{
|
||||
src: "../../node_modules/bootstrap/dist/css/bootstrap.min.css",
|
||||
dest: "./dist/sfe",
|
||||
},
|
||||
],
|
||||
}),
|
||||
resolve({ browser: true }),
|
||||
commonjs(),
|
||||
swc({
|
||||
swc: {
|
||||
jsc: {
|
||||
loose: false,
|
||||
externalHelpers: false,
|
||||
// Requires v1.2.50 or upper and requires target to be es2016 or upper.
|
||||
keepClassNames: false,
|
||||
},
|
||||
minify: false,
|
||||
env: {
|
||||
targets: {
|
||||
edge: "17",
|
||||
ie: "11",
|
||||
},
|
||||
mode: "entry",
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
@ -1,6 +1,8 @@
|
||||
{
|
||||
"extends": "@goauthentik/tsconfig",
|
||||
"compilerOptions": {
|
||||
"types": ["jquery"],
|
||||
"emitDeclarationOnly": true,
|
||||
"baseUrl": ".",
|
||||
"esModuleInterop": true,
|
||||
"lib": ["DOM", "ES2015", "ES2017"]
|
||||
}
|
||||
|
||||
22
web/paths.js
Normal file
22
web/paths.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/**
|
||||
* @typedef {'@goauthentik/web'} WebPackageIdentifier
|
||||
*/
|
||||
|
||||
/**
|
||||
* The root of the web package.
|
||||
*/
|
||||
export const PackageRoot = /** @type {WebPackageIdentifier} */ (resolve(__dirname));
|
||||
|
||||
/**
|
||||
* Path to the web package's distribution directory.
|
||||
*
|
||||
* This is where the built files are located after running the build process.
|
||||
*/
|
||||
export const DistDirectory = /** @type {`${WebPackageIdentifier}/dist`} */ (
|
||||
resolve(__dirname, "dist")
|
||||
);
|
||||
@ -1,72 +1,103 @@
|
||||
import { spawnSync } from "child_process";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import process from "process";
|
||||
/**
|
||||
* @file Lit Localize build script.
|
||||
*
|
||||
* @import { Config } from "@lit/localize-tools/lib/types/config.js"
|
||||
*/
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
import process from "node:process";
|
||||
import { $ } from "zx";
|
||||
|
||||
const localizeRules = await import("../lit-localize.json", {
|
||||
with: {
|
||||
type: "json",
|
||||
},
|
||||
})
|
||||
.then((module) => {
|
||||
return /** @type {Config} */ (module.default);
|
||||
})
|
||||
|
||||
.catch((error) => {
|
||||
console.error("Failed to load lit-localize.json", error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
/**
|
||||
* Determines if all the Xliff translation source files are present and if the Typescript source
|
||||
* files generated from those sources are up-to-date. If they are not, it runs the locale building
|
||||
* script, intercepting the long spew of "this string is not translated" and replacing it with a
|
||||
* summary of how many strings are missing with respect to the source locale.
|
||||
* Attempt to stat a file, returning null if it doesn't exist.
|
||||
*/
|
||||
function tryStat(filePath) {
|
||||
return fs.stat(filePath).catch(() => null);
|
||||
}
|
||||
|
||||
const localizeRules = JSON.parse(fs.readFileSync("./lit-localize.json", "utf-8"));
|
||||
/**
|
||||
* Check if a generated file is up-to-date with its XLIFF source.
|
||||
*
|
||||
* @param {string} languageCode The locale to check.
|
||||
*/
|
||||
async function generatedFileIsUpToDateWithXliffSource(languageCode) {
|
||||
const xlfFilePath = path.join("./xliff", `${languageCode}.xlf`);
|
||||
const xlfStat = await tryStat(xlfFilePath);
|
||||
|
||||
function generatedFileIsUpToDateWithXliffSource(loc) {
|
||||
const xliff = path.join("./xliff", `${loc}.xlf`);
|
||||
const gened = path.join("./src/locales", `${loc}.ts`);
|
||||
if (!xlfStat) {
|
||||
console.error(`lit-localize expected '${languageCode}.xlf', but XLF file is not present`);
|
||||
|
||||
// Returns false if: the expected XLF file doesn't exist, The expected
|
||||
// generated file doesn't exist, or the XLF file is newer (has a higher date)
|
||||
// than the generated file. The missing XLF file is important enough it
|
||||
// generates a unique error message and halts the build.
|
||||
|
||||
try {
|
||||
var xlfStat = fs.statSync(xliff);
|
||||
} catch (_error) {
|
||||
console.error(`lit-localize expected '${loc}.xlf', but XLF file is not present`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// If the generated file doesn't exist, of course it's not up to date.
|
||||
try {
|
||||
var genedStat = fs.statSync(gened);
|
||||
} catch (_error) {
|
||||
return false;
|
||||
const generatedTSFilePath = path.join("./src/locales", `${languageCode}.ts`);
|
||||
|
||||
const generatedTSFilePathStat = await tryStat(generatedTSFilePath);
|
||||
|
||||
// Does the generated file exist?
|
||||
if (!generatedTSFilePathStat) {
|
||||
return {
|
||||
languageCode,
|
||||
exists: false,
|
||||
expired: null,
|
||||
};
|
||||
}
|
||||
|
||||
// if the generated file is the same age or newer (date is greater) than the xliff file, it's
|
||||
// presumed to have been generated by that file and is up-to-date.
|
||||
return genedStat.mtimeMs >= xlfStat.mtimeMs;
|
||||
return {
|
||||
languageCode,
|
||||
exists: true,
|
||||
// Is the generated file older than the XLIFF file?
|
||||
expired: generatedTSFilePathStat.mtimeMs < xlfStat.mtimeMs,
|
||||
};
|
||||
}
|
||||
|
||||
// For all the expected files, find out if any aren't up-to-date.
|
||||
const upToDate = localizeRules.targetLocales.reduce(
|
||||
(acc, loc) => acc && generatedFileIsUpToDateWithXliffSource(loc),
|
||||
true,
|
||||
const results = await Promise.all(
|
||||
localizeRules.targetLocales.map(generatedFileIsUpToDateWithXliffSource),
|
||||
);
|
||||
|
||||
if (!upToDate) {
|
||||
const status = spawnSync("npm", ["run", "build-locales:build"], { encoding: "utf8" });
|
||||
const pendingBuild = results.some((result) => !result.exists || result.expired);
|
||||
|
||||
// Count all the missing message warnings
|
||||
const counts = status.stderr.split("\n").reduce((acc, line) => {
|
||||
const match = /^([\w-]+) message/.exec(line);
|
||||
if (!match) {
|
||||
return acc;
|
||||
}
|
||||
acc.set(match[1], (acc.get(match[1]) || 0) + 1);
|
||||
return acc;
|
||||
}, new Map());
|
||||
|
||||
const locales = Array.from(counts.keys());
|
||||
locales.sort();
|
||||
|
||||
const report = locales
|
||||
.map((locale) => `Locale '${locale}' has ${counts.get(locale)} missing translations`)
|
||||
.join("\n");
|
||||
|
||||
console.log(`Translation tables rebuilt.\n${report}\n`);
|
||||
if (!pendingBuild) {
|
||||
console.log("Local is up-to-date!");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log("Locale ./src is up-to-date");
|
||||
const status = await $({ stdio: ["ignore", "pipe", "pipe"] })`npx lit-localize build`;
|
||||
|
||||
/**
|
||||
* @type {Map<string, number>}
|
||||
*/
|
||||
const counts = new Map();
|
||||
|
||||
// Count all the missing message warnings
|
||||
for (const line of status.stderr.split("\n")) {
|
||||
const match = /^([\w-]+) message/.exec(line);
|
||||
if (!match) continue;
|
||||
|
||||
const count = counts.get(match[1]) || 0;
|
||||
counts.set(match[1], count + 1);
|
||||
}
|
||||
|
||||
const locales = Array.from(counts.keys()).sort();
|
||||
|
||||
for (const locale of locales) {
|
||||
console.log(`Locale '${locale}' has ${counts.get(locale)} missing translations`);
|
||||
}
|
||||
|
||||
await $`npx prettier --write src/locale-codes.ts`;
|
||||
|
||||
console.log("\nTranslation tables rebuilt.\n");
|
||||
|
||||
79
web/scripts/build-sfe.mjs
Normal file
79
web/scripts/build-sfe.mjs
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @file Build script for the simplified flow executor (SFE).
|
||||
*/
|
||||
import { DistDirectory, PackageRoot } from "@goauthentik/web/paths";
|
||||
import esbuild from "esbuild";
|
||||
import copy from "esbuild-plugin-copy";
|
||||
import { es5Plugin } from "esbuild-plugin-es5";
|
||||
import { createRequire } from "node:module";
|
||||
import * as path from "node:path";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
async function buildSFE() {
|
||||
const sourceDirectory = path.join(PackageRoot, "packages", "sfe");
|
||||
const outDirectory = path.join(DistDirectory, "sfe");
|
||||
|
||||
const bootstrapCSSPath = require.resolve(
|
||||
path.join("bootstrap", "dist", "css", "bootstrap.min.css"),
|
||||
);
|
||||
|
||||
/**
|
||||
* @type {esbuild.BuildOptions}
|
||||
*/
|
||||
const config = {
|
||||
tsconfig: path.join(sourceDirectory, "tsconfig.json"),
|
||||
entryPoints: [path.join(sourceDirectory, "index.ts")],
|
||||
minify: false,
|
||||
bundle: true,
|
||||
sourcemap: true,
|
||||
|
||||
legalComments: "external",
|
||||
platform: "browser",
|
||||
format: "iife",
|
||||
alias: {
|
||||
"@swc/helpers": path.dirname(require.resolve("@swc/helpers/package.json")),
|
||||
},
|
||||
banner: {
|
||||
js: [
|
||||
// ---
|
||||
"// Simplified Flow Executor (SFE)",
|
||||
"// @ts-nocheck",
|
||||
"",
|
||||
].join("\n"),
|
||||
},
|
||||
plugins: [
|
||||
copy({
|
||||
assets: [
|
||||
{
|
||||
from: bootstrapCSSPath,
|
||||
to: outDirectory,
|
||||
},
|
||||
],
|
||||
}),
|
||||
es5Plugin({
|
||||
swc: {
|
||||
jsc: {
|
||||
loose: false,
|
||||
externalHelpers: false,
|
||||
keepClassNames: false,
|
||||
},
|
||||
minify: false,
|
||||
},
|
||||
}),
|
||||
],
|
||||
target: ["es5"],
|
||||
outdir: outDirectory,
|
||||
};
|
||||
|
||||
esbuild.build(config);
|
||||
}
|
||||
|
||||
buildSFE()
|
||||
.then(() => {
|
||||
console.log("Build complete");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Build failed", error);
|
||||
process.exit(1);
|
||||
});
|
||||
@ -1,14 +1,14 @@
|
||||
import { execFileSync } from "child_process";
|
||||
import { deepmerge } from "deepmerge-ts";
|
||||
import esbuild from "esbuild";
|
||||
import { polyfillNode } from "esbuild-plugin-polyfill-node";
|
||||
import findFreePorts from "find-free-ports";
|
||||
import { copyFileSync, mkdirSync, readFileSync, statSync } from "fs";
|
||||
import { globSync } from "glob";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { cwd } from "node:process";
|
||||
import process from "node:process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import * as path from "path";
|
||||
import { cwd } from "process";
|
||||
import process from "process";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import { mdxPlugin } from "./esbuild/build-mdx-plugin.mjs";
|
||||
import { buildObserverPlugin } from "./esbuild/build-observer-plugin.mjs";
|
||||
@ -117,6 +117,7 @@ const BASE_ESBUILD_OPTIONS = {
|
||||
write: true,
|
||||
sourcemap: true,
|
||||
minify: NODE_ENV === "production",
|
||||
legalComments: "external",
|
||||
splitting: true,
|
||||
treeShaking: true,
|
||||
external: ["*.woff", "*.woff2"],
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user