Compare commits
74 Commits
flow-no-we
...
enterprise
Author | SHA1 | Date | |
---|---|---|---|
ab42a62916 | |||
ef8d2bdd40 | |||
32f4e08eac | |||
afdf830e8a | |||
7ab636e103 | |||
4efb4d6191 | |||
b855d98b78 | |||
354634cdf4 | |||
319f2ef8d1 | |||
cf58c5617a | |||
71344d0b6a | |||
696db2ae05 | |||
f08da8f295 | |||
89106c8131 | |||
f6b0eecde7 | |||
4ca151ee14 | |||
f66fea4b0a | |||
6d8dc4ac43 | |||
04982c8147 | |||
2ab68480a0 | |||
248d9e48bb | |||
e58e4bdbae | |||
a07ce35985 | |||
cfe275a374 | |||
7f474cde19 | |||
0597a3450b | |||
8191b90126 | |||
2613a5da4b | |||
2c4dd232a1 | |||
6b5c11ccfd | |||
a0b3d37b4a | |||
56eca6dc8f | |||
0377da2779 | |||
b16c67cc82 | |||
28f55635be | |||
8d4b2610b1 | |||
419cf80469 | |||
632dc4b1b2 | |||
93cfa64f5a | |||
fa8f9d4017 | |||
d4c0696a8c | |||
20635a8cc6 | |||
c621ac0a6f | |||
0487c8d0f5 | |||
37511f07a0 | |||
7840a3b52a | |||
787e9e05e4 | |||
3c14b8931f | |||
e3f1d259cf | |||
3d981f9391 | |||
ba1c919781 | |||
38696d4bd9 | |||
7213a1f27a | |||
34b5a51990 | |||
79e779b339 | |||
2a35b13ad6 | |||
3754f27275 | |||
b0547844b9 | |||
1b5abd3a3a | |||
8244c2340a | |||
28080595d0 | |||
3999aa96fb | |||
b5a8957720 | |||
9b01213990 | |||
ae64d9f0fd | |||
ea55083929 | |||
786c38b4cc | |||
60521d89cb | |||
7e7fc75e77 | |||
d0d46299d2 | |||
e025eabdef | |||
44238e6372 | |||
be986c8474 | |||
afb3623622 |
4
.github/actions/setup/action.yml
vendored
4
.github/actions/setup/action.yml
vendored
@ -30,6 +30,10 @@ runs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
- name: Setup docker cache
|
||||
uses: ScribeMD/docker-cache@0.5.0
|
||||
with:
|
||||
key: docker-images-${{ runner.os }}-${{ hashFiles('.github/actions/setup/docker-compose.yml', 'Makefile') }}-${{ inputs.postgresql_version }}
|
||||
- name: Setup dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
|
2
.github/actions/setup/docker-compose.yml
vendored
2
.github/actions/setup/docker-compose.yml
vendored
@ -11,7 +11,7 @@ services:
|
||||
- 5432:5432
|
||||
restart: always
|
||||
redis:
|
||||
image: docker.io/library/redis
|
||||
image: docker.io/library/redis:7
|
||||
ports:
|
||||
- 6379:6379
|
||||
restart: always
|
||||
|
31
.github/codespell-words.txt
vendored
31
.github/codespell-words.txt
vendored
@ -1,7 +1,32 @@
|
||||
akadmin
|
||||
asgi
|
||||
assertIn
|
||||
authentik
|
||||
authn
|
||||
crate
|
||||
docstrings
|
||||
entra
|
||||
goauthentik
|
||||
gunicorn
|
||||
hass
|
||||
jwe
|
||||
jwks
|
||||
keypair
|
||||
keypairs
|
||||
hass
|
||||
warmup
|
||||
kubernetes
|
||||
oidc
|
||||
ontext
|
||||
openid
|
||||
passwordless
|
||||
plex
|
||||
saml
|
||||
scim
|
||||
singed
|
||||
assertIn
|
||||
slo
|
||||
sso
|
||||
totp
|
||||
traefik
|
||||
# https://github.com/codespell-project/codespell/issues/1224
|
||||
upToDate
|
||||
warmup
|
||||
webauthn
|
||||
|
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@ -82,6 +82,12 @@ updates:
|
||||
docusaurus:
|
||||
patterns:
|
||||
- "@docusaurus/*"
|
||||
build:
|
||||
patterns:
|
||||
- "@swc/*"
|
||||
- "swc-*"
|
||||
- "lightningcss*"
|
||||
- "@rspack/binding*"
|
||||
- package-ecosystem: npm
|
||||
directory: "/lifecycle/aws"
|
||||
schedule:
|
||||
|
@ -40,7 +40,7 @@ jobs:
|
||||
attestations: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: docker/setup-qemu-action@v3.5.0
|
||||
- uses: docker/setup-qemu-action@v3.6.0
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- name: prepare variables
|
||||
uses: ./.github/actions/docker-push-variables
|
||||
|
2
.github/workflows/ci-main-daily.yml
vendored
2
.github/workflows/ci-main-daily.yml
vendored
@ -15,8 +15,8 @@ jobs:
|
||||
matrix:
|
||||
version:
|
||||
- docs
|
||||
- version-2025-2
|
||||
- version-2024-12
|
||||
- version-2024-10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
|
2
.github/workflows/ci-outpost.yml
vendored
2
.github/workflows/ci-outpost.yml
vendored
@ -82,7 +82,7 @@ jobs:
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.5.0
|
||||
uses: docker/setup-qemu-action@v3.6.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: prepare variables
|
||||
|
4
.github/workflows/release-publish.yml
vendored
4
.github/workflows/release-publish.yml
vendored
@ -42,7 +42,7 @@ jobs:
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.5.0
|
||||
uses: docker/setup-qemu-action@v3.6.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: prepare variables
|
||||
@ -186,7 +186,7 @@ jobs:
|
||||
container=$(docker container create ${{ steps.ev.outputs.imageMainName }})
|
||||
docker cp ${container}:web/ .
|
||||
- name: Create a Sentry.io release
|
||||
uses: getsentry/action-release@v1
|
||||
uses: getsentry/action-release@v3
|
||||
continue-on-error: true
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
|
22
.vscode/settings.json
vendored
22
.vscode/settings.json
vendored
@ -1,26 +1,4 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"akadmin",
|
||||
"asgi",
|
||||
"authentik",
|
||||
"authn",
|
||||
"entra",
|
||||
"goauthentik",
|
||||
"jwe",
|
||||
"jwks",
|
||||
"kubernetes",
|
||||
"oidc",
|
||||
"openid",
|
||||
"passwordless",
|
||||
"plex",
|
||||
"saml",
|
||||
"scim",
|
||||
"slo",
|
||||
"sso",
|
||||
"totp",
|
||||
"traefik",
|
||||
"webauthn"
|
||||
],
|
||||
"todo-tree.tree.showCountsInTree": true,
|
||||
"todo-tree.tree.showBadges": true,
|
||||
"yaml.customTags": [
|
||||
|
65
Makefile
65
Makefile
@ -4,34 +4,17 @@
|
||||
PWD = $(shell pwd)
|
||||
UID = $(shell id -u)
|
||||
GID = $(shell id -g)
|
||||
NPM_VERSION = $(shell python -m scripts.npm_version)
|
||||
NPM_VERSION = $(shell poetry run python -m scripts.generate_semver)
|
||||
PY_SOURCES = authentik tests scripts lifecycle .github
|
||||
GO_SOURCES = cmd internal
|
||||
WEB_SOURCES = web/src web/packages
|
||||
DOCKER_IMAGE ?= "authentik:test"
|
||||
|
||||
GEN_API_TS = "gen-ts-api"
|
||||
GEN_API_PY = "gen-py-api"
|
||||
GEN_API_GO = "gen-go-api"
|
||||
|
||||
pg_user := $(shell python -m authentik.lib.config postgresql.user 2>/dev/null)
|
||||
pg_host := $(shell python -m authentik.lib.config postgresql.host 2>/dev/null)
|
||||
pg_name := $(shell python -m authentik.lib.config postgresql.name 2>/dev/null)
|
||||
|
||||
CODESPELL_ARGS = -D - -D .github/codespell-dictionary.txt \
|
||||
-I .github/codespell-words.txt \
|
||||
-S 'web/src/locales/**' \
|
||||
-S 'website/docs/developer-docs/api/reference/**' \
|
||||
-S '**/node_modules/**' \
|
||||
-S '**/dist/**' \
|
||||
$(PY_SOURCES) \
|
||||
$(GO_SOURCES) \
|
||||
$(WEB_SOURCES) \
|
||||
website/src \
|
||||
website/blog \
|
||||
website/docs \
|
||||
website/integrations \
|
||||
website/src
|
||||
pg_user := $(shell poetry run python -m authentik.lib.config postgresql.user 2>/dev/null)
|
||||
pg_host := $(shell poetry run python -m authentik.lib.config postgresql.host 2>/dev/null)
|
||||
pg_name := $(shell poetry run python -m authentik.lib.config postgresql.name 2>/dev/null)
|
||||
|
||||
all: lint-fix lint test gen web ## Lint, build, and test everything
|
||||
|
||||
@ -49,26 +32,26 @@ go-test:
|
||||
go test -timeout 0 -v -race -cover ./...
|
||||
|
||||
test: ## Run the server tests and produce a coverage report (locally)
|
||||
coverage run manage.py test --keepdb authentik
|
||||
coverage html
|
||||
coverage report
|
||||
poetry run coverage run manage.py test --keepdb authentik
|
||||
poetry run coverage html
|
||||
poetry run coverage report
|
||||
|
||||
lint-fix: lint-codespell ## Lint and automatically fix errors in the python source code. Reports spelling errors.
|
||||
black $(PY_SOURCES)
|
||||
ruff check --fix $(PY_SOURCES)
|
||||
poetry run black $(PY_SOURCES)
|
||||
poetry run ruff check --fix $(PY_SOURCES)
|
||||
|
||||
lint-codespell: ## Reports spelling errors.
|
||||
codespell -w $(CODESPELL_ARGS)
|
||||
poetry run codespell -w
|
||||
|
||||
lint: ## Lint the python and golang sources
|
||||
bandit -r $(PY_SOURCES) -x web/node_modules -x tests/wdio/node_modules -x website/node_modules
|
||||
poetry run bandit -c pyproject.toml -r $(PY_SOURCES)
|
||||
golangci-lint run -v
|
||||
|
||||
core-install:
|
||||
poetry install
|
||||
|
||||
migrate: ## Run the Authentik Django server's migrations
|
||||
python -m lifecycle.migrate
|
||||
poetry run python -m lifecycle.migrate
|
||||
|
||||
i18n-extract: core-i18n-extract web-i18n-extract ## Extract strings that require translation into files to send to a translation service
|
||||
|
||||
@ -76,7 +59,7 @@ aws-cfn:
|
||||
cd lifecycle/aws && npm run aws-cfn
|
||||
|
||||
core-i18n-extract:
|
||||
ak makemessages \
|
||||
poetry run ak makemessages \
|
||||
--add-location file \
|
||||
--no-obsolete \
|
||||
--ignore web \
|
||||
@ -107,11 +90,11 @@ gen-build: ## Extract the schema from the database
|
||||
AUTHENTIK_DEBUG=true \
|
||||
AUTHENTIK_TENANTS__ENABLED=true \
|
||||
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \
|
||||
ak make_blueprint_schema > blueprints/schema.json
|
||||
poetry run ak make_blueprint_schema > blueprints/schema.json
|
||||
AUTHENTIK_DEBUG=true \
|
||||
AUTHENTIK_TENANTS__ENABLED=true \
|
||||
AUTHENTIK_OUTPOSTS__DISABLE_EMBEDDED_OUTPOST=true \
|
||||
ak spectacular --file schema.yml
|
||||
poetry run ak spectacular --file schema.yml
|
||||
|
||||
gen-changelog: ## (Release) generate the changelog based from the commits since the last tag
|
||||
git log --pretty=format:" - %s" $(shell git describe --tags $(shell git rev-list --tags --max-count=1))...$(shell git branch --show-current) | sort > changelog.md
|
||||
@ -190,7 +173,7 @@ gen-client-go: gen-clean-go ## Build and install the authentik API for Golang
|
||||
rm -rf ./${GEN_API_GO}/config.yaml ./${GEN_API_GO}/templates/
|
||||
|
||||
gen-dev-config: ## Generate a local development config file
|
||||
python -m scripts.generate_config
|
||||
poetry run scripts/generate_config.py
|
||||
|
||||
gen: gen-build gen-client-ts
|
||||
|
||||
@ -271,21 +254,21 @@ ci--meta-debug:
|
||||
node --version
|
||||
|
||||
ci-black: ci--meta-debug
|
||||
black --check $(PY_SOURCES)
|
||||
poetry run black --check $(PY_SOURCES)
|
||||
|
||||
ci-ruff: ci--meta-debug
|
||||
ruff check $(PY_SOURCES)
|
||||
poetry run ruff check $(PY_SOURCES)
|
||||
|
||||
ci-codespell: ci--meta-debug
|
||||
codespell $(CODESPELL_ARGS) -s
|
||||
poetry run codespell -s
|
||||
|
||||
ci-bandit: ci--meta-debug
|
||||
bandit -r $(PY_SOURCES)
|
||||
poetry run bandit -r $(PY_SOURCES)
|
||||
|
||||
ci-pending-migrations: ci--meta-debug
|
||||
ak makemigrations --check
|
||||
poetry run ak makemigrations --check
|
||||
|
||||
ci-test: ci--meta-debug
|
||||
coverage run manage.py test --keepdb --randomly-seed ${CI_TEST_SEED} authentik
|
||||
coverage report
|
||||
coverage xml
|
||||
poetry run coverage run manage.py test --keepdb --randomly-seed ${CI_TEST_SEED} authentik
|
||||
poetry run coverage report
|
||||
poetry run coverage xml
|
||||
|
@ -2,7 +2,7 @@ authentik takes security very seriously. We follow the rules of [responsible di
|
||||
|
||||
## Independent audits and pentests
|
||||
|
||||
We are committed to engaging in regular pentesting and security audits of authentik. Defining and adhering to a cadence of external testing ensures a stronger probability that our code base, our features, and our architecture is as secure and non-exploitable as possible. For more details about specfic audits and pentests, refer to "Audits and Certificates" in our [Security documentation](https://docs.goauthentik.io/docs/security).
|
||||
We are committed to engaging in regular pentesting and security audits of authentik. Defining and adhering to a cadence of external testing ensures a stronger probability that our code base, our features, and our architecture is as secure and non-exploitable as possible. For more details about specific audits and pentests, refer to "Audits and Certificates" in our [Security documentation](https://docs.goauthentik.io/docs/security).
|
||||
|
||||
## What authentik classifies as a CVE
|
||||
|
||||
|
@ -55,7 +55,7 @@ class RedirectToAppLaunch(View):
|
||||
)
|
||||
except FlowNonApplicableException:
|
||||
raise Http404 from None
|
||||
plan.insert_stage(in_memory_stage(RedirectToAppStage))
|
||||
plan.append_stage(in_memory_stage(RedirectToAppStage))
|
||||
return plan.to_redirect(request, flow)
|
||||
|
||||
|
||||
|
@ -9,7 +9,7 @@ class AuthentikEnterpriseAuditConfig(EnterpriseConfig):
|
||||
"""Enterprise app config"""
|
||||
|
||||
name = "authentik.enterprise.audit"
|
||||
label = "authentik_enterprise_audit"
|
||||
label = "authentik_audit"
|
||||
verbose_name = "authentik Enterprise.Audit"
|
||||
default = True
|
||||
|
||||
|
107
authentik/enterprise/audit/models.py
Normal file
107
authentik/enterprise/audit/models.py
Normal file
@ -0,0 +1,107 @@
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from authentik.lib.models import SerializerModel
|
||||
from django.db import models
|
||||
from uuid import uuid4
|
||||
from authentik.core.models import Group, User
|
||||
|
||||
|
||||
# # Names
|
||||
# Lifecycle
|
||||
# Access reviews
|
||||
# Access lifecycle
|
||||
# Governance
|
||||
# Audit
|
||||
# Compliance
|
||||
|
||||
# Lifecycle
|
||||
# Lifecycle review
|
||||
# Review
|
||||
# Access review
|
||||
# Compliance review
|
||||
# X Scheduled review
|
||||
|
||||
|
||||
# Only some objects supported?
|
||||
#
|
||||
# For disabling support:
|
||||
# Application
|
||||
# Provider
|
||||
# Outpost (simply setting the list of providers to empty in the outpost itself)
|
||||
# Flow
|
||||
# Users
|
||||
# Groups <- will get tricky
|
||||
# Roles
|
||||
# Sources
|
||||
# Tokens (api, app_pass)
|
||||
# Brands
|
||||
# Outpost integrations
|
||||
#
|
||||
# w/o disabling support
|
||||
# System Settings
|
||||
# everything else
|
||||
# would need to show in an audit dashboard cause not all have pages to get details
|
||||
|
||||
# "default" policy for objects, by default, everlasting
|
||||
|
||||
|
||||
class AuditPolicyFailAction(models.TextChoices):
|
||||
# For preview
|
||||
NOTHING = "nothing"
|
||||
# Disable the thing failing, HOW
|
||||
DISABLE = "disable"
|
||||
# Emit events
|
||||
WARN = "warn"
|
||||
|
||||
|
||||
class LifecycleRule(SerializerModel):
|
||||
pass
|
||||
|
||||
|
||||
class ReviewRule(SerializerModel):
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
|
||||
# Check every 6 months, allow for daily/weekly/first of month, etc.
|
||||
interval = models.TextField() # timedelta
|
||||
# Preventive notification
|
||||
reminder_interval = models.TextField() # timedelta
|
||||
|
||||
# Must be checked by these
|
||||
groups = models.ManyToManyField(Group)
|
||||
users = models.ManyToManyField(User)
|
||||
|
||||
# How many of the above must approve
|
||||
required_approvals = models.IntegerField(default=1)
|
||||
|
||||
# How long to wait before executing fail action
|
||||
grace_period = models.TextField() # timedelta
|
||||
|
||||
# What to do if not reviewed in time
|
||||
fail_action = models.CharField(choices=AuditPolicyFailAction)
|
||||
|
||||
|
||||
class AuditPolicyBinding(SerializerModel):
|
||||
id = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
|
||||
# Many to many ? Bind users/groups here instead of on the policy ?
|
||||
policy = models.ForeignKey(AuditPolicy, on_delete=models.PROTECT)
|
||||
|
||||
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||
object_id = models.TextField(blank=True) # optional to apply on all objects of specific type
|
||||
content_object = GenericForeignKey("content_type", "object_id")
|
||||
|
||||
# valid -> waiting review -> valid
|
||||
# valid -> waiting review -> review overdue -> valid
|
||||
# valid -> waiting review -> review overdue -> failed -> valid
|
||||
# look at django-fsm or django-viewflow
|
||||
status = models.TextField()
|
||||
|
||||
class Meta:
|
||||
indexes = (
|
||||
models.Index(fields=["content_type"]),
|
||||
models.Index(fields=["content_type", "object_id"]),
|
||||
)
|
||||
|
||||
|
||||
class AuditHistory:
|
||||
pass
|
@ -37,6 +37,7 @@ class GoogleWorkspaceProviderSerializer(EnterpriseRequiredMixin, ProviderSeriali
|
||||
"user_delete_action",
|
||||
"group_delete_action",
|
||||
"default_group_email_domain",
|
||||
"dry_run",
|
||||
]
|
||||
extra_kwargs = {}
|
||||
|
||||
|
@ -8,9 +8,10 @@ from httplib2 import HttpLib2Error, HttpLib2ErrorWithResponse
|
||||
|
||||
from authentik.enterprise.providers.google_workspace.models import GoogleWorkspaceProvider
|
||||
from authentik.lib.sync.outgoing import HTTP_CONFLICT
|
||||
from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
|
||||
from authentik.lib.sync.outgoing.base import SAFE_METHODS, BaseOutgoingSyncClient
|
||||
from authentik.lib.sync.outgoing.exceptions import (
|
||||
BadRequestSyncException,
|
||||
DryRunRejected,
|
||||
NotFoundSyncException,
|
||||
ObjectExistsSyncException,
|
||||
StopSync,
|
||||
@ -43,6 +44,8 @@ class GoogleWorkspaceSyncClient[TModel: Model, TConnection: Model, TSchema: dict
|
||||
self.domains.append(domain_name)
|
||||
|
||||
def _request(self, request: HttpRequest):
|
||||
if self.provider.dry_run and request.method.upper() not in SAFE_METHODS:
|
||||
raise DryRunRejected(request.uri, request.method, request.body)
|
||||
try:
|
||||
response = request.execute()
|
||||
except GoogleAuthError as exc:
|
||||
|
@ -0,0 +1,24 @@
|
||||
# Generated by Django 5.0.12 on 2025-02-24 19:43
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
(
|
||||
"authentik_providers_google_workspace",
|
||||
"0003_googleworkspaceprovidergroup_attributes_and_more",
|
||||
),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="googleworkspaceprovider",
|
||||
name="dry_run",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="When enabled, provider will not modify or create objects in the remote system.",
|
||||
),
|
||||
),
|
||||
]
|
@ -36,6 +36,7 @@ class MicrosoftEntraProviderSerializer(EnterpriseRequiredMixin, ProviderSerializ
|
||||
"filter_group",
|
||||
"user_delete_action",
|
||||
"group_delete_action",
|
||||
"dry_run",
|
||||
]
|
||||
extra_kwargs = {}
|
||||
|
||||
|
@ -3,6 +3,7 @@ from collections.abc import Coroutine
|
||||
from dataclasses import asdict
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
from azure.core.exceptions import (
|
||||
ClientAuthenticationError,
|
||||
ServiceRequestError,
|
||||
@ -12,6 +13,7 @@ from azure.identity.aio import ClientSecretCredential
|
||||
from django.db.models import Model
|
||||
from django.http import HttpResponseBadRequest, HttpResponseNotFound
|
||||
from kiota_abstractions.api_error import APIError
|
||||
from kiota_abstractions.request_information import RequestInformation
|
||||
from kiota_authentication_azure.azure_identity_authentication_provider import (
|
||||
AzureIdentityAuthenticationProvider,
|
||||
)
|
||||
@ -21,13 +23,15 @@ from msgraph.generated.models.o_data_errors.o_data_error import ODataError
|
||||
from msgraph.graph_request_adapter import GraphRequestAdapter, options
|
||||
from msgraph.graph_service_client import GraphServiceClient
|
||||
from msgraph_core import GraphClientFactory
|
||||
from opentelemetry import trace
|
||||
|
||||
from authentik.enterprise.providers.microsoft_entra.models import MicrosoftEntraProvider
|
||||
from authentik.events.utils import sanitize_item
|
||||
from authentik.lib.sync.outgoing import HTTP_CONFLICT
|
||||
from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
|
||||
from authentik.lib.sync.outgoing.base import SAFE_METHODS, BaseOutgoingSyncClient
|
||||
from authentik.lib.sync.outgoing.exceptions import (
|
||||
BadRequestSyncException,
|
||||
DryRunRejected,
|
||||
NotFoundSyncException,
|
||||
ObjectExistsSyncException,
|
||||
StopSync,
|
||||
@ -35,20 +39,24 @@ from authentik.lib.sync.outgoing.exceptions import (
|
||||
)
|
||||
|
||||
|
||||
def get_request_adapter(
|
||||
credentials: ClientSecretCredential, scopes: list[str] | None = None
|
||||
) -> GraphRequestAdapter:
|
||||
if scopes:
|
||||
auth_provider = AzureIdentityAuthenticationProvider(credentials=credentials, scopes=scopes)
|
||||
else:
|
||||
auth_provider = AzureIdentityAuthenticationProvider(credentials=credentials)
|
||||
class AuthentikRequestAdapter(GraphRequestAdapter):
|
||||
def __init__(self, auth_provider, provider: MicrosoftEntraProvider, client=None):
|
||||
super().__init__(auth_provider, client)
|
||||
self._provider = provider
|
||||
|
||||
return GraphRequestAdapter(
|
||||
auth_provider=auth_provider,
|
||||
client=GraphClientFactory.create_with_default_middleware(
|
||||
options=options, client=KiotaClientFactory.get_default_client()
|
||||
),
|
||||
)
|
||||
async def get_http_response_message(
|
||||
self,
|
||||
request_info: RequestInformation,
|
||||
parent_span: trace.Span,
|
||||
claims: str = "",
|
||||
) -> httpx.Response:
|
||||
if self._provider.dry_run and request_info.http_method.value.upper() not in SAFE_METHODS:
|
||||
raise DryRunRejected(
|
||||
url=request_info.url,
|
||||
method=request_info.http_method.value,
|
||||
body=request_info.content.decode("utf-8"),
|
||||
)
|
||||
return await super().get_http_response_message(request_info, parent_span, claims=claims)
|
||||
|
||||
|
||||
class MicrosoftEntraSyncClient[TModel: Model, TConnection: Model, TSchema: dict](
|
||||
@ -63,9 +71,27 @@ class MicrosoftEntraSyncClient[TModel: Model, TConnection: Model, TSchema: dict]
|
||||
self.credentials = provider.microsoft_credentials()
|
||||
self.__prefetch_domains()
|
||||
|
||||
def get_request_adapter(
|
||||
self, credentials: ClientSecretCredential, scopes: list[str] | None = None
|
||||
) -> AuthentikRequestAdapter:
|
||||
if scopes:
|
||||
auth_provider = AzureIdentityAuthenticationProvider(
|
||||
credentials=credentials, scopes=scopes
|
||||
)
|
||||
else:
|
||||
auth_provider = AzureIdentityAuthenticationProvider(credentials=credentials)
|
||||
|
||||
return AuthentikRequestAdapter(
|
||||
auth_provider=auth_provider,
|
||||
provider=self.provider,
|
||||
client=GraphClientFactory.create_with_default_middleware(
|
||||
options=options, client=KiotaClientFactory.get_default_client()
|
||||
),
|
||||
)
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
return GraphServiceClient(request_adapter=get_request_adapter(**self.credentials))
|
||||
return GraphServiceClient(request_adapter=self.get_request_adapter(**self.credentials))
|
||||
|
||||
def _request[T](self, request: Coroutine[Any, Any, T]) -> T:
|
||||
try:
|
||||
|
@ -0,0 +1,24 @@
|
||||
# Generated by Django 5.0.12 on 2025-02-24 19:43
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
(
|
||||
"authentik_providers_microsoft_entra",
|
||||
"0002_microsoftentraprovidergroup_attributes_and_more",
|
||||
),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="microsoftentraprovider",
|
||||
name="dry_run",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="When enabled, provider will not modify or create objects in the remote system.",
|
||||
),
|
||||
),
|
||||
]
|
@ -32,7 +32,6 @@ class MicrosoftEntraUserTests(APITestCase):
|
||||
|
||||
@apply_blueprint("system/providers-microsoft-entra.yaml")
|
||||
def setUp(self) -> None:
|
||||
|
||||
# Delete all users and groups as the mocked HTTP responses only return one ID
|
||||
# which will cause errors with multiple users
|
||||
Tenant.objects.update(avatars="none")
|
||||
@ -97,6 +96,38 @@ class MicrosoftEntraUserTests(APITestCase):
|
||||
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
|
||||
user_create.assert_called_once()
|
||||
|
||||
def test_user_create_dry_run(self):
|
||||
"""Test user creation (dry run)"""
|
||||
self.provider.dry_run = True
|
||||
self.provider.save()
|
||||
uid = generate_id()
|
||||
with (
|
||||
patch(
|
||||
"authentik.enterprise.providers.microsoft_entra.models.MicrosoftEntraProvider.microsoft_credentials",
|
||||
MagicMock(return_value={"credentials": self.creds}),
|
||||
),
|
||||
patch(
|
||||
"msgraph.generated.organization.organization_request_builder.OrganizationRequestBuilder.get",
|
||||
AsyncMock(
|
||||
return_value=OrganizationCollectionResponse(
|
||||
value=[
|
||||
Organization(verified_domains=[VerifiedDomain(name="goauthentik.io")])
|
||||
]
|
||||
)
|
||||
),
|
||||
),
|
||||
):
|
||||
user = User.objects.create(
|
||||
username=uid,
|
||||
name=f"{uid} {uid}",
|
||||
email=f"{uid}@goauthentik.io",
|
||||
)
|
||||
microsoft_user = MicrosoftEntraProviderUser.objects.filter(
|
||||
provider=self.provider, user=user
|
||||
).first()
|
||||
self.assertIsNone(microsoft_user)
|
||||
self.assertFalse(Event.objects.filter(action=EventAction.SYSTEM_EXCEPTION).exists())
|
||||
|
||||
def test_user_not_created(self):
|
||||
"""Test without property mappings, no group is created"""
|
||||
self.provider.property_mappings.clear()
|
||||
|
@ -89,9 +89,9 @@ class SourceStageFinal(StageView):
|
||||
This stage uses the override flow token to resume execution of the initial flow the
|
||||
source stage is bound to."""
|
||||
|
||||
def dispatch(self):
|
||||
def dispatch(self, *args, **kwargs):
|
||||
token: FlowToken = self.request.session.get(SESSION_KEY_OVERRIDE_FLOW_TOKEN)
|
||||
self._logger.info("Replacing source flow with overridden flow", flow=token.flow.slug)
|
||||
self.logger.info("Replacing source flow with overridden flow", flow=token.flow.slug)
|
||||
plan = token.plan
|
||||
plan.context[PLAN_CONTEXT_IS_RESTORED] = token
|
||||
response = plan.to_redirect(self.request, token.flow)
|
||||
|
@ -4,7 +4,8 @@ from django.urls import reverse
|
||||
|
||||
from authentik.core.tests.utils import create_test_flow, create_test_user
|
||||
from authentik.enterprise.stages.source.models import SourceStage
|
||||
from authentik.flows.models import FlowDesignation, FlowStageBinding, FlowToken
|
||||
from authentik.enterprise.stages.source.stage import SourceStageFinal
|
||||
from authentik.flows.models import FlowDesignation, FlowStageBinding, FlowToken, in_memory_stage
|
||||
from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED, FlowPlan
|
||||
from authentik.flows.tests import FlowTestCase
|
||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||
@ -87,6 +88,7 @@ class TestSourceStage(FlowTestCase):
|
||||
self.assertIsNotNone(flow_token)
|
||||
session = self.client.session
|
||||
plan: FlowPlan = session[SESSION_KEY_PLAN]
|
||||
plan.insert_stage(in_memory_stage(SourceStageFinal), index=0)
|
||||
plan.context[PLAN_CONTEXT_IS_RESTORED] = flow_token
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
@ -96,4 +98,6 @@ class TestSourceStage(FlowTestCase):
|
||||
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}), follow=True
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageRedirects(response, reverse("authentik_core:root-redirect"))
|
||||
self.assertStageRedirects(
|
||||
response, reverse("authentik_core:if-flow", kwargs={"flow_slug": flow.slug})
|
||||
)
|
||||
|
@ -76,10 +76,10 @@ class FlowPlan:
|
||||
self.bindings.append(binding)
|
||||
self.markers.append(marker or StageMarker())
|
||||
|
||||
def insert_stage(self, stage: Stage, marker: StageMarker | None = None):
|
||||
def insert_stage(self, stage: Stage, marker: StageMarker | None = None, index=1):
|
||||
"""Insert stage into plan, as immediate next stage"""
|
||||
self.bindings.insert(1, FlowStageBinding(stage=stage, order=0))
|
||||
self.markers.insert(1, marker or StageMarker())
|
||||
self.bindings.insert(index, FlowStageBinding(stage=stage, order=0))
|
||||
self.markers.insert(index, marker or StageMarker())
|
||||
|
||||
def redirect(self, destination: str):
|
||||
"""Insert a redirect stage as next stage"""
|
||||
|
@ -282,16 +282,14 @@ class ConfigLoader:
|
||||
|
||||
def get_optional_int(self, path: str, default=None) -> int | None:
|
||||
"""Wrapper for get that converts value into int or None if set"""
|
||||
value = self.get(path, default)
|
||||
value = self.get(path, UNSET)
|
||||
if value is UNSET:
|
||||
return default
|
||||
try:
|
||||
return int(value)
|
||||
except (ValueError, TypeError) as exc:
|
||||
if value is None or (isinstance(value, str) and value.lower() == "null"):
|
||||
return default
|
||||
if value is UNSET:
|
||||
return default
|
||||
return None
|
||||
self.log("warning", "Failed to parse config as int", path=path, exc=str(exc))
|
||||
return default
|
||||
|
||||
@ -372,9 +370,9 @@ def django_db_config(config: ConfigLoader | None = None) -> dict:
|
||||
"sslcert": config.get("postgresql.sslcert"),
|
||||
"sslkey": config.get("postgresql.sslkey"),
|
||||
},
|
||||
"CONN_MAX_AGE": CONFIG.get_optional_int("postgresql.conn_max_age", 0),
|
||||
"CONN_HEALTH_CHECKS": CONFIG.get_bool("postgresql.conn_health_checks", False),
|
||||
"DISABLE_SERVER_SIDE_CURSORS": CONFIG.get_bool(
|
||||
"CONN_MAX_AGE": config.get_optional_int("postgresql.conn_max_age", 0),
|
||||
"CONN_HEALTH_CHECKS": config.get_bool("postgresql.conn_health_checks", False),
|
||||
"DISABLE_SERVER_SIDE_CURSORS": config.get_bool(
|
||||
"postgresql.disable_server_side_cursors", False
|
||||
),
|
||||
"TEST": {
|
||||
@ -383,8 +381,8 @@ def django_db_config(config: ConfigLoader | None = None) -> dict:
|
||||
}
|
||||
}
|
||||
|
||||
conn_max_age = CONFIG.get_optional_int("postgresql.conn_max_age", UNSET)
|
||||
disable_server_side_cursors = CONFIG.get_bool("postgresql.disable_server_side_cursors", UNSET)
|
||||
conn_max_age = config.get_optional_int("postgresql.conn_max_age", UNSET)
|
||||
disable_server_side_cursors = config.get_bool("postgresql.disable_server_side_cursors", UNSET)
|
||||
if config.get_bool("postgresql.use_pgpool", False):
|
||||
db["default"]["DISABLE_SERVER_SIDE_CURSORS"] = True
|
||||
if disable_server_side_cursors is not UNSET:
|
||||
|
@ -33,6 +33,7 @@ class SyncObjectSerializer(PassiveSerializer):
|
||||
)
|
||||
)
|
||||
sync_object_id = CharField()
|
||||
override_dry_run = BooleanField(default=False)
|
||||
|
||||
|
||||
class SyncObjectResultSerializer(PassiveSerializer):
|
||||
@ -98,6 +99,7 @@ class OutgoingSyncProviderStatusMixin:
|
||||
page=1,
|
||||
provider_pk=provider.pk,
|
||||
pk=params.validated_data["sync_object_id"],
|
||||
override_dry_run=params.validated_data["override_dry_run"],
|
||||
).get()
|
||||
return Response(SyncObjectResultSerializer(instance={"messages": res}).data)
|
||||
|
||||
|
@ -28,6 +28,14 @@ class Direction(StrEnum):
|
||||
remove = "remove"
|
||||
|
||||
|
||||
SAFE_METHODS = [
|
||||
"GET",
|
||||
"HEAD",
|
||||
"OPTIONS",
|
||||
"TRACE",
|
||||
]
|
||||
|
||||
|
||||
class BaseOutgoingSyncClient[
|
||||
TModel: "Model", TConnection: "Model", TSchema: dict, TProvider: "OutgoingSyncProvider"
|
||||
]:
|
||||
|
@ -21,6 +21,22 @@ class BadRequestSyncException(BaseSyncException):
|
||||
"""Exception when invalid data was sent to the remote system"""
|
||||
|
||||
|
||||
class DryRunRejected(BaseSyncException):
|
||||
"""When dry_run is enabled and a provider dropped a mutating request"""
|
||||
|
||||
def __init__(self, url: str, method: str, body: dict):
|
||||
super().__init__()
|
||||
self.url = url
|
||||
self.method = method
|
||||
self.body = body
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
def __str__(self):
|
||||
return f"Dry-run rejected request: {self.method} {self.url}"
|
||||
|
||||
|
||||
class StopSync(BaseSyncException):
|
||||
"""Exception raised when a configuration error should stop the sync process"""
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
from typing import Any, Self
|
||||
|
||||
import pglock
|
||||
from django.db import connection
|
||||
from django.db import connection, models
|
||||
from django.db.models import Model, QuerySet, TextChoices
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from authentik.core.models import Group, User
|
||||
from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
|
||||
@ -18,6 +19,14 @@ class OutgoingSyncDeleteAction(TextChoices):
|
||||
|
||||
|
||||
class OutgoingSyncProvider(Model):
|
||||
"""Base abstract models for providers implementing outgoing sync"""
|
||||
|
||||
dry_run = models.BooleanField(
|
||||
default=False,
|
||||
help_text=_(
|
||||
"When enabled, provider will not modify or create objects in the remote system."
|
||||
),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
@ -32,7 +41,7 @@ class OutgoingSyncProvider(Model):
|
||||
|
||||
@property
|
||||
def sync_lock(self) -> pglock.advisory:
|
||||
"""Postgres lock for syncing SCIM to prevent multiple parallel syncs happening"""
|
||||
"""Postgres lock for syncing to prevent multiple parallel syncs happening"""
|
||||
return pglock.advisory(
|
||||
lock_id=f"goauthentik.io/{connection.schema_name}/providers/outgoing-sync/{str(self.pk)}",
|
||||
timeout=0,
|
||||
|
@ -20,6 +20,7 @@ from authentik.lib.sync.outgoing import PAGE_SIZE, PAGE_TIMEOUT
|
||||
from authentik.lib.sync.outgoing.base import Direction
|
||||
from authentik.lib.sync.outgoing.exceptions import (
|
||||
BadRequestSyncException,
|
||||
DryRunRejected,
|
||||
StopSync,
|
||||
TransientSyncException,
|
||||
)
|
||||
@ -105,7 +106,9 @@ class SyncTasks:
|
||||
return
|
||||
task.set_status(TaskStatus.SUCCESSFUL, *messages)
|
||||
|
||||
def sync_objects(self, object_type: str, page: int, provider_pk: int, **filter):
|
||||
def sync_objects(
|
||||
self, object_type: str, page: int, provider_pk: int, override_dry_run=False, **filter
|
||||
):
|
||||
_object_type = path_to_class(object_type)
|
||||
self.logger = get_logger().bind(
|
||||
provider_type=class_to_path(self._provider_model),
|
||||
@ -116,6 +119,10 @@ class SyncTasks:
|
||||
provider = self._provider_model.objects.filter(pk=provider_pk).first()
|
||||
if not provider:
|
||||
return messages
|
||||
# Override dry run mode if requested, however don't save the provider
|
||||
# so that scheduled sync tasks still run in dry_run mode
|
||||
if override_dry_run:
|
||||
provider.dry_run = False
|
||||
try:
|
||||
client = provider.client_for_model(_object_type)
|
||||
except TransientSyncException:
|
||||
@ -132,6 +139,22 @@ class SyncTasks:
|
||||
except SkipObjectException:
|
||||
self.logger.debug("skipping object due to SkipObject", obj=obj)
|
||||
continue
|
||||
except DryRunRejected as exc:
|
||||
messages.append(
|
||||
asdict(
|
||||
LogEvent(
|
||||
_("Dropping mutating request due to dry run"),
|
||||
log_level="info",
|
||||
logger=f"{provider._meta.verbose_name}@{object_type}",
|
||||
attributes={
|
||||
"obj": sanitize_item(obj),
|
||||
"method": exc.method,
|
||||
"url": exc.url,
|
||||
"body": exc.body,
|
||||
},
|
||||
)
|
||||
)
|
||||
)
|
||||
except BadRequestSyncException as exc:
|
||||
self.logger.warning("failed to sync object", exc=exc, obj=obj)
|
||||
messages.append(
|
||||
@ -231,8 +254,10 @@ class SyncTasks:
|
||||
raise Retry() from exc
|
||||
except SkipObjectException:
|
||||
continue
|
||||
except DryRunRejected as exc:
|
||||
self.logger.info("Rejected dry-run event", exc=exc)
|
||||
except StopSync as exc:
|
||||
self.logger.warning(exc, provider_pk=provider.pk)
|
||||
self.logger.warning("Stopping sync", exc=exc, provider_pk=provider.pk)
|
||||
|
||||
def sync_signal_m2m(self, group_pk: str, action: str, pk_set: list[int]):
|
||||
self.logger = get_logger().bind(
|
||||
@ -263,5 +288,7 @@ class SyncTasks:
|
||||
raise Retry() from exc
|
||||
except SkipObjectException:
|
||||
continue
|
||||
except DryRunRejected as exc:
|
||||
self.logger.info("Rejected dry-run event", exc=exc)
|
||||
except StopSync as exc:
|
||||
self.logger.warning(exc, provider_pk=provider.pk)
|
||||
self.logger.warning("Stopping sync", exc=exc, provider_pk=provider.pk)
|
||||
|
@ -158,6 +158,18 @@ class TestConfig(TestCase):
|
||||
test_obj = Test()
|
||||
dumps(test_obj, indent=4, cls=AttrEncoder)
|
||||
|
||||
def test_get_optional_int(self):
|
||||
config = ConfigLoader()
|
||||
self.assertEqual(config.get_optional_int("foo", 21), 21)
|
||||
self.assertEqual(config.get_optional_int("foo"), None)
|
||||
config.set("foo", "21")
|
||||
self.assertEqual(config.get_optional_int("foo"), 21)
|
||||
self.assertEqual(config.get_optional_int("foo", 0), 21)
|
||||
self.assertEqual(config.get_optional_int("foo", "null"), 21)
|
||||
config.set("foo", "null")
|
||||
self.assertEqual(config.get_optional_int("foo"), None)
|
||||
self.assertEqual(config.get_optional_int("foo", 21), None)
|
||||
|
||||
@mock.patch.dict(environ, check_deprecations_env_vars)
|
||||
def test_check_deprecations(self):
|
||||
"""Test config key re-write for deprecated env vars"""
|
||||
@ -221,6 +233,16 @@ class TestConfig(TestCase):
|
||||
},
|
||||
)
|
||||
|
||||
def test_db_conn_max_age(self):
|
||||
"""Test DB conn_max_age Config"""
|
||||
config = ConfigLoader()
|
||||
config.set("postgresql.conn_max_age", "null")
|
||||
conf = django_db_config(config)
|
||||
self.assertEqual(
|
||||
conf["default"]["CONN_MAX_AGE"],
|
||||
None,
|
||||
)
|
||||
|
||||
def test_db_read_replicas(self):
|
||||
"""Test read replicas"""
|
||||
config = ConfigLoader()
|
||||
|
@ -9,7 +9,12 @@ from hashlib import sha256
|
||||
from typing import Any
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
|
||||
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey
|
||||
from cryptography.hazmat.primitives.asymmetric.ec import (
|
||||
SECP256R1,
|
||||
SECP384R1,
|
||||
SECP521R1,
|
||||
EllipticCurvePrivateKey,
|
||||
)
|
||||
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
|
||||
from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes
|
||||
from dacite import Config
|
||||
@ -114,6 +119,22 @@ class JWTAlgorithms(models.TextChoices):
|
||||
HS256 = "HS256", _("HS256 (Symmetric Encryption)")
|
||||
RS256 = "RS256", _("RS256 (Asymmetric Encryption)")
|
||||
ES256 = "ES256", _("ES256 (Asymmetric Encryption)")
|
||||
ES384 = "ES384", _("ES384 (Asymmetric Encryption)")
|
||||
ES512 = "ES512", _("ES512 (Asymmetric Encryption)")
|
||||
|
||||
@classmethod
|
||||
def from_private_key(cls, private_key: PrivateKeyTypes | None) -> str:
|
||||
if isinstance(private_key, RSAPrivateKey):
|
||||
return cls.RS256
|
||||
if isinstance(private_key, EllipticCurvePrivateKey):
|
||||
curve = private_key.curve
|
||||
if isinstance(curve, SECP256R1):
|
||||
return cls.ES256
|
||||
if isinstance(curve, SECP384R1):
|
||||
return cls.ES384
|
||||
if isinstance(curve, SECP521R1):
|
||||
return cls.ES512
|
||||
raise ValueError(f"Invalid private key type: {type(private_key)}")
|
||||
|
||||
|
||||
class ScopeMapping(PropertyMapping):
|
||||
@ -263,11 +284,7 @@ class OAuth2Provider(WebfingerProvider, Provider):
|
||||
return self.client_secret, JWTAlgorithms.HS256
|
||||
key: CertificateKeyPair = self.signing_key
|
||||
private_key = key.private_key
|
||||
if isinstance(private_key, RSAPrivateKey):
|
||||
return private_key, JWTAlgorithms.RS256
|
||||
if isinstance(private_key, EllipticCurvePrivateKey):
|
||||
return private_key, JWTAlgorithms.ES256
|
||||
raise ValueError(f"Invalid private key type: {type(private_key)}")
|
||||
return private_key, JWTAlgorithms.from_private_key(private_key)
|
||||
|
||||
def get_issuer(self, request: HttpRequest) -> str | None:
|
||||
"""Get issuer, based on request"""
|
||||
|
@ -71,7 +71,7 @@ class CodeValidatorView(PolicyAccessView):
|
||||
except FlowNonApplicableException:
|
||||
LOGGER.warning("Flow not applicable to user")
|
||||
return None
|
||||
plan.insert_stage(in_memory_stage(OAuthDeviceCodeFinishStage))
|
||||
plan.append_stage(in_memory_stage(OAuthDeviceCodeFinishStage))
|
||||
return plan.to_redirect(self.request, self.token.provider.authorization_flow)
|
||||
|
||||
|
||||
|
@ -34,5 +34,5 @@ class EndSessionView(PolicyAccessView):
|
||||
PLAN_CONTEXT_APPLICATION: self.application,
|
||||
},
|
||||
)
|
||||
plan.insert_stage(in_memory_stage(SessionEndStage))
|
||||
plan.append_stage(in_memory_stage(SessionEndStage))
|
||||
return plan.to_redirect(self.request, self.flow)
|
||||
|
@ -75,10 +75,7 @@ class JWKSView(View):
|
||||
key_data = {}
|
||||
|
||||
if use == "sig":
|
||||
if isinstance(private_key, RSAPrivateKey):
|
||||
key_data["alg"] = JWTAlgorithms.RS256
|
||||
elif isinstance(private_key, EllipticCurvePrivateKey):
|
||||
key_data["alg"] = JWTAlgorithms.ES256
|
||||
key_data["alg"] = JWTAlgorithms.from_private_key(private_key)
|
||||
elif use == "enc":
|
||||
key_data["alg"] = "RSA-OAEP-256"
|
||||
key_data["enc"] = "A256CBC-HS512"
|
||||
|
@ -36,17 +36,17 @@ class IngressReconciler(KubernetesObjectReconciler[V1Ingress]):
|
||||
def reconciler_name() -> str:
|
||||
return "ingress"
|
||||
|
||||
def _check_annotations(self, reference: V1Ingress):
|
||||
def _check_annotations(self, current: V1Ingress, reference: V1Ingress):
|
||||
"""Check that all annotations *we* set are correct"""
|
||||
for key, value in self.get_ingress_annotations().items():
|
||||
if key not in reference.metadata.annotations:
|
||||
for key, value in reference.metadata.annotations.items():
|
||||
if key not in current.metadata.annotations:
|
||||
raise NeedsUpdate()
|
||||
if reference.metadata.annotations[key] != value:
|
||||
if current.metadata.annotations[key] != value:
|
||||
raise NeedsUpdate()
|
||||
|
||||
def reconcile(self, current: V1Ingress, reference: V1Ingress):
|
||||
super().reconcile(current, reference)
|
||||
self._check_annotations(reference)
|
||||
self._check_annotations(current, reference)
|
||||
# Create a list of all expected host and tls hosts
|
||||
expected_hosts = []
|
||||
expected_hosts_tls = []
|
||||
|
@ -46,7 +46,7 @@ class RACStartView(PolicyAccessView):
|
||||
)
|
||||
except FlowNonApplicableException:
|
||||
raise Http404 from None
|
||||
plan.insert_stage(
|
||||
plan.append_stage(
|
||||
in_memory_stage(
|
||||
RACFinalStage,
|
||||
application=self.application,
|
||||
|
@ -61,7 +61,7 @@ class SAMLSLOView(PolicyAccessView):
|
||||
PLAN_CONTEXT_APPLICATION: self.application,
|
||||
},
|
||||
)
|
||||
plan.insert_stage(in_memory_stage(SessionEndStage))
|
||||
plan.append_stage(in_memory_stage(SessionEndStage))
|
||||
return plan.to_redirect(self.request, self.flow)
|
||||
|
||||
def post(self, request: HttpRequest, application_slug: str) -> HttpResponse:
|
||||
|
@ -30,6 +30,7 @@ class SCIMProviderSerializer(ProviderSerializer):
|
||||
"token",
|
||||
"exclude_users_service_account",
|
||||
"filter_group",
|
||||
"dry_run",
|
||||
]
|
||||
extra_kwargs = {}
|
||||
|
||||
|
@ -12,8 +12,9 @@ from authentik.lib.sync.outgoing import (
|
||||
HTTP_SERVICE_UNAVAILABLE,
|
||||
HTTP_TOO_MANY_REQUESTS,
|
||||
)
|
||||
from authentik.lib.sync.outgoing.base import BaseOutgoingSyncClient
|
||||
from authentik.lib.sync.outgoing.base import SAFE_METHODS, BaseOutgoingSyncClient
|
||||
from authentik.lib.sync.outgoing.exceptions import (
|
||||
DryRunRejected,
|
||||
NotFoundSyncException,
|
||||
ObjectExistsSyncException,
|
||||
TransientSyncException,
|
||||
@ -54,6 +55,8 @@ class SCIMClient[TModel: "Model", TConnection: "Model", TSchema: "BaseModel"](
|
||||
|
||||
def _request(self, method: str, path: str, **kwargs) -> dict:
|
||||
"""Wrapper to send a request to the full URL"""
|
||||
if self.provider.dry_run and method.upper() not in SAFE_METHODS:
|
||||
raise DryRunRejected(f"{self.base_url}{path}", method, body=kwargs.get("json"))
|
||||
try:
|
||||
response = self._session.request(
|
||||
method,
|
||||
|
@ -0,0 +1,21 @@
|
||||
# Generated by Django 5.0.12 on 2025-02-24 19:43
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_providers_scim", "0010_scimprovider_verify_certificates"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="scimprovider",
|
||||
name="dry_run",
|
||||
field=models.BooleanField(
|
||||
default=False,
|
||||
help_text="When enabled, provider will not modify or create objects in the remote system.",
|
||||
),
|
||||
),
|
||||
]
|
@ -3,12 +3,15 @@
|
||||
from json import loads
|
||||
|
||||
from django.test import TestCase
|
||||
from django.utils.text import slugify
|
||||
from jsonschema import validate
|
||||
from requests_mock import Mocker
|
||||
|
||||
from authentik.blueprints.tests import apply_blueprint
|
||||
from authentik.core.models import Application, Group, User
|
||||
from authentik.events.models import SystemTask
|
||||
from authentik.lib.generators import generate_id
|
||||
from authentik.lib.sync.outgoing.base import SAFE_METHODS
|
||||
from authentik.providers.scim.models import SCIMMapping, SCIMProvider
|
||||
from authentik.providers.scim.tasks import scim_sync, sync_tasks
|
||||
from authentik.tenants.models import Tenant
|
||||
@ -330,3 +333,59 @@ class SCIMUserTests(TestCase):
|
||||
"userName": uid,
|
||||
},
|
||||
)
|
||||
|
||||
def test_user_create_dry_run(self):
|
||||
"""Test user creation (dry_run)"""
|
||||
# Update the provider before we start mocking as saving the provider triggers a full sync
|
||||
self.provider.dry_run = True
|
||||
self.provider.save()
|
||||
with Mocker() as mock:
|
||||
scim_id = generate_id()
|
||||
mock.get(
|
||||
"https://localhost/ServiceProviderConfig",
|
||||
json={},
|
||||
)
|
||||
mock.post(
|
||||
"https://localhost/Users",
|
||||
json={
|
||||
"id": scim_id,
|
||||
},
|
||||
)
|
||||
uid = generate_id()
|
||||
User.objects.create(
|
||||
username=uid,
|
||||
name=f"{uid} {uid}",
|
||||
email=f"{uid}@goauthentik.io",
|
||||
)
|
||||
self.assertEqual(mock.call_count, 1, mock.request_history)
|
||||
self.assertEqual(mock.request_history[0].method, "GET")
|
||||
|
||||
def test_sync_task_dry_run(self):
|
||||
"""Test sync tasks"""
|
||||
# Update the provider before we start mocking as saving the provider triggers a full sync
|
||||
self.provider.dry_run = True
|
||||
self.provider.save()
|
||||
with Mocker() as mock:
|
||||
uid = generate_id()
|
||||
mock.get(
|
||||
"https://localhost/ServiceProviderConfig",
|
||||
json={},
|
||||
)
|
||||
User.objects.create(
|
||||
username=uid,
|
||||
name=f"{uid} {uid}",
|
||||
email=f"{uid}@goauthentik.io",
|
||||
)
|
||||
|
||||
sync_tasks.trigger_single_task(self.provider, scim_sync).get()
|
||||
|
||||
self.assertEqual(mock.call_count, 3)
|
||||
for request in mock.request_history:
|
||||
self.assertIn(request.method, SAFE_METHODS)
|
||||
task = SystemTask.objects.filter(uid=slugify(self.provider.name)).first()
|
||||
self.assertIsNotNone(task)
|
||||
drop_msg = task.messages[2]
|
||||
self.assertEqual(drop_msg["event"], "Dropping mutating request due to dry run")
|
||||
self.assertIsNotNone(drop_msg["attributes"]["url"])
|
||||
self.assertIsNotNone(drop_msg["attributes"]["body"])
|
||||
self.assertIsNotNone(drop_msg["attributes"]["method"])
|
||||
|
@ -7,6 +7,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django.views import View
|
||||
from rest_framework.serializers import BaseSerializer
|
||||
|
||||
from authentik.core.types import UserSettingSerializer
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.flows.exceptions import StageInvalidException
|
||||
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
|
||||
@ -71,6 +72,14 @@ class AuthenticatorEmailStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
||||
def component(self) -> str:
|
||||
return "ak-stage-authenticator-email-form"
|
||||
|
||||
def ui_user_settings(self) -> UserSettingSerializer | None:
|
||||
return UserSettingSerializer(
|
||||
data={
|
||||
"title": self.friendly_name or str(self._meta.verbose_name),
|
||||
"component": "ak-user-settings-authenticator-email",
|
||||
}
|
||||
)
|
||||
|
||||
@property
|
||||
def backend_class(self) -> type[BaseEmailBackend]:
|
||||
"""Get the email backend class to use"""
|
||||
|
@ -299,12 +299,6 @@ class TestAuthenticatorEmailStage(FlowTestCase):
|
||||
data={"component": "ak-stage-authenticator-email", "code": device.token},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(device.confirmed)
|
||||
# Get a fresh session to check if the key was removed
|
||||
session = self.client.session
|
||||
session.save()
|
||||
session.load()
|
||||
self.assertNotIn(SESSION_KEY_EMAIL_DEVICE, session)
|
||||
|
||||
def test_model_properties_and_methods(self):
|
||||
"""Test model properties"""
|
||||
@ -331,7 +325,6 @@ class TestAuthenticatorEmailStage(FlowTestCase):
|
||||
self.stage.send(device)
|
||||
|
||||
def test_email_tasks(self):
|
||||
|
||||
email_send_mock = MagicMock()
|
||||
with patch(
|
||||
"authentik.stages.email.tasks.send_mails",
|
||||
|
@ -146,5 +146,10 @@
|
||||
"name": "LogMeOnce",
|
||||
"icon_dark": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMjA0OCAyMDQ4IiB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTSAwIDAgTCAtNSAxIEwgLTE1IDQgTCAtMzggMTEgTCAtNTcgMTggTCAtNzYgMjkgTCAtOTAgMzggTCAtMTA5IDUyIEwgLTExNiA1OSBMIC0xMjcgNjggTCAtMTQwIDgxIEwgLTE0OCA4OCBMIC0xNDkgOTEgTCAtMTUxIDkxIEwgLTE2MSAxMDEgTCAtMTY5IDEwOCBMIC0xNjkgMTEwIEwgLTE3MSAxMTAgTCAtMTgwIDExOSBMIC0xODggMTI2IEwgLTIwMSAxMzcgTCAtMjEyIDE0NiBMIC0yMjYgMTU3IEwgLTIzOSAxNjcgTCAtMjUwIDE3NSBMIC0yNzAgMTg4IEwgLTI5MSAyMDIgTCAtMzA5IDIxMiBMIC0zMzAgMjI0IEwgLTM1MyAyMzUgTCAtMzg2IDI1MCBMIC00MDcgMjU5IEwgLTQ0OSAyNzMgTCAtNDcyIDI4MSBMIC01MDIgMjg5IEwgLTU1MSAyOTggTCAtNjE1IDMwOSBMIC02NDcgMzE2IEwgLTY4MSAzMjQgTCAtNjk4IDMzMCBMIC03MTQgMzM4IEwgLTczNSAzNTEgTCAtNzQ1IDM1OCBMIC03NTYgMzY3IEwgLTc2NCAzNzUgTCAtNzY2IDM3OSBMIC03NjggMzc5IEwgLTc3MyAzODUgTCAtNzgxIDM5NSBMIC03OTEgNDEwIEwgLTgwMSA0MjggTCAtODEwIDQ0NyBMIC04MTYgNDY1IEwgLTgyMiA1MDAgTCAtODI3IDU0NSBMIC04MzAgNTkyIEwgLTgzMiA2NTMgTCAtODMyIDcxMiBMIC04MzEgNzQyIEwgLTgyOCA3OTQgTCAtODIyIDg2NiBMIC04MTYgOTI4IEwgLTgxMiA5NTcgTCAtODAwIDEwMjAgTCAtNzg5IDEwNzIgTCAtNzgzIDEwOTggTCAtNzY1IDExNjIgTCAtNzUxIDEyMDcgTCAtNzQ0IDEyMjUgTCAtNzM0IDEyNTQgTCAtNzIwIDEyOTAgTCAtNzA0IDEzMjggTCAtNjg5IDEzNjEgTCAtNjY4IDE0MDMgTCAtNjUzIDE0MzEgTCAtNjM5IDE0NTYgTCAtNjIwIDE0ODggTCAtNjA3IDE1MDkgTCAtNTk0IDE1MjkgTCAtNTg0IDE1NDQgTCAtNTcxIDE1NjMgTCAtNTU4IDE1ODEgTCAtNTQ0IDE2MDAgTCAtNTMzIDE2MTUgTCAtNTIwIDE2MzIgTCAtNTA3IDE2NDggTCAtNDc5IDE2ODIgTCAtNDcwIDE2OTMgTCAtNDYwIDE3MDQgTCAtNDQ5IDE3MTYgTCAtNDQyIDE3MjQgTCAtNDMyIDE3MzQgTCAtNDI1IDE3NDIgTCAtMzY4IDE3OTkgTCAtMzUxIDE4MTUgTCAtMzM1IDE4MzAgTCAtMzI3IDE4MzcgTCAtMzE0IDE4NDkgTCAtMjg4IDE4NzEgTCAtMjcxIDE4ODUgTCAtMjYxIDE4OTMgTCAtMjQ0IDE5MDcgTCAtMjI4IDE5MTkgTCAtMjE1IDE5MjkgTCAtMTk2IDE5NDMgTCAtMTg0IDE5NTIgTCAtMTY4IDE5NjMgTCAtMTQ4IDE5NzcgTCAtMTMzIDE5ODcgTCAtMTEyIDIwMDAgTCAtODggMjAxMyBMIC03MiAyMDIxIEwgLTUyIDIwMzAgTCAtMzIgMjAzNyBMIDEyIDIwNDggTCA2MCAyMDQ4IEwgNjEgMjA0NyBMIDczIDIwNDQgTCAxMDAgMjAzNyBMIDEyMSAyMDI5IEwgMTQ5IDIwMTUgTCAxNjcgMjAwNSBMIDE4NiAxOTk0IEwgMjEwIDE5NzkgTCAyMzEgMTk2NSBMIDI2NiAxOTQxIEwgMjg0IDE5MjggTCAyOTUgMTkyMCBMIDMwNiAxOTExIEwgMzI0IDE4OTggTCAzMzggMTg4NyBMIDM0OSAxODc3IEwgMzU4IDE4NzAgTCAzNjkgMTg2MCBMIDM4MCAxODUxIEwgMzgwIDE4NDkgTCAzODIgMTg0OSBMIDM4NSAxODQ2IEwgMzk2IDE4MzcgTCA0MDMgMTgzMCBMIDQxMSAxODIzIEwgNDE4IDE4MTYgTCA0MjYgMTgwOSBMIDQ3NSAxNzYwIEwgNDc3IDE3NTYgTCA0NzkgMTc1NiBMIDQ4NyAxNzQ3IEwgNTAzIDE3MzAgTCA1MTAgMTcyMiBMIDUxOSAxNzEyIEwgNTI2IDE3MDQgTCA1MzggMTY5MSBMIDU0NyAxNjgwIEwgNTU4IDE2NjcgTCA1NjYgMTY1NiBMIDU3OSAxNjQwIEwgNTkwIDE2MjYgTCA2MDYgMTYwNSBMIDYyMSAxNTgzIEwgNjMxIDE1NjkgTCA2NDUgMTU0OSBMIDY1NSAxNTMzIEwgNjcwIDE1MTAgTCA2ODUgMTQ4NSBMIDY5NiAxNDY3IEwgNzA2IDE0NDkgTCA3MzAgMTQwNSBMIDc0NSAxMzc0IEwgNzYyIDEzMzggTCA3NzQgMTMxMCBMIDc5MiAxMjY3IEwgODExIDEyMTYgTCA4MjcgMTE2NiBMIDg0MiAxMTE3IEwgODU4IDEwNTEgTCA4NzAgOTk2IEwgODc1IDk2NyBMIDg4MSA5MTcgTCA4ODYgODgyIEwgODkxIDg0NiBMIDg5NCA4MTIgTCA4OTYgNzc0IEwgODk2IDY1NSBMIDg5NSA2MzAgTCA4OTIgNTkzIEwgODg4IDU1OSBMIDg3NyA0NzAgTCA4NzIgNDQ1IEwgODY1IDQyNiBMIDg1NyA0MTEgTCA4NDcgMzk1IEwgODM0IDM3OCBMIDgxNCAzNTggTCA4MDAgMzQ3IEwgNzg1IDMzNyBMIDc2NiAzMjcgTCA3NTAgMzIxIEwgNzI3IDMxNSBMIDY5MyAzMDggTCA2NTAgMzAxIEwgNTk3IDI5MiBMIDU3MCAyODYgTCA1NDggMjgwIEwgNTE1IDI3MCBMIDQ4NyAyNjEgTCA0NTkgMjUwIEwgNDM4IDI0MSBMIDQxNCAyMzAgTCAzODIgMjEzIEwgMzU4IDE5OSBMIDM0NyAxOTIgTCAzMzAgMTgwIEwgMzEyIDE2NyBMIDMwMCAxNTcgTCAyODYgMTQ2IEwgMjcxIDEzMyBMIDI2MyAxMjYgTCAyNTEgMTE1IEwgMjQwIDEwNiBMIDIyOCA5NSBMIDIwMCA3MSBMIDE4OSA2MiBMIDE3NCA1MCBMIDE1OCAzOCBMIDE0MiAyNyBMIDEyNyAxOSBMIDExNSAxNCBMIDk1IDggTCA3MSAwIEwgMCAwIHogTSAzMCA2MjIgTCA1MSA2MjMgTCA3OSA2MjcgTCAxMDAgNjMzIEwgMTIxIDY0MSBMIDE0MSA2NTIgTCAxNTcgNjYzIEwgMTcyIDY3NiBMIDE4NiA2OTAgTCAxODYgNjkyIEwgMTg4IDY5MiBMIDIwNiA3MTYgTCAyMTYgNzM0IEwgMjIxIDc0NSBMIDIyNiA3NTggTCAyMzMgNzg0IEwgMjM2IDc5OCBMIDIzNyA4MDggTCAyMzcgODQwIEwgMjMzIDg2NSBMIDIyNiA4OTEgTCAyMTkgOTA5IEwgMjA5IDkyOCBMIDE5NSA5NDkgTCAxODYgOTU5IEwgMTc5IDk2NyBMIDE2NiA5ODAgTCAxNTUgOTg5IEwgMTQyIDk5OCBMIDEyNiAxMDA3IEwgMTEzIDEwMTIgTCAxMTQgMTAyNCBMIDEyMiAxMDYwIEwgMTM3IDExMjMgTCAxNTYgMTIwOCBMIDE3MCAxMjcxIEwgMTc0IDEyOTYgTCAxNzYgMTMxMCBMIDE3NiAxMzM1IEwgMTczIDEzNDkgTCAxNjYgMTM1OSBMIDE1NyAxMzY3IEwgMTQ2IDEzNzIgTCAxNDAgMTM3NCBMIDExOSAxMzc2IEwgNTEgMTM3NiBMIC0zMiAxMzc3IEwgLTY0IDEzNzcgTCAtNzggMTM3NCBMIC04NSAxMzcwIEwgLTk4IDEzNTkgTCAtMTA2IDEzNDkgTCAtMTEwIDEzNDEgTCAtMTEyIDEzMzMgTCAtMTEyIDEzMTggTCAtMTA3IDEyODggTCAtMTAwIDEyNTYgTCAtODUgMTE5NiBMIC02OSAxMTI0IEwgLTU3IDEwNzIgTCAtNTAgMTAzNiBMIC00NyAxMDExIEwgLTUzIDEwMDkgTCAtNjkgMTAwMSBMIC04MSA5OTQgTCAtOTMgOTg1IEwgLTEwMyA5NzYgTCAtMTExIDk2OSBMIC0xMjAgOTYwIEwgLTEzMSA5NDYgTCAtMTQyIDkyOSBMIC0xNTIgOTEwIEwgLTE2MCA4OTAgTCAtMTY2IDg3MCBMIC0xNjkgODUzIEwgLTE3MCA4NDQgTCAtMTcwIDgxNCBMIC0xNjcgNzk0IEwgLTE2MSA3NjcgTCAtMTU0IDc0NiBMIC0xMzcgNzEzIEwgLTEyNiA2OTggTCAtMTE5IDY5MCBMIC0xMDggNjc4IEwgLTkzIDY2NSBMIC03NyA2NTMgTCAtNTYgNjQxIEwgLTM4IDYzMyBMIC0xNiA2MjcgTCAzIDYyNCBMIDMwIDYyMiB6ICIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOTkxKSIgc3R5bGU9ImZpbGw6I2ZmZmZmZiIgLz4KPC9zdmc+Cg==",
|
||||
"icon_light": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAyMDQ4IDIwNDgiIHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCB0cmFuc2Zvcm09InRyYW5zbGF0ZSg5OTEpIiBkPSJtMCAwaDcxbDI0IDggMjAgNiAxMiA1IDE1IDggMTYgMTEgMTYgMTIgMTUgMTIgMTEgOSAyOCAyNCAxMiAxMSAxMSA5IDEyIDExIDggNyAxNSAxMyAxNCAxMSAxMiAxMCAxOCAxMyAxNyAxMiAxMSA3IDI0IDE0IDMyIDE3IDI0IDExIDIxIDkgMjggMTEgMjggOSAzMyAxMCAyMiA2IDI3IDYgNTMgOSA0MyA3IDM0IDcgMjMgNiAxNiA2IDE5IDEwIDE1IDEwIDE0IDExIDIwIDIwIDEzIDE3IDEwIDE2IDggMTUgNyAxOSA1IDI1IDExIDg5IDQgMzQgMyAzNyAxIDI1djExOWwtMiAzOC0zIDM0LTUgMzYtNSAzNS02IDUwLTUgMjktMTIgNTUtMTYgNjYtMTUgNDktMTYgNTAtMTkgNTEtMTggNDMtMTIgMjgtMTcgMzYtMTUgMzEtMjQgNDQtMTAgMTgtMTEgMTgtMTUgMjUtMTUgMjMtMTAgMTYtMTQgMjAtMTAgMTQtMTUgMjItMTYgMjEtMTEgMTQtMTMgMTYtOCAxMS0xMSAxMy05IDExLTEyIDEzLTcgOC05IDEwLTcgOC0xNiAxNy04IDloLTJsLTIgNC00OSA0OS04IDctNyA3LTggNy03IDctMTEgOS0zIDNoLTJ2MmwtMTEgOS0xMSAxMC05IDctMTEgMTAtMTQgMTEtMTggMTMtMTEgOS0xMSA4LTE4IDEzLTM1IDI0LTIxIDE0LTI0IDE1LTE5IDExLTE4IDEwLTI4IDE0LTIxIDgtMjcgNy0xMiAzLTEgMWgtNDhsLTQ0LTExLTIwLTctMjAtOS0xNi04LTI0LTEzLTIxLTEzLTE1LTEwLTIwLTE0LTE2LTExLTEyLTktMTktMTQtMTMtMTAtMTYtMTItMTctMTQtMTAtOC0xNy0xNC0yNi0yMi0xMy0xMi04LTctMTYtMTUtMTctMTYtNTctNTctNy04LTEwLTEwLTctOC0xMS0xMi0xMC0xMS05LTExLTI4LTM0LTEzLTE2LTEzLTE3LTExLTE1LTE0LTE5LTEzLTE4LTEzLTE5LTEwLTE1LTEzLTIwLTEzLTIxLTE5LTMyLTE0LTI1LTE1LTI4LTIxLTQyLTE1LTMzLTE2LTM4LTE0LTM2LTEwLTI5LTctMTgtMTQtNDUtMTgtNjQtNi0yNi0xMS01Mi0xMi02My00LTI5LTYtNjItNi03Mi0zLTUyLTEtMzB2LTU5bDItNjEgMy00NyA1LTQ1IDYtMzUgNi0xOCA5LTE5IDEwLTE4IDEwLTE1IDgtMTAgNS02aDJsMi00IDgtOCAxMS05IDEwLTcgMjEtMTMgMTYtOCAxNy02IDM0LTggMzItNyA2NC0xMSA0OS05IDMwLTggMjMtOCA0Mi0xNCAyMS05IDMzLTE1IDIzLTExIDIxLTEyIDE4LTEwIDIxLTE0IDIwLTEzIDExLTggMTMtMTAgMTQtMTEgMTEtOSAxMy0xMSA4LTcgOS05aDJ2LTJsOC03IDEwLTEwaDJsMS0zIDgtNyAxMy0xMyAxMS05IDctNyAxOS0xNCAxNC05IDE5LTExIDE5LTcgMjMtNyAxMC0zeiIgZmlsbD0iI0YxODQyOSIvPgo8cGF0aCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMDIxLDYyMikiIGQ9Im0wIDAgMjEgMSAyOCA0IDIxIDYgMjEgOCAyMCAxMSAxNiAxMSAxNSAxMyAxNCAxNHYyaDJsMTggMjQgMTAgMTggNSAxMSA1IDEzIDcgMjYgMyAxNCAxIDEwdjMybC00IDI1LTcgMjYtNyAxOC0xMCAxOS0xNCAyMS05IDEwLTcgOC0xMyAxMy0xMSA5LTEzIDktMTYgOS0xMyA1IDEgMTIgOCAzNiAxNSA2MyAxOSA4NSAxNCA2MyA0IDI1IDIgMTR2MjVsLTMgMTQtNyAxMC05IDgtMTEgNS02IDItMjEgMmgtNjhsLTgzIDFoLTMybC0xNC0zLTctNC0xMy0xMS04LTEwLTQtOC0yLTh2LTE1bDUtMzAgNy0zMiAxNS02MCAxNi03MiAxMi01MiA3LTM2IDMtMjUtNi0yLTE2LTgtMTItNy0xMi05LTEwLTktOC03LTktOS0xMS0xNC0xMS0xNy0xMC0xOS04LTIwLTYtMjAtMy0xNy0xLTl2LTMwbDMtMjAgNi0yNyA3LTIxIDE3LTMzIDExLTE1IDctOCAxMS0xMiAxNS0xMyAxNi0xMiAyMS0xMiAxOC04IDIyLTYgMTktM3oiIGZpbGw9IiNGRUZGRkUiLz4KPC9zdmc+Cg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"a10c6dd9-465e-4226-8198-c7c44b91c555": {
|
||||
"name": "Kaspersky Password Manager",
|
||||
"icon_dark": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzc0ODRfODc0NSkiPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMV83NDg0Xzg3NDUpIj48ZyBjbGlwLXBhdGg9InVybCgjY2xpcDJfNzQ4NF84NzQ1KSI+PHBhdGggZD0iTTI4MS45NjQgNi45NjE3MUMyNjUuODkzIC0yLjI5OTc0IDI0Ni4xMDcgLTIuMjk5NzQgMjMwLjAzNiA2Ljk2MTcxTDQ2LjAzNjUgMTEzLjAwMkMyOS45MjcxIDEyMi4yODYgMjAgMTM5LjQ2NiAyMCAxNTguMDZWMzUzLjk3MkMyMCAzNzIuNTY2IDI5LjkyNzEgMzg5Ljc0NSA0Ni4wMzY1IDM5OS4wMjlMMjMwLjAzNiA1MDUuMDdDMjQ2LjEwNyA1MTQuMzMxIDI2NS44OTMgNTE0LjMzMSAyODEuOTY0IDUwNS4wN0w0NjUuOTY0IDM5OS4wMjlDNDgyLjA3MyAzODkuNzQ1IDQ5MiAzNzIuNTY2IDQ5MiAzNTMuOTcyVjE1OC4wNkM0OTIgMTM5LjQ2NiA0ODIuMDczIDEyMi4yODYgNDY1Ljk2NCAxMTMuMDAyTDI4MS45NjQgNi45NjE3MVoiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl83NDg0Xzg3NDUpIi8+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00NTMuOTggMTMzLjc5OEwyNjkuOTggMjcuNzU3NEMyNjEuMzI3IDIyLjc3MDQgMjUwLjY3MyAyMi43NzA0IDI0Mi4wMiAyNy43NTc0TDU4LjAxOTYgMTMzLjc5OEM0OS4zNDUzIDEzOC43OTcgNDQgMTQ4LjA0NyA0NCAxNTguMDZWMzUzLjk3MkM0NCAzNjMuOTg0IDQ5LjM0NTMgMzczLjIzNCA1OC4wMTk2IDM3OC4yMzNMMjQyLjAyIDQ4NC4yNzRDMjUwLjY3MyA0ODkuMjYxIDI2MS4zMjcgNDg5LjI2MSAyNjkuOTggNDg0LjI3NEw0NTMuOTggMzc4LjIzM0M0NjIuNjU1IDM3My4yMzQgNDY4IDM2My45ODQgNDY4IDM1My45NzJWMTU4LjA2QzQ2OCAxNDguMDQ3IDQ2Mi42NTUgMTM4Ljc5NyA0NTMuOTggMTMzLjc5OFpNMjgxLjk2NCA2Ljk2MTcxQzI2NS44OTMgLTIuMjk5NzQgMjQ2LjEwNyAtMi4yOTk3NCAyMzAuMDM2IDYuOTYxNzFMNDYuMDM2NSAxMTMuMDAyQzI5LjkyNzEgMTIyLjI4NiAyMCAxMzkuNDY2IDIwIDE1OC4wNlYzNTMuOTcyQzIwIDM3Mi41NjYgMjkuOTI3MSAzODkuNzQ1IDQ2LjAzNjUgMzk5LjAyOUwyMzAuMDM2IDUwNS4wN0MyNDYuMTA3IDUxNC4zMzEgMjY1Ljg5MyA1MTQuMzMxIDI4MS45NjQgNTA1LjA3TDQ2NS45NjQgMzk5LjAyOUM0ODIuMDczIDM4OS43NDUgNDkyIDM3Mi41NjYgNDkyIDM1My45NzJWMTU4LjA2QzQ5MiAxMzkuNDY2IDQ4Mi4wNzMgMTIyLjI4NiA0NjUuOTY0IDExMy4wMDJMMjgxLjk2NCA2Ljk2MTcxWiIgZmlsbD0iYmxhY2siLz48L2c+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNTYgMTQwQzI0Mi43NDUgMTQwIDIzMiAxNTAuNzQ1IDIzMiAxNjRDMjMyIDE3Ny4yNTUgMjQyLjc0NSAxODggMjU2IDE4OEMyNjkuMjU1IDE4OCAyODAgMTc3LjI1NSAyODAgMTY0QzI4MCAxNTAuNzQ1IDI2OS4yNTUgMTQwIDI1NiAxNDBaTTI0OCAxNjRDMjQ4IDE1OS41ODIgMjUxLjU4MiAxNTYgMjU2IDE1NkMyNjAuNDE4IDE1NiAyNjQgMTU5LjU4MiAyNjQgMTY0QzI2NCAxNjguNDE4IDI2MC40MTggMTcyIDI1NiAxNzJDMjUxLjU4MiAxNzIgMjQ4IDE2OC40MTggMjQ4IDE2NFoiIGZpbGw9ImJsYWNrIi8+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNzIgMTkyQzE3MiAxNDUuNjA4IDIwOS42MDggMTA4IDI1NiAxMDhDMzAyLjM5MiAxMDggMzQwIDE0NS42MDggMzQwIDE5MkMzNDAgMjI3LjA2MSAzMTguNTE5IDI1Ny4xMDUgMjg4IDI2OS42OVYzODYuNjdDMjg4IDM5Mi4zOTEgMjg0Ljk0NiAzOTcuNjc2IDI3OS45ODkgNDAwLjUzM0wyNjMuOTg5IDQwOS43NTNDMjU5LjA0NCA0MTIuNjAzIDI1Mi45NTYgNDEyLjYwMyAyNDguMDExIDQwOS43NTNMMjMyLjAxMSA0MDAuNTMzQzIyNy4wNTQgMzk3LjY3NiAyMjQgMzkyLjM5MSAyMjQgMzg2LjY3VjM3MkMyMjQgMzY5Ljg3OCAyMjQuODQzIDM2Ny44NDQgMjI2LjM0MyAzNjYuMzQzTDIzNiAzNTYuNjg2VjM1NS4zMTRMMjI2LjM0MyAzNDUuNjU3QzIyNC44NDMgMzQ0LjE1NyAyMjQgMzQyLjEyMiAyMjQgMzQwVjMzMkMyMjQgMzI5Ljg3OCAyMjQuODQzIDMyNy44NDQgMjI2LjM0MyAzMjYuMzQzTDIzNiAzMTYuNjg2VjMxNS4zMTRMMjI2LjM0MyAzMDUuNjU3QzIyNC44NDMgMzA0LjE1NyAyMjQgMzAyLjEyMiAyMjQgMzAwVjI2OS42OUMxOTMuNDgxIDI1Ny4xMDUgMTcyIDIyNy4wNjEgMTcyIDE5MlpNMjU2IDEyNEMyMTguNDQ1IDEyNCAxODggMTU0LjQ0NSAxODggMTkyQzE4OCAyMjkuNTU1IDIxOC40NDUgMjYwIDI1NiAyNjBDMjkzLjU1NSAyNjAgMzI0IDIyOS41NTUgMzI0IDE5MkMzMjQgMTU0LjQ0NSAyOTMuNTU1IDEyNCAyNTYgMTI0Wk0yNTYgMjc2QzI2MS40NzEgMjc2IDI2Ni44MiAyNzUuNDc3IDI3MiAyNzQuNDc4VjM4Ni42N0wyNTYgMzk1Ljg5TDI0MCAzODYuNjdWMzc1LjMxNEwyNDkuNjU3IDM2NS42NTdDMjUxLjE1NyAzNjQuMTU3IDI1MiAzNjIuMTIyIDI1MiAzNjBWMzUyQzI1MiAzNDkuODc4IDI1MS4xNTcgMzQ3Ljg0NCAyNDkuNjU3IDM0Ni4zNDNMMjQwIDMzNi42ODZWMzM1LjMxNEwyNDkuNjU3IDMyNS42NTdDMjUxLjE1NyAzMjQuMTU3IDI1MiAzMjIuMTIyIDI1MiAzMjBWMzEyQzI1MiAzMDkuODc4IDI1MS4xNTcgMzA3Ljg0NCAyNDkuNjU3IDMwNi4zNDNMMjQwIDI5Ni42ODZWMjc0LjQ3OEMyNDUuMTggMjc1LjQ3NyAyNTAuNTI5IDI3NiAyNTYgMjc2WiIgZmlsbD0iYmxhY2siLz48L2c+PC9nPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl83NDg0Xzg3NDUiIHgxPSIzOTMuODY1IiB5MT0iNjMuMjc5NiIgeDI9Ijk5LjIwNDMiIHkyPSI0MjkuOTk4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agc3RvcC1jb2xvcj0iIzRERkY4OCIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzNERThDQSIvPjwvbGluZWFyR3JhZGllbnQ+PGNsaXBQYXRoIGlkPSJjbGlwMF83NDg0Xzg3NDUiPjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJjbGlwMV83NDg0Xzg3NDUiPjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJjbGlwMl83NDg0Xzg3NDUiPjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPjwvY2xpcFBhdGg+PC9kZWZzPjwvc3ZnPg==",
|
||||
"icon_light": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzc0ODRfODc0NSkiPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMV83NDg0Xzg3NDUpIj48ZyBjbGlwLXBhdGg9InVybCgjY2xpcDJfNzQ4NF84NzQ1KSI+PHBhdGggZD0iTTI4MS45NjQgNi45NjE3MUMyNjUuODkzIC0yLjI5OTc0IDI0Ni4xMDcgLTIuMjk5NzQgMjMwLjAzNiA2Ljk2MTcxTDQ2LjAzNjUgMTEzLjAwMkMyOS45MjcxIDEyMi4yODYgMjAgMTM5LjQ2NiAyMCAxNTguMDZWMzUzLjk3MkMyMCAzNzIuNTY2IDI5LjkyNzEgMzg5Ljc0NSA0Ni4wMzY1IDM5OS4wMjlMMjMwLjAzNiA1MDUuMDdDMjQ2LjEwNyA1MTQuMzMxIDI2NS44OTMgNTE0LjMzMSAyODEuOTY0IDUwNS4wN0w0NjUuOTY0IDM5OS4wMjlDNDgyLjA3MyAzODkuNzQ1IDQ5MiAzNzIuNTY2IDQ5MiAzNTMuOTcyVjE1OC4wNkM0OTIgMTM5LjQ2NiA0ODIuMDczIDEyMi4yODYgNDY1Ljk2NCAxMTMuMDAyTDI4MS45NjQgNi45NjE3MVoiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl83NDg0Xzg3NDUpIi8+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00NTMuOTggMTMzLjc5OEwyNjkuOTggMjcuNzU3NEMyNjEuMzI3IDIyLjc3MDQgMjUwLjY3MyAyMi43NzA0IDI0Mi4wMiAyNy43NTc0TDU4LjAxOTYgMTMzLjc5OEM0OS4zNDUzIDEzOC43OTcgNDQgMTQ4LjA0NyA0NCAxNTguMDZWMzUzLjk3MkM0NCAzNjMuOTg0IDQ5LjM0NTMgMzczLjIzNCA1OC4wMTk2IDM3OC4yMzNMMjQyLjAyIDQ4NC4yNzRDMjUwLjY3MyA0ODkuMjYxIDI2MS4zMjcgNDg5LjI2MSAyNjkuOTggNDg0LjI3NEw0NTMuOTggMzc4LjIzM0M0NjIuNjU1IDM3My4yMzQgNDY4IDM2My45ODQgNDY4IDM1My45NzJWMTU4LjA2QzQ2OCAxNDguMDQ3IDQ2Mi42NTUgMTM4Ljc5NyA0NTMuOTggMTMzLjc5OFpNMjgxLjk2NCA2Ljk2MTcxQzI2NS44OTMgLTIuMjk5NzQgMjQ2LjEwNyAtMi4yOTk3NCAyMzAuMDM2IDYuOTYxNzFMNDYuMDM2NSAxMTMuMDAyQzI5LjkyNzEgMTIyLjI4NiAyMCAxMzkuNDY2IDIwIDE1OC4wNlYzNTMuOTcyQzIwIDM3Mi41NjYgMjkuOTI3MSAzODkuNzQ1IDQ2LjAzNjUgMzk5LjAyOUwyMzAuMDM2IDUwNS4wN0MyNDYuMTA3IDUxNC4zMzEgMjY1Ljg5MyA1MTQuMzMxIDI4MS45NjQgNTA1LjA3TDQ2NS45NjQgMzk5LjAyOUM0ODIuMDczIDM4OS43NDUgNDkyIDM3Mi41NjYgNDkyIDM1My45NzJWMTU4LjA2QzQ5MiAxMzkuNDY2IDQ4Mi4wNzMgMTIyLjI4NiA0NjUuOTY0IDExMy4wMDJMMjgxLjk2NCA2Ljk2MTcxWiIgZmlsbD0iYmxhY2siLz48L2c+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNTYgMTQwQzI0Mi43NDUgMTQwIDIzMiAxNTAuNzQ1IDIzMiAxNjRDMjMyIDE3Ny4yNTUgMjQyLjc0NSAxODggMjU2IDE4OEMyNjkuMjU1IDE4OCAyODAgMTc3LjI1NSAyODAgMTY0QzI4MCAxNTAuNzQ1IDI2OS4yNTUgMTQwIDI1NiAxNDBaTTI0OCAxNjRDMjQ4IDE1OS41ODIgMjUxLjU4MiAxNTYgMjU2IDE1NkMyNjAuNDE4IDE1NiAyNjQgMTU5LjU4MiAyNjQgMTY0QzI2NCAxNjguNDE4IDI2MC40MTggMTcyIDI1NiAxNzJDMjUxLjU4MiAxNzIgMjQ4IDE2OC40MTggMjQ4IDE2NFoiIGZpbGw9ImJsYWNrIi8+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0xNzIgMTkyQzE3MiAxNDUuNjA4IDIwOS42MDggMTA4IDI1NiAxMDhDMzAyLjM5MiAxMDggMzQwIDE0NS42MDggMzQwIDE5MkMzNDAgMjI3LjA2MSAzMTguNTE5IDI1Ny4xMDUgMjg4IDI2OS42OVYzODYuNjdDMjg4IDM5Mi4zOTEgMjg0Ljk0NiAzOTcuNjc2IDI3OS45ODkgNDAwLjUzM0wyNjMuOTg5IDQwOS43NTNDMjU5LjA0NCA0MTIuNjAzIDI1Mi45NTYgNDEyLjYwMyAyNDguMDExIDQwOS43NTNMMjMyLjAxMSA0MDAuNTMzQzIyNy4wNTQgMzk3LjY3NiAyMjQgMzkyLjM5MSAyMjQgMzg2LjY3VjM3MkMyMjQgMzY5Ljg3OCAyMjQuODQzIDM2Ny44NDQgMjI2LjM0MyAzNjYuMzQzTDIzNiAzNTYuNjg2VjM1NS4zMTRMMjI2LjM0MyAzNDUuNjU3QzIyNC44NDMgMzQ0LjE1NyAyMjQgMzQyLjEyMiAyMjQgMzQwVjMzMkMyMjQgMzI5Ljg3OCAyMjQuODQzIDMyNy44NDQgMjI2LjM0MyAzMjYuMzQzTDIzNiAzMTYuNjg2VjMxNS4zMTRMMjI2LjM0MyAzMDUuNjU3QzIyNC44NDMgMzA0LjE1NyAyMjQgMzAyLjEyMiAyMjQgMzAwVjI2OS42OUMxOTMuNDgxIDI1Ny4xMDUgMTcyIDIyNy4wNjEgMTcyIDE5MlpNMjU2IDEyNEMyMTguNDQ1IDEyNCAxODggMTU0LjQ0NSAxODggMTkyQzE4OCAyMjkuNTU1IDIxOC40NDUgMjYwIDI1NiAyNjBDMjkzLjU1NSAyNjAgMzI0IDIyOS41NTUgMzI0IDE5MkMzMjQgMTU0LjQ0NSAyOTMuNTU1IDEyNCAyNTYgMTI0Wk0yNTYgMjc2QzI2MS40NzEgMjc2IDI2Ni44MiAyNzUuNDc3IDI3MiAyNzQuNDc4VjM4Ni42N0wyNTYgMzk1Ljg5TDI0MCAzODYuNjdWMzc1LjMxNEwyNDkuNjU3IDM2NS42NTdDMjUxLjE1NyAzNjQuMTU3IDI1MiAzNjIuMTIyIDI1MiAzNjBWMzUyQzI1MiAzNDkuODc4IDI1MS4xNTcgMzQ3Ljg0NCAyNDkuNjU3IDM0Ni4zNDNMMjQwIDMzNi42ODZWMzM1LjMxNEwyNDkuNjU3IDMyNS42NTdDMjUxLjE1NyAzMjQuMTU3IDI1MiAzMjIuMTIyIDI1MiAzMjBWMzEyQzI1MiAzMDkuODc4IDI1MS4xNTcgMzA3Ljg0NCAyNDkuNjU3IDMwNi4zNDNMMjQwIDI5Ni42ODZWMjc0LjQ3OEMyNDUuMTggMjc1LjQ3NyAyNTAuNTI5IDI3NiAyNTYgMjc2WiIgZmlsbD0iYmxhY2siLz48L2c+PC9nPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl83NDg0Xzg3NDUiIHgxPSIzOTMuODY1IiB5MT0iNjMuMjc5NiIgeDI9Ijk5LjIwNDMiIHkyPSI0MjkuOTk4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agc3RvcC1jb2xvcj0iIzRERkY4OCIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzNERThDQSIvPjwvbGluZWFyR3JhZGllbnQ+PGNsaXBQYXRoIGlkPSJjbGlwMF83NDg0Xzg3NDUiPjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJjbGlwMV83NDg0Xzg3NDUiPjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPjwvY2xpcFBhdGg+PGNsaXBQYXRoIGlkPSJjbGlwMl83NDg0Xzg3NDUiPjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPjwvY2xpcFBhdGg+PC9kZWZzPjwvc3ZnPg=="
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -5,7 +5,7 @@ entries:
|
||||
- attrs:
|
||||
designation: stage_configuration
|
||||
name: default-authenticator-totp-setup
|
||||
title: Setup Two-Factor authentication
|
||||
title: Set up Two-Factor authentication
|
||||
authentication: require_authenticated
|
||||
identifiers:
|
||||
slug: default-authenticator-totp-setup
|
||||
|
@ -6669,6 +6669,11 @@
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Filter group"
|
||||
},
|
||||
"dry_run": {
|
||||
"type": "boolean",
|
||||
"title": "Dry run",
|
||||
"description": "When enabled, provider will not modify or create objects in the remote system."
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
@ -14196,6 +14201,11 @@
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Default group email domain"
|
||||
},
|
||||
"dry_run": {
|
||||
"type": "boolean",
|
||||
"title": "Dry run",
|
||||
"description": "When enabled, provider will not modify or create objects in the remote system."
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
@ -14344,6 +14354,11 @@
|
||||
"suspend"
|
||||
],
|
||||
"title": "Group delete action"
|
||||
},
|
||||
"dry_run": {
|
||||
"type": "boolean",
|
||||
"title": "Dry run",
|
||||
"description": "When enabled, provider will not modify or create objects in the remote system."
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
8
go.mod
8
go.mod
@ -22,17 +22,17 @@ require (
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484
|
||||
github.com/pires/go-proxyproto v0.8.0
|
||||
github.com/prometheus/client_golang v1.21.0
|
||||
github.com/prometheus/client_golang v1.21.1
|
||||
github.com/redis/go-redis/v9 v9.7.1
|
||||
github.com/sethvargo/go-envconfig v1.1.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/wwt/guac v1.3.2
|
||||
goauthentik.io/api/v3 v3.2025020.1
|
||||
goauthentik.io/api/v3 v3.2025021.2
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.27.0
|
||||
golang.org/x/sync v0.11.0
|
||||
golang.org/x/oauth2 v0.28.0
|
||||
golang.org/x/sync v0.12.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
layeh.com/radius v0.0.0-20210819152912-ad72663a72ab
|
||||
)
|
||||
|
16
go.sum
16
go.sum
@ -239,8 +239,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA=
|
||||
github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
||||
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
|
||||
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
@ -299,8 +299,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
goauthentik.io/api/v3 v3.2025020.1 h1:7922W4XiGif7lUCl2qlaeQJ3wSx1wDDDpXx8ryx0Hv0=
|
||||
goauthentik.io/api/v3 v3.2025020.1/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
goauthentik.io/api/v3 v3.2025021.2 h1:9y87piH47omtkWxQpKZaKai/+jh+cJdLxj5MC2Y/ZLI=
|
||||
goauthentik.io/api/v3 v3.2025021.2/go.mod h1:zz+mEZg8rY/7eEjkMGWJ2DnGqk+zqxuybGCGrR2O4Kw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@ -394,8 +394,8 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -410,8 +410,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
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.1001.0",
|
||||
"aws-cdk": "^2.1003.0",
|
||||
"cross-env": "^7.0.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -17,9 +17,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk": {
|
||||
"version": "2.1001.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1001.0.tgz",
|
||||
"integrity": "sha512-Wp6fKNXcxBm+f8U1GkLV4gEgqq1pu5uwyDCMBg7ZB/6CtP+PsD/mPhuKyMULNWucDvYN8oy70XLOkMnxa3NWFw==",
|
||||
"version": "2.1003.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1003.0.tgz",
|
||||
"integrity": "sha512-FORPDGW8oUg4tXFlhX+lv/j+152LO9wwi3/CwNr1WY3c3HwJUtc0fZGb2B3+Fzy6NhLWGHJclUsJPEhjEt8Nhg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
|
@ -10,7 +10,7 @@
|
||||
"node": ">=20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.1001.0",
|
||||
"aws-cdk": "^2.1003.0",
|
||||
"cross-env": "^7.0.3"
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,9 @@ def wait_for_db():
|
||||
# Sanity check, ensure SECRET_KEY is set before we even check for database connectivity
|
||||
if CONFIG.get("secret_key") is None or len(CONFIG.get("secret_key")) == 0:
|
||||
CONFIG.log("info", "----------------------------------------------------------------------")
|
||||
CONFIG.log("info", "Secret key missing, check https://goauthentik.io/docs/installation/.")
|
||||
CONFIG.log(
|
||||
"info", "Secret key missing, check https://docs.goauthentik.io/docs/install-config/"
|
||||
)
|
||||
CONFIG.log("info", "----------------------------------------------------------------------")
|
||||
sysexit(1)
|
||||
check_postgres()
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-02-25 00:11+0000\n"
|
||||
"POT-Creation-Date: 2025-03-02 00:10+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -865,6 +865,12 @@ msgstr ""
|
||||
msgid "Invalid next URL"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/models.py
|
||||
msgid ""
|
||||
"When enabled, provider will not modify or create objects in the remote "
|
||||
"system."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Starting full provider sync"
|
||||
msgstr ""
|
||||
@ -879,6 +885,10 @@ msgstr ""
|
||||
msgid "Syncing page {page} of groups"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Dropping mutating request due to dry run"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Stopping sync due to error: {error}"
|
||||
@ -1365,6 +1375,14 @@ msgstr ""
|
||||
msgid "ES256 (Asymmetric Encryption)"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "ES384 (Asymmetric Encryption)"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "ES512 (Asymmetric Encryption)"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "Scope used by the client"
|
||||
msgstr ""
|
||||
|
Binary file not shown.
@ -19,7 +19,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-02-25 00:11+0000\n"
|
||||
"POT-Creation-Date: 2025-03-02 00:10+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: Marc Schmitt, 2025\n"
|
||||
"Language-Team: French (https://app.transifex.com/authentik/teams/119923/fr/)\n"
|
||||
@ -948,6 +948,14 @@ msgstr "Jetons du flux"
|
||||
msgid "Invalid next URL"
|
||||
msgstr "URL suivante invalide"
|
||||
|
||||
#: authentik/lib/sync/outgoing/models.py
|
||||
msgid ""
|
||||
"When enabled, provider will not modify or create objects in the remote "
|
||||
"system."
|
||||
msgstr ""
|
||||
"Si activé, le fournisseur ne changera ou ne créera pas d'objets auprès du "
|
||||
"système distant."
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Starting full provider sync"
|
||||
msgstr "Démarrage d'une synchronisation complète du fournisseur"
|
||||
@ -962,6 +970,10 @@ msgstr "Synchronisation de la page {page} d'utilisateurs"
|
||||
msgid "Syncing page {page} of groups"
|
||||
msgstr "Synchronisation de la page {page} de groupes"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Dropping mutating request due to dry run"
|
||||
msgstr "Abandon de la requête de mutation en raison d'une simulation"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Stopping sync due to error: {error}"
|
||||
@ -1510,6 +1522,14 @@ msgstr "RS256 (chiffrement asymétrique)"
|
||||
msgid "ES256 (Asymmetric Encryption)"
|
||||
msgstr "ES256 (Chiffrement Asymétrique)"
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "ES384 (Asymmetric Encryption)"
|
||||
msgstr "ES384 (chiffrement asymétrique)"
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "ES512 (Asymmetric Encryption)"
|
||||
msgstr "ES512 (chiffrement asymétrique)"
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "Scope used by the client"
|
||||
msgstr "Portées utilisées par le client"
|
||||
|
Binary file not shown.
Binary file not shown.
@ -15,7 +15,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-02-25 00:11+0000\n"
|
||||
"POT-Creation-Date: 2025-03-02 00:10+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2025\n"
|
||||
"Language-Team: Chinese Simplified (https://app.transifex.com/authentik/teams/119923/zh-Hans/)\n"
|
||||
@ -878,6 +878,12 @@ msgstr "流程令牌"
|
||||
msgid "Invalid next URL"
|
||||
msgstr "无效的 next URL"
|
||||
|
||||
#: authentik/lib/sync/outgoing/models.py
|
||||
msgid ""
|
||||
"When enabled, provider will not modify or create objects in the remote "
|
||||
"system."
|
||||
msgstr "启用时,提供程序将不会在远程系统上修改或创建对象。"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Starting full provider sync"
|
||||
msgstr "开始全量提供程序同步"
|
||||
@ -892,6 +898,10 @@ msgstr "正在同步用户页面 {page}"
|
||||
msgid "Syncing page {page} of groups"
|
||||
msgstr "正在同步群组页面 {page}"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Dropping mutating request due to dry run"
|
||||
msgstr "由于启用了试运行,已放弃变更请求"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Stopping sync due to error: {error}"
|
||||
@ -1389,6 +1399,14 @@ msgstr "RS256(非对称加密)"
|
||||
msgid "ES256 (Asymmetric Encryption)"
|
||||
msgstr "ES256(非对称加密)"
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "ES384 (Asymmetric Encryption)"
|
||||
msgstr "ES384(非对称加密)"
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "ES512 (Asymmetric Encryption)"
|
||||
msgstr "ES512(非对称加密)"
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "Scope used by the client"
|
||||
msgstr "客户端使用的作用域"
|
||||
|
Binary file not shown.
@ -14,7 +14,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-02-25 00:11+0000\n"
|
||||
"POT-Creation-Date: 2025-03-02 00:10+0000\n"
|
||||
"PO-Revision-Date: 2022-09-26 16:47+0000\n"
|
||||
"Last-Translator: deluxghost, 2025\n"
|
||||
"Language-Team: Chinese (China) (https://app.transifex.com/authentik/teams/119923/zh_CN/)\n"
|
||||
@ -877,6 +877,12 @@ msgstr "流程令牌"
|
||||
msgid "Invalid next URL"
|
||||
msgstr "无效的 next URL"
|
||||
|
||||
#: authentik/lib/sync/outgoing/models.py
|
||||
msgid ""
|
||||
"When enabled, provider will not modify or create objects in the remote "
|
||||
"system."
|
||||
msgstr "启用时,提供程序将不会在远程系统上修改或创建对象。"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Starting full provider sync"
|
||||
msgstr "开始全量提供程序同步"
|
||||
@ -891,6 +897,10 @@ msgstr "正在同步用户页面 {page}"
|
||||
msgid "Syncing page {page} of groups"
|
||||
msgstr "正在同步群组页面 {page}"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
msgid "Dropping mutating request due to dry run"
|
||||
msgstr "由于启用了试运行,已放弃变更请求"
|
||||
|
||||
#: authentik/lib/sync/outgoing/tasks.py
|
||||
#, python-brace-format
|
||||
msgid "Stopping sync due to error: {error}"
|
||||
@ -1388,6 +1398,14 @@ msgstr "RS256(非对称加密)"
|
||||
msgid "ES256 (Asymmetric Encryption)"
|
||||
msgstr "ES256(非对称加密)"
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "ES384 (Asymmetric Encryption)"
|
||||
msgstr "ES384(非对称加密)"
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "ES512 (Asymmetric Encryption)"
|
||||
msgstr "ES512(非对称加密)"
|
||||
|
||||
#: authentik/providers/oauth2/models.py
|
||||
msgid "Scope used by the client"
|
||||
msgstr "客户端使用的作用域"
|
||||
|
567
poetry.lock
generated
567
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,27 @@ version = "2025.2.1"
|
||||
description = ""
|
||||
authors = ["authentik Team <hello@goauthentik.io>"]
|
||||
|
||||
[tool.bandit]
|
||||
exclude_dirs = ["**/node_modules/**"]
|
||||
|
||||
[tool.codespell]
|
||||
skip = [
|
||||
"**/node_modules",
|
||||
"**/package-lock.json",
|
||||
"schema.yml",
|
||||
"unittest.xml",
|
||||
"./blueprints/schema.json",
|
||||
"go.sum",
|
||||
"locale",
|
||||
"**/dist",
|
||||
"**/web/src/locales",
|
||||
"**/web/xliff",
|
||||
"./website/build",
|
||||
"./gen-ts-api",
|
||||
"*.api.mdx",
|
||||
]
|
||||
dictionary = ".github/codespell-dictionary.txt,-"
|
||||
ignore-words = ".github/codespell-words.txt"
|
||||
[tool.black]
|
||||
line-length = 100
|
||||
target-version = ['py312']
|
||||
@ -123,7 +144,9 @@ kubernetes = "*"
|
||||
ldap3 = "*"
|
||||
lxml = "*"
|
||||
msgraph-sdk = "*"
|
||||
opencontainers = { git = "https://github.com/vsoch/oci-python", rev = "20d69d9cc50a0fef31605b46f06da0c94f1ec3cf", extras = ["reggie"] }
|
||||
opencontainers = { git = "https://github.com/vsoch/oci-python", rev = "20d69d9cc50a0fef31605b46f06da0c94f1ec3cf", extras = [
|
||||
"reggie",
|
||||
] }
|
||||
packaging = "*"
|
||||
paramiko = "*"
|
||||
psycopg = { extras = ["c"], version = "*" }
|
||||
|
39
schema.yml
39
schema.yml
@ -44154,6 +44154,10 @@ components:
|
||||
$ref: '#/components/schemas/OutgoingSyncDeleteAction'
|
||||
default_group_email_domain:
|
||||
type: string
|
||||
dry_run:
|
||||
type: boolean
|
||||
description: When enabled, provider will not modify or create objects in
|
||||
the remote system.
|
||||
required:
|
||||
- assigned_backchannel_application_name
|
||||
- assigned_backchannel_application_slug
|
||||
@ -44317,6 +44321,10 @@ components:
|
||||
default_group_email_domain:
|
||||
type: string
|
||||
minLength: 1
|
||||
dry_run:
|
||||
type: boolean
|
||||
description: When enabled, provider will not modify or create objects in
|
||||
the remote system.
|
||||
required:
|
||||
- credentials
|
||||
- default_group_email_domain
|
||||
@ -46397,6 +46405,10 @@ components:
|
||||
$ref: '#/components/schemas/OutgoingSyncDeleteAction'
|
||||
group_delete_action:
|
||||
$ref: '#/components/schemas/OutgoingSyncDeleteAction'
|
||||
dry_run:
|
||||
type: boolean
|
||||
description: When enabled, provider will not modify or create objects in
|
||||
the remote system.
|
||||
required:
|
||||
- assigned_backchannel_application_name
|
||||
- assigned_backchannel_application_slug
|
||||
@ -46557,6 +46569,10 @@ components:
|
||||
$ref: '#/components/schemas/OutgoingSyncDeleteAction'
|
||||
group_delete_action:
|
||||
$ref: '#/components/schemas/OutgoingSyncDeleteAction'
|
||||
dry_run:
|
||||
type: boolean
|
||||
description: When enabled, provider will not modify or create objects in
|
||||
the remote system.
|
||||
required:
|
||||
- client_id
|
||||
- client_secret
|
||||
@ -50679,6 +50695,10 @@ components:
|
||||
default_group_email_domain:
|
||||
type: string
|
||||
minLength: 1
|
||||
dry_run:
|
||||
type: boolean
|
||||
description: When enabled, provider will not modify or create objects in
|
||||
the remote system.
|
||||
PatchedGroupKerberosSourceConnectionRequest:
|
||||
type: object
|
||||
description: OAuth Group-Source connection Serializer
|
||||
@ -51260,6 +51280,10 @@ components:
|
||||
$ref: '#/components/schemas/OutgoingSyncDeleteAction'
|
||||
group_delete_action:
|
||||
$ref: '#/components/schemas/OutgoingSyncDeleteAction'
|
||||
dry_run:
|
||||
type: boolean
|
||||
description: When enabled, provider will not modify or create objects in
|
||||
the remote system.
|
||||
PatchedNotificationRequest:
|
||||
type: object
|
||||
description: Notification Serializer
|
||||
@ -52427,6 +52451,10 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
dry_run:
|
||||
type: boolean
|
||||
description: When enabled, provider will not modify or create objects in
|
||||
the remote system.
|
||||
PatchedSCIMSourceGroupRequest:
|
||||
type: object
|
||||
description: SCIMSourceGroup Serializer
|
||||
@ -55823,6 +55851,10 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
dry_run:
|
||||
type: boolean
|
||||
description: When enabled, provider will not modify or create objects in
|
||||
the remote system.
|
||||
required:
|
||||
- assigned_backchannel_application_name
|
||||
- assigned_backchannel_application_slug
|
||||
@ -55909,6 +55941,10 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
dry_run:
|
||||
type: boolean
|
||||
description: When enabled, provider will not modify or create objects in
|
||||
the remote system.
|
||||
required:
|
||||
- name
|
||||
- token
|
||||
@ -57105,6 +57141,9 @@ components:
|
||||
sync_object_id:
|
||||
type: string
|
||||
minLength: 1
|
||||
override_dry_run:
|
||||
type: boolean
|
||||
default: false
|
||||
required:
|
||||
- sync_object_id
|
||||
- sync_object_model
|
||||
|
1
scripts/generate_config.py
Normal file → Executable file
1
scripts/generate_config.py
Normal file → Executable file
@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate config for development"""
|
||||
|
||||
from yaml import safe_dump
|
||||
|
15
scripts/generate_semver.py
Executable file
15
scripts/generate_semver.py
Executable file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generates a Semantic Versioning identifier, suffixed with a timestamp.
|
||||
"""
|
||||
|
||||
from time import time
|
||||
|
||||
from authentik import __version__ as package_version
|
||||
|
||||
"""
|
||||
See: https://semver.org/#spec-item-9 (Pre-release spec)
|
||||
"""
|
||||
pre_release_timestamp = int(time())
|
||||
|
||||
print(f"{package_version}-{pre_release_timestamp}")
|
@ -1,7 +0,0 @@
|
||||
"""Helper script to generate an NPM Version"""
|
||||
|
||||
from time import time
|
||||
|
||||
from authentik import __version__
|
||||
|
||||
print(f"{__version__}-{int(time())}")
|
33
web/package-lock.json
generated
33
web/package-lock.json
generated
@ -23,7 +23,7 @@
|
||||
"@floating-ui/dom": "^1.6.11",
|
||||
"@formatjs/intl-listformat": "^7.5.7",
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@goauthentik/api": "^2025.2.1-1740653734",
|
||||
"@goauthentik/api": "^2025.2.1-1740858273",
|
||||
"@lit-labs/ssr": "^3.2.2",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@lit/localize": "^0.12.2",
|
||||
@ -37,11 +37,12 @@
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"chart.js": "^4.4.4",
|
||||
"chartjs-adapter-moment": "^1.0.1",
|
||||
"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",
|
||||
"dompurify": "^3.2.4",
|
||||
"fuse.js": "^7.0.0",
|
||||
"guacamole-common-js": "^1.5.0",
|
||||
@ -1815,9 +1816,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@goauthentik/api": {
|
||||
"version": "2025.2.1-1740653734",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2025.2.1-1740653734.tgz",
|
||||
"integrity": "sha512-GRxBt52lgZOvEu7l9DN1lj0L2Q9KUiftrC9MWfaz3dIlw1s+kKzic/NTTlB7AaEsRqw7+i10aI6GkiKAErw2VA=="
|
||||
"version": "2025.2.1-1740858273",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2025.2.1-1740858273.tgz",
|
||||
"integrity": "sha512-dOY32RKJSy3fYteuL4h13NaRVznq0O9Z0IXdwUDwkCQ4GoBfqUaPIFb55tFvRNrdNZG0efor0PIGISxoHpRJwA=="
|
||||
},
|
||||
"node_modules/@goauthentik/web": {
|
||||
"resolved": "",
|
||||
@ -9532,13 +9533,13 @@
|
||||
"pnpm": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/chartjs-adapter-moment": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz",
|
||||
"integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==",
|
||||
"node_modules/chartjs-adapter-date-fns": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz",
|
||||
"integrity": "sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==",
|
||||
"peerDependencies": {
|
||||
"chart.js": ">=3.0.0",
|
||||
"moment": "^2.10.2"
|
||||
"chart.js": ">=2.8.0",
|
||||
"date-fns": ">=2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cheerio": {
|
||||
@ -10770,6 +10771,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
||||
@ -16580,6 +16590,7 @@
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
"@floating-ui/dom": "^1.6.11",
|
||||
"@formatjs/intl-listformat": "^7.5.7",
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@goauthentik/api": "^2025.2.1-1740653734",
|
||||
"@goauthentik/api": "^2025.2.1-1740858273",
|
||||
"@lit-labs/ssr": "^3.2.2",
|
||||
"@lit/context": "^1.1.2",
|
||||
"@lit/localize": "^0.12.2",
|
||||
@ -25,11 +25,12 @@
|
||||
"@webcomponents/webcomponentsjs": "^2.8.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"chart.js": "^4.4.4",
|
||||
"chartjs-adapter-moment": "^1.0.1",
|
||||
"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",
|
||||
"dompurify": "^3.2.4",
|
||||
"fuse.js": "^7.0.0",
|
||||
"guacamole-common-js": "^1.5.0",
|
||||
|
@ -20,7 +20,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
|
||||
import { Application, CoreApi } from "@goauthentik/api";
|
||||
import { Application, CoreApi, PoliciesApi } from "@goauthentik/api";
|
||||
|
||||
import "./ApplicationWizardHint";
|
||||
|
||||
@ -172,6 +172,29 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${msg("Create")}</button>
|
||||
</ak-forms-modal>`;
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html` ${super.renderToolbar()}
|
||||
<ak-forms-confirm
|
||||
successMessage=${msg("Successfully cleared application cache")}
|
||||
errorMessage=${msg("Failed to delete application cache")}
|
||||
action=${msg("Clear cache")}
|
||||
.onConfirm=${() => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesAllCacheClearCreate();
|
||||
}}
|
||||
>
|
||||
<span slot="header"> ${msg("Clear Application cache")} </span>
|
||||
<p slot="body">
|
||||
${msg(
|
||||
"Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.",
|
||||
)}
|
||||
</p>
|
||||
<button slot="trigger" class="pf-c-button pf-m-secondary" type="button">
|
||||
${msg("Clear cache")}
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-forms-confirm>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@ -328,7 +328,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
||||
if (!(this.wizard && app && provider)) {
|
||||
throw new Error("Submit step received uninitialized wizard context");
|
||||
}
|
||||
// An empty object is truthy, an empty array is falsey. *WAT Javascript*.
|
||||
// An empty object is truthy, an empty array is falsey. *WAT JavaScript*.
|
||||
const keys = Object.keys(this.wizard.errors);
|
||||
return match([this.state, keys])
|
||||
.with(["submitted", P._], () =>
|
||||
|
@ -5,15 +5,32 @@ import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
import { TableColumn } from "@goauthentik/elements/table/Table";
|
||||
import { TableModal } from "@goauthentik/elements/table/TableModal";
|
||||
import { match } from "ts-pattern";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import { CoreApi, User } from "@goauthentik/api";
|
||||
import { CoreApi, CoreUsersListRequest, User } from "@goauthentik/api";
|
||||
|
||||
// Leaving room in the future for a multi-state control if someone somehow needs to filter inactive
|
||||
// users as well.
|
||||
type UserListFilter = "active" | "all";
|
||||
type UserListRequestFilter = Partial<Pick<CoreUsersListRequest, "isActive">>;
|
||||
|
||||
@customElement("ak-group-member-select-table")
|
||||
export class MemberSelectTable extends TableModal<User> {
|
||||
static get styles() {
|
||||
return [
|
||||
...super.styles,
|
||||
css`
|
||||
.show-disabled-toggle-group {
|
||||
margin-inline-start: 0.5rem;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
checkbox = true;
|
||||
checkboxChip = true;
|
||||
|
||||
@ -24,11 +41,22 @@ export class MemberSelectTable extends TableModal<User> {
|
||||
@property()
|
||||
confirm!: (selectedItems: User[]) => Promise<unknown>;
|
||||
|
||||
userListFilter: UserListFilter = "active";
|
||||
|
||||
order = "username";
|
||||
|
||||
// The `userListRequestFilter` clause is necessary because the back-end for searches is
|
||||
// tri-state: `isActive: true` will only show active users, `isActive: false` will show only
|
||||
// inactive users; only when it's _missing_ will you get all users.
|
||||
async apiEndpoint(): Promise<PaginatedResponse<User>> {
|
||||
const userListRequestFilter: UserListRequestFilter = match(this.userListFilter)
|
||||
.with("all", () => ({}))
|
||||
.with("active", () => ({ isActive: true }))
|
||||
.exhaustive();
|
||||
|
||||
return new CoreApi(DEFAULT_CONFIG).coreUsersList({
|
||||
...(await this.defaultEndpointConfig()),
|
||||
...userListRequestFilter,
|
||||
includeGroups: false,
|
||||
});
|
||||
}
|
||||
@ -41,6 +69,36 @@ export class MemberSelectTable extends TableModal<User> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarAfter() {
|
||||
const toggleShowDisabledUsers = () => {
|
||||
this.userListFilter = this.userListFilter === "all" ? "active" : "all";
|
||||
this.page = 1;
|
||||
this.fetch();
|
||||
};
|
||||
|
||||
return html`
|
||||
<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
<div class="pf-c-input-group show-disabled-toggle-group">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${this.userListFilter === "all"}
|
||||
@change=${toggleShowDisabledUsers}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"></i>
|
||||
</span>
|
||||
</span>
|
||||
<span class="pf-c-switch__label">${msg("Show inactive users")}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
row(item: User): TemplateResult[] {
|
||||
return [
|
||||
html`<div>${item.username}</div>
|
||||
|
@ -161,6 +161,26 @@ export class GoogleWorkspaceProviderFormPage extends BaseProviderForm<GoogleWork
|
||||
help=${msg("Determines what authentik will do when a Group is deleted.")}
|
||||
>
|
||||
</ak-radio-input>
|
||||
<ak-form-element-horizontal name="dryRun">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${first(this.instance?.dryRun, false)}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"></i>
|
||||
</span>
|
||||
</span>
|
||||
<span class="pf-c-switch__label">${msg("Enable dry-run mode")}</span>
|
||||
</label>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"When enabled, mutating requests will be dropped and logged instead.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group ?expanded=${true}>
|
||||
|
@ -4,6 +4,7 @@ import "@goauthentik/admin/providers/google_workspace/GoogleWorkspaceProviderUse
|
||||
import "@goauthentik/admin/rbac/ObjectPermissionsPage";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import "@goauthentik/components/ak-status-label";
|
||||
import "@goauthentik/components/events/ObjectChangelog";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/Markdown";
|
||||
@ -176,6 +177,23 @@ export class GoogleWorkspaceProviderViewPage extends AKElement {
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${msg("Dry-run")}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-status-label
|
||||
?good=${!this.provider.dryRun}
|
||||
type="info"
|
||||
good-label=${msg("No")}
|
||||
bad-label=${msg("Yes")}
|
||||
></ak-status-label>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
|
@ -150,6 +150,26 @@ export class MicrosoftEntraProviderFormPage extends BaseProviderForm<MicrosoftEn
|
||||
help=${msg("Determines what authentik will do when a Group is deleted.")}
|
||||
>
|
||||
</ak-radio-input>
|
||||
<ak-form-element-horizontal name="dryRun">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${first(this.instance?.dryRun, false)}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"></i>
|
||||
</span>
|
||||
</span>
|
||||
<span class="pf-c-switch__label">${msg("Enable dry-run mode")}</span>
|
||||
</label>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"When enabled, mutating requests will be dropped and logged instead.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group ?expanded=${true}>
|
||||
|
@ -176,6 +176,23 @@ export class MicrosoftEntraProviderViewPage extends AKElement {
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${msg("Dry-run")}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-status-label
|
||||
?good=${!this.provider.dryRun}
|
||||
type="info"
|
||||
good-label=${msg("No")}
|
||||
bad-label=${msg("Yes")}
|
||||
></ak-status-label>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="pf-c-card__footer">
|
||||
|
@ -25,7 +25,7 @@ import "@goauthentik/elements/buttons/SpinnerButton";
|
||||
import { getURLParam } from "@goauthentik/elements/router/RouteMatch";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, PropertyValues, TemplateResult, html } from "lit";
|
||||
import { CSSResult, PropertyValues, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
|
||||
import PFBanner from "@patternfly/patternfly/components/Banner/banner.css";
|
||||
@ -94,6 +94,11 @@ export class ProxyProviderViewPage extends AKElement {
|
||||
PFCard,
|
||||
PFDescriptionList,
|
||||
PFBanner,
|
||||
css`
|
||||
:host(:not([theme="dark"])) .ak-markdown-section {
|
||||
background-color: var(--pf-c-card--BackgroundColor);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@ -188,7 +193,7 @@ export class ProxyProviderViewPage extends AKElement {
|
||||
return html`<section
|
||||
slot="page-${convertToSlug(server.label)}"
|
||||
data-tab-title="${server.label}"
|
||||
class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile"
|
||||
class="pf-c-page__main-section pf-m-no-padding-mobile ak-markdown-section"
|
||||
>
|
||||
<ak-markdown
|
||||
.replacers=${replacers}
|
||||
|
@ -61,6 +61,26 @@ export function renderForm(provider?: Partial<SCIMProvider>, errors: ValidationE
|
||||
)}
|
||||
inputHint="code"
|
||||
></ak-text-input>
|
||||
<ak-form-element-horizontal name="dryRun">
|
||||
<label class="pf-c-switch">
|
||||
<input
|
||||
class="pf-c-switch__input"
|
||||
type="checkbox"
|
||||
?checked=${first(provider?.dryRun, false)}
|
||||
/>
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"></i>
|
||||
</span>
|
||||
</span>
|
||||
<span class="pf-c-switch__label">${msg("Enable dry-run mode")}</span>
|
||||
</label>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"When enabled, mutating requests will be dropped and logged instead.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group expanded>
|
||||
|
@ -5,6 +5,7 @@ import "@goauthentik/admin/providers/scim/SCIMProviderUserList";
|
||||
import "@goauthentik/admin/rbac/ObjectPermissionsPage";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import "@goauthentik/components/ak-status-label";
|
||||
import "@goauthentik/components/events/ObjectChangelog";
|
||||
import MDSCIMProvider from "@goauthentik/docs/add-secure-apps/providers/scim/index.md";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
@ -151,7 +152,7 @@ export class SCIMProviderViewPage extends AKElement {
|
||||
<div class="pf-l-grid__item pf-m-7-col pf-l-stack pf-m-gutter">
|
||||
<div class="pf-c-card pf-m-12-col pf-l-stack__item">
|
||||
<div class="pf-c-card__body">
|
||||
<dl class="pf-c-description-list pf-m-3-col-on-lg">
|
||||
<dl class="pf-c-description-list pf-m-4-col-on-lg">
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
@ -178,7 +179,23 @@ export class SCIMProviderViewPage extends AKElement {
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
>${msg("Dry-run")}</span
|
||||
>
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<ak-status-label
|
||||
?good=${!this.provider.dryRun}
|
||||
type="info"
|
||||
good-label=${msg("No")}
|
||||
bad-label=${msg("Yes")}
|
||||
></ak-status-label>
|
||||
</div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="pf-c-description-list__group">
|
||||
<dt class="pf-c-description-list__term">
|
||||
<span class="pf-c-description-list__text"
|
||||
|
@ -248,7 +248,7 @@ export class UserListPage extends WithBrandConfig(WithCapabilitiesConfig(TablePa
|
||||
return [
|
||||
html`<a href="#/identity/users/${item.pk}">
|
||||
<div>${item.username}</div>
|
||||
<small>${item.name === "" ? msg("<No name set>") : item.name}</small>
|
||||
<small>${item.name ? item.name : html`<${msg("No name set")}>`}</small>
|
||||
</a>`,
|
||||
html`<ak-status-label ?good=${item.isActive}></ak-status-label>`,
|
||||
html`${item.lastLogin
|
||||
|
@ -17,7 +17,7 @@ import { Legend, Tooltip } from "chart.js";
|
||||
import { BarController, DoughnutController, LineController } from "chart.js";
|
||||
import { ArcElement, BarElement } from "chart.js";
|
||||
import { LinearScale, TimeScale } from "chart.js";
|
||||
import "chartjs-adapter-moment";
|
||||
import "chartjs-adapter-date-fns";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
|
@ -119,12 +119,28 @@ export class SyncObjectForm extends Form<SyncObjectRequest> {
|
||||
|
||||
renderForm() {
|
||||
return html` ${this.model === SyncObjectModelEnum.AuthentikCoreModelsUser
|
||||
? this.renderSelectUser()
|
||||
: nothing}
|
||||
${this.model === SyncObjectModelEnum.AuthentikCoreModelsGroup
|
||||
? this.renderSelectGroup()
|
||||
: nothing}
|
||||
${this.result ? this.renderResult() : html``}`;
|
||||
? this.renderSelectUser()
|
||||
: nothing}
|
||||
${this.model === SyncObjectModelEnum.AuthentikCoreModelsGroup
|
||||
? this.renderSelectGroup()
|
||||
: nothing}
|
||||
<ak-form-element-horizontal name="overrideDryRun">
|
||||
<label class="pf-c-switch">
|
||||
<input class="pf-c-switch__input" type="checkbox" />
|
||||
<span class="pf-c-switch__toggle">
|
||||
<span class="pf-c-switch__toggle-icon">
|
||||
<i class="fas fa-check" aria-hidden="true"></i>
|
||||
</span>
|
||||
</span>
|
||||
<span class="pf-c-switch__label">${msg("Override dry-run mode")}</span>
|
||||
</label>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.",
|
||||
)}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
${this.result ? this.renderResult() : html``}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,10 @@ export class UserSettingsPage extends AKElement {
|
||||
:host([theme="dark"]) .pf-c-page__main-section {
|
||||
--pf-c-page__main-section--BackgroundColor: transparent;
|
||||
}
|
||||
.pf-c-page__main {
|
||||
min-height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
@media screen and (min-width: 1200px) {
|
||||
:host {
|
||||
width: 90rem;
|
||||
|
@ -7276,9 +7276,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>User Statistics</source>
|
||||
<target>Benutzerstatistiken</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>Benutzertyp, der für neu angelegte Benutzer verwendet wird.</target>
|
||||
@ -9016,6 +9013,39 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -5934,9 +5934,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sb4d7bae2440d9781">
|
||||
<source>User Statistics</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
</trans-unit>
|
||||
@ -7543,6 +7540,39 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7303,10 +7303,6 @@ Las vinculaciones a grupos o usuarios se comparan con el usuario del evento.</ta
|
||||
<source>User Statistics</source>
|
||||
<target>Estadísticas de Usuario</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
<target><No name set></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>Tipo de usuario utilizado para usuarios recién creados.</target>
|
||||
@ -9109,6 +9105,39 @@ Las vinculaciones a grupos o usuarios se comparan con el usuario del evento.</ta
|
||||
</trans-unit>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7451,10 +7451,6 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
<source>User Statistics</source>
|
||||
<target>Statistiques Utilisateur</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
<target><No name set></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>Type d'utilisateur pour les utilisateurs nouvellement créés.</target>
|
||||
@ -9602,6 +9598,44 @@ Les liaisons avec les groupes/utilisateurs sont vérifiées par rapport à l'uti
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
<target>Jetons envoyés par courriel.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
<target>Activer le mode simulation</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
<target>Si activé, les requêtes de mutations seront abandonnées et affichées à la place.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
<target>Forcer la désactivation du mode simulation</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
<target>Si activé, la synchronisation exécutera les requêtes de mutations sans vérifier le réglage du mode simulation du fournisseur.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
<target>Mode simulation</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7436,10 +7436,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>User Statistics</source>
|
||||
<target>Statistiche degli utenti</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
<target><No name set></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>Tipo di utente utilizzato per gli utenti appena creati.</target>
|
||||
@ -9460,6 +9456,39 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7375,10 +7375,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>User Statistics</source>
|
||||
<target>사용자 통계</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
<target><설정된 이름 없음></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>새로 생성된 사용자에 사용되는 사용자 유형입니다.</target>
|
||||
@ -9016,6 +9012,39 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7307,9 +7307,6 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
<trans-unit id="sb4d7bae2440d9781">
|
||||
<source>User Statistics</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
</trans-unit>
|
||||
@ -8917,6 +8914,39 @@ Bindingen naar groepen/gebruikers worden gecontroleerd tegen de gebruiker van de
|
||||
</trans-unit>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7453,10 +7453,6 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
<source>User Statistics</source>
|
||||
<target>Statystyki użytkowników</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
<target><No name set></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>Typ użytkownika używany dla nowo utworzonych użytkowników.</target>
|
||||
@ -9346,6 +9342,39 @@ Powiązania z grupami/użytkownikami są sprawdzane względem użytkownika zdarz
|
||||
</trans-unit>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7398,10 +7398,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>User Statistics</source>
|
||||
<target>Ũśēŕ Śţàţĩśţĩćś</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
<target><Ńō ńàḿē śēţ></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>Ũśēŕ ţŷƥē ũśēď ƒōŕ ńēŵĺŷ ćŕēàţēď ũśēŕś.</target>
|
||||
@ -9353,4 +9349,37 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
</body></file></xliff>
|
||||
|
@ -7452,10 +7452,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>User Statistics</source>
|
||||
<target>Статистика пользователей</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
<target><No name set></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>Тип пользователя, используемый для вновь созданных пользователей.</target>
|
||||
@ -9379,6 +9375,39 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7409,10 +7409,6 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
|
||||
<source>User Statistics</source>
|
||||
<target>Kullanıcı İstatistikleri</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
<target><İsim belirlenmedi></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>Yeni oluşturulan kullanıcılar için kullanılan kullanıcı türü.</target>
|
||||
@ -9409,6 +9405,39 @@ Gruplara/kullanıcılara yapılan bağlamalar, etkinliğin kullanıcısına kar
|
||||
</trans-unit>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -2864,9 +2864,6 @@ doesn't pass when either or both of the selected options are equal or above the
|
||||
<trans-unit id="s510c7add9e24c306">
|
||||
<source>Hide deactivated user</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s895514dda9cb9c94">
|
||||
<source>Create recovery link</source>
|
||||
</trans-unit>
|
||||
@ -6146,6 +6143,39 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
@ -7452,10 +7452,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>User Statistics</source>
|
||||
<target>用户统计</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
<target><未设置名称></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>新创建用户使用的用户类型。</target>
|
||||
@ -9603,6 +9599,50 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
<target>通过电子邮件发送的令牌。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
<target>启用试运行模式</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
<target>启用时,变更请求将会被放弃,仅记录日志。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
<target>覆盖试运行模式</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
<target>启用时,此同步仍将执行变更请求,无论提供程序是否启用试运行模式。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
<target>试运行</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
<target>已成功清除应用程序缓存</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
<target>删除应用程序缓存失败</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
<target>清除应用程序缓存</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
<target>确实要清除应用程序缓存吗?这将导致所有策略在下次使用时重新评估。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
<target>未设置名称</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
<target>显示不活跃的用户</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -5634,9 +5634,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="sb4d7bae2440d9781">
|
||||
<source>User Statistics</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
</trans-unit>
|
||||
@ -7243,6 +7240,39 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7452,10 +7452,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>User Statistics</source>
|
||||
<target>用户统计</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
<target><未设置名称></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>新创建用户使用的用户类型。</target>
|
||||
@ -9603,6 +9599,50 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
<target>通过电子邮件发送的令牌。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
<target>启用试运行模式</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
<target>启用时,变更请求将会被放弃,仅记录日志。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
<target>覆盖试运行模式</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
<target>启用时,此同步仍将执行变更请求,无论提供程序是否启用试运行模式。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
<target>试运行</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
<target>已成功清除应用程序缓存</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
<target>删除应用程序缓存失败</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
<target>清除应用程序缓存</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
<target>确实要清除应用程序缓存吗?这将导致所有策略在下次使用时重新评估。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
<target>未设置名称</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
<target>显示不活跃的用户</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -7363,10 +7363,6 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
<source>User Statistics</source>
|
||||
<target>使用者統計資料</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s0924f51b028233a3">
|
||||
<source><No name set></source>
|
||||
<target><No name set></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="s32babfed740fd3c1">
|
||||
<source>User type used for newly created users.</source>
|
||||
<target>用於建立使用者的使用者類型。</target>
|
||||
@ -8993,6 +8989,39 @@ Bindings to groups/users are checked against the user of the event.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s833cfe815918c143">
|
||||
<source>Tokens sent via email.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4d3933feb8dc7cee">
|
||||
<source>Enable dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s8c9ca09b5eeb5ae9">
|
||||
<source>When enabled, mutating requests will be dropped and logged instead.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc4bf1a365af696eb">
|
||||
<source>Override dry-run mode</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s94a5233931be4110">
|
||||
<source>When enabled, this sync will still execute mutating requests regardless of the dry-run mode in the provider.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sc2473aae093ce256">
|
||||
<source>Dry-run</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s34661765d81e7340">
|
||||
<source>Successfully cleared application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9377ae285bc0157c">
|
||||
<source>Failed to delete application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s754153d9e524ffce">
|
||||
<source>Clear Application cache</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sea5092df2c72b064">
|
||||
<source>Are you sure you want to clear the application cache? This will cause all policies to be re-evaluated on their next usage.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s1367dcd6e8749fcd">
|
||||
<source>No name set</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s85a91d843c3eb4ad">
|
||||
<source>Show inactive users</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
2
website/.gitignore
vendored
2
website/.gitignore
vendored
@ -24,5 +24,5 @@ yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
static/docker-compose.yml
|
||||
static/schema.yaml
|
||||
static/schema.yml
|
||||
docs/developer-docs/api/reference/**
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user