Compare commits
1 Commits
trustchain
...
expression
Author | SHA1 | Date |
---|---|---|
Marc 'risson' Schmitt | 5945b36200 |
|
@ -194,6 +194,7 @@ pip-selfcheck.json
|
|||
# End of https://www.gitignore.io/api/python,django
|
||||
/static/
|
||||
local.env.yml
|
||||
/variables/
|
||||
|
||||
media/
|
||||
*mmdb
|
||||
|
|
|
@ -106,6 +106,7 @@ default_token_length: 60
|
|||
impersonation: true
|
||||
|
||||
blueprints_dir: /blueprints
|
||||
variables_discovery_dir: /data/variables
|
||||
|
||||
web:
|
||||
# No default here as it's set dynamically
|
||||
|
|
|
@ -1,10 +1,32 @@
|
|||
"""Expression Policy API"""
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.policies.api.policies import PolicySerializer
|
||||
from authentik.policies.expression.evaluator import PolicyEvaluator
|
||||
from authentik.policies.expression.models import ExpressionPolicy
|
||||
from authentik.policies.expression.models import ExpressionPolicy, ExpressionVariable
|
||||
|
||||
|
||||
class ExpressionVariableSerializer(ModelSerializer):
|
||||
"""Expression Variable Serializer"""
|
||||
|
||||
class Meta:
|
||||
model = ExpressionVariable
|
||||
fields = "__all__"
|
||||
extra_kwargs = {
|
||||
"managed": {"read_only": True},
|
||||
}
|
||||
|
||||
|
||||
class ExpressionVariableViewSet(UsedByMixin, ModelViewSet):
|
||||
"""Expression Variable Viewset"""
|
||||
|
||||
queryset = ExpressionVariable.objects.all()
|
||||
serializer_class = ExpressionVariableSerializer
|
||||
filterset_fields = "__all__"
|
||||
ordering = ["name"]
|
||||
search_fields = ["name"]
|
||||
|
||||
|
||||
class ExpressionPolicySerializer(PolicySerializer):
|
||||
|
@ -18,7 +40,7 @@ class ExpressionPolicySerializer(PolicySerializer):
|
|||
|
||||
class Meta:
|
||||
model = ExpressionPolicy
|
||||
fields = PolicySerializer.Meta.fields + ["expression"]
|
||||
fields = PolicySerializer.Meta.fields + ["expression", "variables"]
|
||||
|
||||
|
||||
class ExpressionPolicyViewSet(UsedByMixin, ModelViewSet):
|
||||
|
|
|
@ -13,7 +13,7 @@ from authentik.policies.types import PolicyRequest, PolicyResult
|
|||
|
||||
LOGGER = get_logger()
|
||||
if TYPE_CHECKING:
|
||||
from authentik.policies.expression.models import ExpressionPolicy
|
||||
from authentik.policies.expression.models import ExpressionPolicy, ExpressionVariable
|
||||
|
||||
|
||||
class PolicyEvaluator(BaseEvaluator):
|
||||
|
@ -30,6 +30,7 @@ class PolicyEvaluator(BaseEvaluator):
|
|||
# update website/docs/expressions/_functions.md
|
||||
self._context["ak_message"] = self.expr_func_message
|
||||
self._context["ak_user_has_authenticator"] = self.expr_func_user_has_authenticator
|
||||
self._context["ak_variables"] = {}
|
||||
|
||||
def expr_func_message(self, message: str):
|
||||
"""Wrapper to append to messages list, which is returned with PolicyResult"""
|
||||
|
@ -52,6 +53,12 @@ class PolicyEvaluator(BaseEvaluator):
|
|||
self._context["ak_client_ip"] = ip_address(get_client_ip(request))
|
||||
self._context["http_request"] = request
|
||||
|
||||
def set_variables(self, variables: list["ExpressionVariable"]):
|
||||
"""Update context base on expression policy variables"""
|
||||
for variable in variables:
|
||||
variable.reload()
|
||||
self._context["ak_variables"][variable.name] = variable.value
|
||||
|
||||
def handle_error(self, exc: Exception, expression_source: str):
|
||||
"""Exception Handler"""
|
||||
raise PolicyException(exc)
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
# Generated by Django 4.2.5 on 2023-09-29 00:25
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("authentik_policies_expression", "0004_expressionpolicy_authentik_p_policy__fb6feb_idx"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="ExpressionVariable",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
|
||||
),
|
||||
),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("last_updated", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"managed",
|
||||
models.TextField(
|
||||
default=None,
|
||||
help_text="Objects that are managed by authentik. These objects are created and updated automatically. This flag only indicates that an object can be overwritten by migrations. You can still modify the objects via the API, but expect changes to be overwritten in a later update.",
|
||||
null=True,
|
||||
unique=True,
|
||||
verbose_name="Managed by authentik",
|
||||
),
|
||||
),
|
||||
("name", models.TextField(unique=True)),
|
||||
("value", models.TextField()),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Expression Variable",
|
||||
"verbose_name_plural": "Expression Variables",
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="expressionpolicy",
|
||||
name="variables",
|
||||
field=models.ManyToManyField(
|
||||
blank=True, to="authentik_policies_expression.expressionvariable"
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,18 +1,66 @@
|
|||
"""authentik expression Policy Models"""
|
||||
from pathlib import Path
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext as _
|
||||
from rest_framework.serializers import BaseSerializer
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.blueprints.models import ManagedModel
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.models import CreatedUpdatedModel, SerializerModel
|
||||
from authentik.policies.expression.evaluator import PolicyEvaluator
|
||||
from authentik.policies.models import Policy
|
||||
from authentik.policies.types import PolicyRequest, PolicyResult
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
MANAGED_DISCOVERED = "goauthentik.io/variables/discovered/%s"
|
||||
|
||||
|
||||
class ExpressionVariable(SerializerModel, ManagedModel, CreatedUpdatedModel):
|
||||
"""Variable that can be given to expression policies"""
|
||||
|
||||
name = models.TextField(unique=True)
|
||||
value = models.TextField()
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[BaseSerializer]:
|
||||
from authentik.policies.expression.api import ExpressionVariableSerializer
|
||||
|
||||
return ExpressionVariableSerializer
|
||||
|
||||
def reload(self):
|
||||
"""Reload a variable from disk if it's managed"""
|
||||
if self.managed != MANAGED_DISCOVERED % self.name:
|
||||
return
|
||||
path = Path(CONFIG.get("variables_discovery_dir")) / Path(self.name)
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8") as _file:
|
||||
body = _file.read()
|
||||
if body != self.value:
|
||||
self.value = body
|
||||
self.save()
|
||||
except (OSError, ValueError) as exc:
|
||||
LOGGER.warning(
|
||||
"Failed to reload variable, continuing anyway",
|
||||
exc=exc,
|
||||
file=path,
|
||||
variable=self.name,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Expression Variable")
|
||||
verbose_name_plural = _("Expression Variables")
|
||||
|
||||
|
||||
class ExpressionPolicy(Policy):
|
||||
"""Execute arbitrary Python code to implement custom checks and validation."""
|
||||
|
||||
expression = models.TextField()
|
||||
|
||||
variables = models.ManyToManyField(ExpressionVariable, blank=True)
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[BaseSerializer]:
|
||||
from authentik.policies.expression.api import ExpressionPolicySerializer
|
||||
|
@ -28,6 +76,7 @@ class ExpressionPolicy(Policy):
|
|||
evaluator = PolicyEvaluator(self.name)
|
||||
evaluator.policy = self
|
||||
evaluator.set_policy_request(request)
|
||||
evaluator.set_variables(self.variables)
|
||||
return evaluator.evaluate(self.expression)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
"""Expression tasks"""
|
||||
from glob import glob
|
||||
from pathlib import Path
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from structlog.stdlib import get_logger
|
||||
from watchdog.events import (
|
||||
FileCreatedEvent,
|
||||
FileModifiedEvent,
|
||||
FileSystemEvent,
|
||||
FileSystemEventHandler,
|
||||
)
|
||||
from watchdog.observers import Observer
|
||||
|
||||
from authentik.events.monitored_tasks import (
|
||||
MonitoredTask,
|
||||
TaskResult,
|
||||
TaskResultStatus,
|
||||
prefill_task,
|
||||
)
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.policies.expression.models import MANAGED_DISCOVERED, ExpressionVariable
|
||||
from authentik.root.celery import CELERY_APP
|
||||
|
||||
LOGGER = get_logger()
|
||||
_file_watcher_started = False
|
||||
|
||||
|
||||
@CELERY_APP.task(bind=True, base=MonitoredTask)
|
||||
@prefill_task
|
||||
def variable_discovery(self: MonitoredTask):
|
||||
"""Discover, import and update variables from the filesystem"""
|
||||
variables = {}
|
||||
discovered = 0
|
||||
base_path = Path(CONFIG.get("variables_discovery_dir")).absolute()
|
||||
for file in glob(str(base_path) + "/**", recursive=True):
|
||||
path = Path(file)
|
||||
if not path.exists():
|
||||
continue
|
||||
if path.is_dir():
|
||||
continue
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8") as _file:
|
||||
body = _file.read()
|
||||
variables[str(path.relative_to(base_path))] = body
|
||||
discovered += 1
|
||||
except (OSError, ValueError) as exc:
|
||||
LOGGER.warning("Failed to open file", exc=exc, file=path)
|
||||
for name, value in variables.items():
|
||||
variable = ExpressionVariable.objects.filter(managed=MANAGED_DISCOVERED % name).first()
|
||||
if not variable:
|
||||
variable = ExpressionVariable(name=name, managed=MANAGED_DISCOVERED % name)
|
||||
if variable.value != value:
|
||||
variable.value = value
|
||||
variable.save()
|
||||
self.set_status(
|
||||
TaskResult(
|
||||
TaskResultStatus.SUCCESSFUL,
|
||||
messages=[_("Successfully imported %(count)d files." % {"count": discovered})],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class VariableEventHandler(FileSystemEventHandler):
|
||||
"""Event handler for variable events"""
|
||||
|
||||
def on_any_event(self, event: FileSystemEvent):
|
||||
if not isinstance(event, (FileCreatedEvent, FileModifiedEvent)):
|
||||
return
|
||||
if event.is_directory:
|
||||
return
|
||||
LOGGER.debug("variable file changed, starting discovery", file=event.src_path)
|
||||
variable_discovery.delay()
|
||||
|
||||
|
||||
def start_variables_watcher():
|
||||
"""Start variables watcher, if it's not running already."""
|
||||
# This function might be called twice since it's called on celery startup
|
||||
# pylint: disable=global-statement
|
||||
global _file_watcher_started
|
||||
if _file_watcher_started:
|
||||
return
|
||||
observer = Observer()
|
||||
observer.schedule(VariableEventHandler(), CONFIG.get("variables_discovery_dir"), recursive=True)
|
||||
observer.start()
|
||||
_file_watcher_started = True
|
|
@ -1,4 +1,7 @@
|
|||
"""API URLs"""
|
||||
from authentik.policies.expression.api import ExpressionPolicyViewSet
|
||||
from authentik.policies.expression.api import ExpressionPolicyViewSet, ExpressionVariableViewSet
|
||||
|
||||
api_urlpatterns = [("policies/expression", ExpressionPolicyViewSet)]
|
||||
api_urlpatterns = [
|
||||
("policies/expression/variables", ExpressionVariableViewSet),
|
||||
("policies/expression", ExpressionPolicyViewSet),
|
||||
]
|
||||
|
|
|
@ -105,8 +105,10 @@ def worker_ready_hook(*args, **kwargs):
|
|||
except ProgrammingError as exc:
|
||||
LOGGER.warning("Startup task failed", task=task, exc=exc)
|
||||
from authentik.blueprints.v1.tasks import start_blueprint_watcher
|
||||
from authentik.policies.expression.tasks import start_variables_watcher
|
||||
|
||||
start_blueprint_watcher()
|
||||
start_variables_watcher()
|
||||
|
||||
|
||||
class LivenessProbe(bootsteps.StartStopStep):
|
||||
|
|
|
@ -559,6 +559,43 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"model",
|
||||
"identifiers"
|
||||
],
|
||||
"properties": {
|
||||
"model": {
|
||||
"const": "authentik_policies_expression.expressionvariable"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"absent",
|
||||
"present",
|
||||
"created",
|
||||
"must_created"
|
||||
],
|
||||
"default": "present"
|
||||
},
|
||||
"conditions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"attrs": {
|
||||
"$ref": "#/$defs/model_authentik_policies_expression.expressionvariable"
|
||||
},
|
||||
"identifiers": {
|
||||
"$ref": "#/$defs/model_authentik_policies_expression.expressionvariable"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
@ -3426,6 +3463,7 @@
|
|||
"authentik_policies_dummy.dummypolicy",
|
||||
"authentik_policies_event_matcher.eventmatcherpolicy",
|
||||
"authentik_policies_expiry.passwordexpirypolicy",
|
||||
"authentik_policies_expression.expressionvariable",
|
||||
"authentik_policies_expression.expressionpolicy",
|
||||
"authentik_policies_password.passwordpolicy",
|
||||
"authentik_policies_reputation.reputationpolicy",
|
||||
|
@ -3517,6 +3555,22 @@
|
|||
},
|
||||
"required": []
|
||||
},
|
||||
"model_authentik_policies_expression.expressionvariable": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Name"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Value"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
},
|
||||
"model_authentik_policies_expression.expressionpolicy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -3534,6 +3588,13 @@
|
|||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Expression"
|
||||
},
|
||||
"variables": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
},
|
||||
"title": "Variables"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
|
|
@ -74,6 +74,7 @@ services:
|
|||
- ./media:/media
|
||||
- ./certs:/certs
|
||||
- ./custom-templates:/templates
|
||||
- ./data:/data
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/bin/bash -e
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
MODE_FILE="${TMPDIR}/authentik-mode"
|
||||
|
||||
function log {
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-15 09:51+0000\n"
|
||||
"POT-Creation-Date: 2023-09-29 00:26+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"
|
||||
|
@ -70,6 +70,7 @@ msgid "authentik Export - %(date)s"
|
|||
msgstr ""
|
||||
|
||||
#: authentik/blueprints/v1/tasks.py:150 authentik/crypto/tasks.py:93
|
||||
#: authentik/policies/expression/tasks.py:59
|
||||
#, python-format
|
||||
msgid "Successfully imported %(count)d files."
|
||||
msgstr ""
|
||||
|
@ -724,11 +725,19 @@ msgstr ""
|
|||
msgid "Password Expiry Policies"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/expression/models.py:40
|
||||
#: authentik/policies/expression/models.py:53
|
||||
msgid "Expression Variable"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/expression/models.py:54
|
||||
msgid "Expression Variables"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/expression/models.py:89
|
||||
msgid "Expression Policy"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/policies/expression/models.py:41
|
||||
#: authentik/policies/expression/models.py:90
|
||||
msgid "Expression Policies"
|
||||
msgstr ""
|
||||
|
||||
|
|
377
schema.yml
377
schema.yml
|
@ -11748,6 +11748,14 @@ paths:
|
|||
description: A search term.
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: variables
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: integer
|
||||
explode: true
|
||||
style: form
|
||||
tags:
|
||||
- policies
|
||||
security:
|
||||
|
@ -11984,6 +11992,288 @@ paths:
|
|||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/policies/expression/variables/:
|
||||
get:
|
||||
operationId: policies_expression_variables_list
|
||||
description: Expression Variable Viewset
|
||||
parameters:
|
||||
- in: query
|
||||
name: created
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- in: query
|
||||
name: last_updated
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
- in: query
|
||||
name: managed
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
- name: ordering
|
||||
required: false
|
||||
in: query
|
||||
description: Which field to use when ordering the results.
|
||||
schema:
|
||||
type: string
|
||||
- name: page
|
||||
required: false
|
||||
in: query
|
||||
description: A page number within the paginated result set.
|
||||
schema:
|
||||
type: integer
|
||||
- name: page_size
|
||||
required: false
|
||||
in: query
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
description: A search term.
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: value
|
||||
schema:
|
||||
type: string
|
||||
tags:
|
||||
- policies
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PaginatedExpressionVariableList'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
post:
|
||||
operationId: policies_expression_variables_create
|
||||
description: Expression Variable Viewset
|
||||
tags:
|
||||
- policies
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExpressionVariableRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'201':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExpressionVariable'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/policies/expression/variables/{id}/:
|
||||
get:
|
||||
operationId: policies_expression_variables_retrieve
|
||||
description: Expression Variable Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this Expression Variable.
|
||||
required: true
|
||||
tags:
|
||||
- policies
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExpressionVariable'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
put:
|
||||
operationId: policies_expression_variables_update
|
||||
description: Expression Variable Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this Expression Variable.
|
||||
required: true
|
||||
tags:
|
||||
- policies
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExpressionVariableRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExpressionVariable'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
patch:
|
||||
operationId: policies_expression_variables_partial_update
|
||||
description: Expression Variable Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this Expression Variable.
|
||||
required: true
|
||||
tags:
|
||||
- policies
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedExpressionVariableRequest'
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ExpressionVariable'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
delete:
|
||||
operationId: policies_expression_variables_destroy
|
||||
description: Expression Variable Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this Expression Variable.
|
||||
required: true
|
||||
tags:
|
||||
- policies
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/policies/expression/variables/{id}/used_by/:
|
||||
get:
|
||||
operationId: policies_expression_variables_used_by_list
|
||||
description: Get a list of all objects that use this object
|
||||
parameters:
|
||||
- in: path
|
||||
name: id
|
||||
schema:
|
||||
type: integer
|
||||
description: A unique integer value identifying this Expression Variable.
|
||||
required: true
|
||||
tags:
|
||||
- policies
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UsedBy'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/policies/password/:
|
||||
get:
|
||||
operationId: policies_password_list
|
||||
|
@ -29556,6 +29846,7 @@ components:
|
|||
* `authentik_policies_dummy.dummypolicy` - Dummy Policy
|
||||
* `authentik_policies_event_matcher.eventmatcherpolicy` - Event Matcher Policy
|
||||
* `authentik_policies_expiry.passwordexpirypolicy` - Password Expiry Policy
|
||||
* `authentik_policies_expression.expressionvariable` - Expression Variable
|
||||
* `authentik_policies_expression.expressionpolicy` - Expression Policy
|
||||
* `authentik_policies_password.passwordpolicy` - Password Policy
|
||||
* `authentik_policies_reputation.reputationpolicy` - Reputation Policy
|
||||
|
@ -29749,6 +30040,7 @@ components:
|
|||
* `authentik_policies_dummy.dummypolicy` - Dummy Policy
|
||||
* `authentik_policies_event_matcher.eventmatcherpolicy` - Event Matcher Policy
|
||||
* `authentik_policies_expiry.passwordexpirypolicy` - Password Expiry Policy
|
||||
* `authentik_policies_expression.expressionvariable` - Expression Variable
|
||||
* `authentik_policies_expression.expressionpolicy` - Expression Policy
|
||||
* `authentik_policies_password.passwordpolicy` - Password Policy
|
||||
* `authentik_policies_reputation.reputationpolicy` - Reputation Policy
|
||||
|
@ -29918,6 +30210,10 @@ components:
|
|||
readOnly: true
|
||||
expression:
|
||||
type: string
|
||||
variables:
|
||||
type: array
|
||||
items:
|
||||
type: integer
|
||||
required:
|
||||
- bound_to
|
||||
- component
|
||||
|
@ -29941,9 +30237,61 @@ components:
|
|||
expression:
|
||||
type: string
|
||||
minLength: 1
|
||||
variables:
|
||||
type: array
|
||||
items:
|
||||
type: integer
|
||||
required:
|
||||
- expression
|
||||
- name
|
||||
ExpressionVariable:
|
||||
type: object
|
||||
description: Expression Variable Serializer
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
readOnly: true
|
||||
created:
|
||||
type: string
|
||||
format: date-time
|
||||
readOnly: true
|
||||
last_updated:
|
||||
type: string
|
||||
format: date-time
|
||||
readOnly: true
|
||||
managed:
|
||||
type: string
|
||||
readOnly: true
|
||||
nullable: true
|
||||
title: Managed by authentik
|
||||
description: Objects that are managed by authentik. These objects are created
|
||||
and updated automatically. This flag only indicates that an object can
|
||||
be overwritten by migrations. You can still modify the objects via the
|
||||
API, but expect changes to be overwritten in a later update.
|
||||
name:
|
||||
type: string
|
||||
value:
|
||||
type: string
|
||||
required:
|
||||
- created
|
||||
- id
|
||||
- last_updated
|
||||
- managed
|
||||
- name
|
||||
- value
|
||||
ExpressionVariableRequest:
|
||||
type: object
|
||||
description: Expression Variable Serializer
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
value:
|
||||
type: string
|
||||
minLength: 1
|
||||
required:
|
||||
- name
|
||||
- value
|
||||
FilePathRequest:
|
||||
type: object
|
||||
description: Serializer to upload file
|
||||
|
@ -31909,6 +32257,7 @@ components:
|
|||
- authentik_policies_dummy.dummypolicy
|
||||
- authentik_policies_event_matcher.eventmatcherpolicy
|
||||
- authentik_policies_expiry.passwordexpirypolicy
|
||||
- authentik_policies_expression.expressionvariable
|
||||
- authentik_policies_expression.expressionpolicy
|
||||
- authentik_policies_password.passwordpolicy
|
||||
- authentik_policies_reputation.reputationpolicy
|
||||
|
@ -31983,6 +32332,7 @@ components:
|
|||
* `authentik_policies_dummy.dummypolicy` - Dummy Policy
|
||||
* `authentik_policies_event_matcher.eventmatcherpolicy` - Event Matcher Policy
|
||||
* `authentik_policies_expiry.passwordexpirypolicy` - Password Expiry Policy
|
||||
* `authentik_policies_expression.expressionvariable` - Expression Variable
|
||||
* `authentik_policies_expression.expressionpolicy` - Expression Policy
|
||||
* `authentik_policies_password.passwordpolicy` - Password Policy
|
||||
* `authentik_policies_reputation.reputationpolicy` - Reputation Policy
|
||||
|
@ -33288,6 +33638,18 @@ components:
|
|||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedExpressionVariableList:
|
||||
type: object
|
||||
properties:
|
||||
pagination:
|
||||
$ref: '#/components/schemas/Pagination'
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ExpressionVariable'
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedFlowList:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -34973,6 +35335,7 @@ components:
|
|||
* `authentik_policies_dummy.dummypolicy` - Dummy Policy
|
||||
* `authentik_policies_event_matcher.eventmatcherpolicy` - Event Matcher Policy
|
||||
* `authentik_policies_expiry.passwordexpirypolicy` - Password Expiry Policy
|
||||
* `authentik_policies_expression.expressionvariable` - Expression Variable
|
||||
* `authentik_policies_expression.expressionpolicy` - Expression Policy
|
||||
* `authentik_policies_password.passwordpolicy` - Password Policy
|
||||
* `authentik_policies_reputation.reputationpolicy` - Reputation Policy
|
||||
|
@ -35070,6 +35433,20 @@ components:
|
|||
expression:
|
||||
type: string
|
||||
minLength: 1
|
||||
variables:
|
||||
type: array
|
||||
items:
|
||||
type: integer
|
||||
PatchedExpressionVariableRequest:
|
||||
type: object
|
||||
description: Expression Variable Serializer
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
value:
|
||||
type: string
|
||||
minLength: 1
|
||||
PatchedFlowRequest:
|
||||
type: object
|
||||
description: Flow Serializer
|
||||
|
|
|
@ -17,6 +17,7 @@ with open("local.env.yml", "w", encoding="utf-8") as _config:
|
|||
},
|
||||
"blueprints_dir": "./blueprints",
|
||||
"cert_discovery_dir": "./certs",
|
||||
"variables_discovery_dir": "./variables",
|
||||
"geoip": "tests/GeoLite2-City-Test.mmdb",
|
||||
},
|
||||
_config,
|
||||
|
|
|
@ -201,6 +201,7 @@ export class AdminInterface extends Interface {
|
|||
["/events/transports", msg("Notification Transports")]]],
|
||||
[null, msg("Customisation"), null, [
|
||||
["/policy/policies", msg("Policies")],
|
||||
["/policy/expression/variables", msg("Variables")],
|
||||
["/core/property-mappings", msg("Property Mappings")],
|
||||
["/blueprints/instances", msg("Blueprints")],
|
||||
["/policy/reputation", msg("Reputation scores")]]],
|
||||
|
|
|
@ -60,6 +60,10 @@ export const ROUTES: Route[] = [
|
|||
await import("@goauthentik/admin/policies/PolicyListPage");
|
||||
return html`<ak-policy-list></ak-policy-list>`;
|
||||
}),
|
||||
new Route(new RegExp("^/policy/expression/variables"), async () => {
|
||||
await import("@goauthentik/admin/policies/expression/ExpressionVariableListPage");
|
||||
return html`<ak-expression-variable-list></ak-expression-variable-list>`;
|
||||
}),
|
||||
new Route(new RegExp("^/policy/reputation$"), async () => {
|
||||
await import("@goauthentik/admin/policies/reputation/ReputationListPage");
|
||||
return html`<ak-policy-reputation-list></ak-policy-reputation-list>`;
|
||||
|
|
|
@ -11,7 +11,7 @@ import { TemplateResult, html } from "lit";
|
|||
import { customElement } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import { ExpressionPolicy, PoliciesApi } from "@goauthentik/api";
|
||||
import { ExpressionPolicy, PaginatedExpressionVariableList, PoliciesApi } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-policy-expression-form")
|
||||
export class ExpressionPolicyForm extends ModelForm<ExpressionPolicy, string> {
|
||||
|
@ -21,6 +21,14 @@ export class ExpressionPolicyForm extends ModelForm<ExpressionPolicy, string> {
|
|||
});
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
this.variables = await new PoliciesApi(DEFAULT_CONFIG).policiesExpressionVariablesList({
|
||||
ordering: "name",
|
||||
});
|
||||
}
|
||||
|
||||
variables?: PaginatedExpressionVariableList;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
if (this.instance) {
|
||||
return msg("Successfully updated policy.");
|
||||
|
@ -100,6 +108,35 @@ export class ExpressionPolicyForm extends ModelForm<ExpressionPolicy, string> {
|
|||
</a>
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Variables")}
|
||||
?required=${true}
|
||||
name="variables"
|
||||
>
|
||||
<select class="pf-c-form-control" multiple>
|
||||
${this.variables?.results.map((variable) => {
|
||||
const selected = Array.from(this.instance?.variables || []).some(
|
||||
(va) => {
|
||||
return va == variable.id;
|
||||
},
|
||||
);
|
||||
return html`<option
|
||||
value=${ifDefined(variable.id)}
|
||||
?selected=${selected}
|
||||
>
|
||||
${variable.name}
|
||||
</option>`;
|
||||
})}
|
||||
</select>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
"Select variables that will be made available to this expression.",
|
||||
)}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Hold control/command to select multiple items.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import { ExpressionVariable, PoliciesApi } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-expression-variable-form")
|
||||
export class ExpressionVariableForm extends ModelForm<ExpressionVariable, number> {
|
||||
loadInstance(pk: number): Promise<ExpressionVariable> {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionVariablesRetrieve({
|
||||
id: pk,
|
||||
});
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
if (this.instance) {
|
||||
return msg("Successfully updated variable.");
|
||||
} else {
|
||||
return msg("Successfully created variable.");
|
||||
}
|
||||
}
|
||||
|
||||
async send(data: ExpressionVariable): Promise<ExpressionVariable> {
|
||||
if (this.instance) {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionVariablesUpdate({
|
||||
id: this.instance.id || 0,
|
||||
expressionVariableRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionVariablesCreate({
|
||||
expressionVariableRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<span> ${msg("Variable that can be passed to an expression policy")} </span>
|
||||
<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.name || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal label=${msg("Value")} ?required=${true} name="value">
|
||||
<input
|
||||
type="text"
|
||||
value="${ifDefined(this.instance?.value || "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
</form>`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
import "@goauthentik/admin/policies/expression/ExpressionVariableForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import "@goauthentik/elements/forms/ConfirmationForm";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
import "@goauthentik/elements/forms/ModalForm";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
import { TableColumn } from "@goauthentik/elements/table/Table";
|
||||
import { TablePage } from "@goauthentik/elements/table/TablePage";
|
||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import { ExpressionVariable, PoliciesApi } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-expression-variable-list")
|
||||
export class ExpressionVariableListPage extends TablePage<ExpressionVariable> {
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
return msg("Variables");
|
||||
}
|
||||
pageDescription(): string {
|
||||
return msg("Variables that can be passed on to expressions.");
|
||||
}
|
||||
pageIcon(): string {
|
||||
// TODO: ask Jens what to put here
|
||||
return "pf-icon pf-icon-infrastructure";
|
||||
}
|
||||
|
||||
checkbox = true;
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
|
||||
async apiEndpoint(page: number): Promise<PaginatedResponse<ExpressionVariable>> {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionVariablesList({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
pageSize: (await uiConfig()).pagination.perPage,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [new TableColumn(msg("Name"), "name"), new TableColumn(msg("Actions"))];
|
||||
}
|
||||
|
||||
row(item: ExpressionVariable): TemplateResult[] {
|
||||
let managedSubText = msg("Managed by authentik");
|
||||
if (item.managed && item.managed.startsWith("goauthentik.io/variables/discovered")) {
|
||||
managedSubText = msg("Managed by authentik (Discovered)");
|
||||
}
|
||||
return [
|
||||
html`<div>${item.name}</div>
|
||||
${item.managed ? html`<small>${managedSubText}</small>` : html``}`,
|
||||
html` <ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Update")} </span>
|
||||
<span slot="header"> ${msg("Update Variable")} </span>
|
||||
<ak-expression-variable-form slot="form" .instancePk=${item.id}>
|
||||
</ak-expression-variable-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-plain">
|
||||
<pf-tooltip position="top" content=${msg("Edit")}>
|
||||
<i class="fas fa-edit"></i>
|
||||
</pf-tooltip>
|
||||
</button>
|
||||
</ak-forms-modal>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Variable / Variables")}
|
||||
.objects=${this.selectedElements}
|
||||
.usedBy=${(item: ExpressionVariable) => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionVariablesUsedByList({
|
||||
id: item.id,
|
||||
});
|
||||
}}
|
||||
.delete=${(item: ExpressionVariable) => {
|
||||
return new PoliciesApi(DEFAULT_CONFIG).policiesExpressionVariablesDestroy({
|
||||
id: item.id,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
|
||||
${msg("Delete")}
|
||||
</button>
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
<span slot="header"> ${msg("Create Variable")} </span>
|
||||
<ak-expression-variable-form slot="form"> </ak-expression-variable-form>
|
||||
<button slot="trigger" class="pf-c-button pf-m-primary">${msg("Create")}</button>
|
||||
</ak-forms-modal>
|
||||
`;
|
||||
}
|
||||
}
|
|
@ -5925,6 +5925,36 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="se9e9e1d6799b86a5">
|
||||
<source>WebAuthn not supported by browser.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb39940945f8f7122">
|
||||
<source>Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se1d0e029ea650f11">
|
||||
<source>Select variables that will be made available to this expression.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s97e9ea093d519435">
|
||||
<source>Successfully updated variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9d8d42526c7f0f0e">
|
||||
<source>Successfully created variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd483758478e36417">
|
||||
<source>Variable that can be passed to an expression policy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s41f7dca46d3f1b4a">
|
||||
<source>Value</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se5363f539effce3b">
|
||||
<source>Variables that can be passed on to expressions.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4bc8b951c8067ed4">
|
||||
<source>Update Variable</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5ab1f5073aa96219">
|
||||
<source>Variable / Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s52a2427b2afc939b">
|
||||
<source>Create Variable</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -6239,6 +6239,36 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="se9e9e1d6799b86a5">
|
||||
<source>WebAuthn not supported by browser.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb39940945f8f7122">
|
||||
<source>Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se1d0e029ea650f11">
|
||||
<source>Select variables that will be made available to this expression.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s97e9ea093d519435">
|
||||
<source>Successfully updated variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9d8d42526c7f0f0e">
|
||||
<source>Successfully created variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd483758478e36417">
|
||||
<source>Variable that can be passed to an expression policy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s41f7dca46d3f1b4a">
|
||||
<source>Value</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se5363f539effce3b">
|
||||
<source>Variables that can be passed on to expressions.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4bc8b951c8067ed4">
|
||||
<source>Update Variable</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5ab1f5073aa96219">
|
||||
<source>Variable / Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s52a2427b2afc939b">
|
||||
<source>Create Variable</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -5833,6 +5833,36 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="se9e9e1d6799b86a5">
|
||||
<source>WebAuthn not supported by browser.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb39940945f8f7122">
|
||||
<source>Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se1d0e029ea650f11">
|
||||
<source>Select variables that will be made available to this expression.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s97e9ea093d519435">
|
||||
<source>Successfully updated variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9d8d42526c7f0f0e">
|
||||
<source>Successfully created variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd483758478e36417">
|
||||
<source>Variable that can be passed to an expression policy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s41f7dca46d3f1b4a">
|
||||
<source>Value</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se5363f539effce3b">
|
||||
<source>Variables that can be passed on to expressions.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4bc8b951c8067ed4">
|
||||
<source>Update Variable</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5ab1f5073aa96219">
|
||||
<source>Variable / Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s52a2427b2afc939b">
|
||||
<source>Create Variable</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -5941,6 +5941,36 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="se9e9e1d6799b86a5">
|
||||
<source>WebAuthn not supported by browser.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb39940945f8f7122">
|
||||
<source>Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se1d0e029ea650f11">
|
||||
<source>Select variables that will be made available to this expression.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s97e9ea093d519435">
|
||||
<source>Successfully updated variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9d8d42526c7f0f0e">
|
||||
<source>Successfully created variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd483758478e36417">
|
||||
<source>Variable that can be passed to an expression policy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s41f7dca46d3f1b4a">
|
||||
<source>Value</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se5363f539effce3b">
|
||||
<source>Variables that can be passed on to expressions.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4bc8b951c8067ed4">
|
||||
<source>Update Variable</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5ab1f5073aa96219">
|
||||
<source>Variable / Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s52a2427b2afc939b">
|
||||
<source>Create Variable</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -6072,6 +6072,36 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="se9e9e1d6799b86a5">
|
||||
<source>WebAuthn not supported by browser.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb39940945f8f7122">
|
||||
<source>Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se1d0e029ea650f11">
|
||||
<source>Select variables that will be made available to this expression.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s97e9ea093d519435">
|
||||
<source>Successfully updated variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9d8d42526c7f0f0e">
|
||||
<source>Successfully created variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd483758478e36417">
|
||||
<source>Variable that can be passed to an expression policy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s41f7dca46d3f1b4a">
|
||||
<source>Value</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se5363f539effce3b">
|
||||
<source>Variables that can be passed on to expressions.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4bc8b951c8067ed4">
|
||||
<source>Update Variable</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5ab1f5073aa96219">
|
||||
<source>Variable / Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s52a2427b2afc939b">
|
||||
<source>Create Variable</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -6174,6 +6174,36 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="se9e9e1d6799b86a5">
|
||||
<source>WebAuthn not supported by browser.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb39940945f8f7122">
|
||||
<source>Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se1d0e029ea650f11">
|
||||
<source>Select variables that will be made available to this expression.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s97e9ea093d519435">
|
||||
<source>Successfully updated variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9d8d42526c7f0f0e">
|
||||
<source>Successfully created variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd483758478e36417">
|
||||
<source>Variable that can be passed to an expression policy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s41f7dca46d3f1b4a">
|
||||
<source>Value</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se5363f539effce3b">
|
||||
<source>Variables that can be passed on to expressions.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4bc8b951c8067ed4">
|
||||
<source>Update Variable</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5ab1f5073aa96219">
|
||||
<source>Variable / Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s52a2427b2afc939b">
|
||||
<source>Create Variable</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -5826,6 +5826,36 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="se9e9e1d6799b86a5">
|
||||
<source>WebAuthn not supported by browser.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb39940945f8f7122">
|
||||
<source>Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se1d0e029ea650f11">
|
||||
<source>Select variables that will be made available to this expression.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s97e9ea093d519435">
|
||||
<source>Successfully updated variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9d8d42526c7f0f0e">
|
||||
<source>Successfully created variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd483758478e36417">
|
||||
<source>Variable that can be passed to an expression policy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s41f7dca46d3f1b4a">
|
||||
<source>Value</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se5363f539effce3b">
|
||||
<source>Variables that can be passed on to expressions.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4bc8b951c8067ed4">
|
||||
<source>Update Variable</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5ab1f5073aa96219">
|
||||
<source>Variable / Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s52a2427b2afc939b">
|
||||
<source>Create Variable</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -613,9 +613,9 @@
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="saa0e2675da69651b">
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>未找到 URL "
|
||||
<x id="0" equiv-text="${this.url}"/>"。</target>
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>未找到 URL "
|
||||
<x id="0" equiv-text="${this.url}"/>"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s58cd9c2fe836d9c6">
|
||||
|
@ -1067,8 +1067,8 @@
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa8384c9c26731f83">
|
||||
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
|
||||
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s55787f4dfcdce52b">
|
||||
|
@ -1814,8 +1814,8 @@
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa90b7809586c35ce">
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s0410779cb47de312">
|
||||
|
@ -3238,8 +3238,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="s76768bebabb7d543">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
||||
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
||||
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s026555347e589f0e">
|
||||
|
@ -4031,8 +4031,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="s7b1fba26d245cb1c">
|
||||
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
|
||||
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
|
||||
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
|
||||
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s44536d20bb5c8257">
|
||||
|
@ -4041,8 +4041,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="s3bb51cabb02b997e">
|
||||
<source>Format: "weeks=3;days=2;hours=3,seconds=2".</source>
|
||||
<target>格式:"weeks=3;days=2;hours=3,seconds=2"。</target>
|
||||
<source>Format: "weeks=3;days=2;hours=3,seconds=2".</source>
|
||||
<target>格式:"weeks=3;days=2;hours=3,seconds=2"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s04bfd02201db5ab8">
|
||||
|
@ -4238,10 +4238,10 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa95a538bfbb86111">
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<target>您确定要更新
|
||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
|
||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sc92d7cfb6ee1fec6">
|
||||
|
@ -5342,7 +5342,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="sdf1d8edef27236f0">
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<target>像 YubiKey 这样的“漫游”身份验证器</target>
|
||||
|
||||
</trans-unit>
|
||||
|
@ -5677,10 +5677,10 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="s2d5f69929bb7221d">
|
||||
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
|
||||
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.name}"/>("
|
||||
<x id="1" equiv-text="${prompt.fieldKey}"/>",类型为
|
||||
<x id="0" equiv-text="${prompt.name}"/>("
|
||||
<x id="1" equiv-text="${prompt.fieldKey}"/>",类型为
|
||||
<x id="2" equiv-text="${prompt.type}"/>)</target>
|
||||
|
||||
</trans-unit>
|
||||
|
@ -5729,7 +5729,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1608b2f94fa0dbd4">
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<target>如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。</target>
|
||||
|
||||
</trans-unit>
|
||||
|
@ -7818,6 +7818,36 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
<trans-unit id="se9e9e1d6799b86a5">
|
||||
<source>WebAuthn not supported by browser.</source>
|
||||
<target>浏览器不支持 WebAuthn。</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb39940945f8f7122">
|
||||
<source>Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se1d0e029ea650f11">
|
||||
<source>Select variables that will be made available to this expression.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s97e9ea093d519435">
|
||||
<source>Successfully updated variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9d8d42526c7f0f0e">
|
||||
<source>Successfully created variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd483758478e36417">
|
||||
<source>Variable that can be passed to an expression policy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s41f7dca46d3f1b4a">
|
||||
<source>Value</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se5363f539effce3b">
|
||||
<source>Variables that can be passed on to expressions.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4bc8b951c8067ed4">
|
||||
<source>Update Variable</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5ab1f5073aa96219">
|
||||
<source>Variable / Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s52a2427b2afc939b">
|
||||
<source>Create Variable</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -5878,6 +5878,36 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="se9e9e1d6799b86a5">
|
||||
<source>WebAuthn not supported by browser.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb39940945f8f7122">
|
||||
<source>Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se1d0e029ea650f11">
|
||||
<source>Select variables that will be made available to this expression.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s97e9ea093d519435">
|
||||
<source>Successfully updated variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9d8d42526c7f0f0e">
|
||||
<source>Successfully created variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd483758478e36417">
|
||||
<source>Variable that can be passed to an expression policy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s41f7dca46d3f1b4a">
|
||||
<source>Value</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se5363f539effce3b">
|
||||
<source>Variables that can be passed on to expressions.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4bc8b951c8067ed4">
|
||||
<source>Update Variable</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5ab1f5073aa96219">
|
||||
<source>Variable / Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s52a2427b2afc939b">
|
||||
<source>Create Variable</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -5877,6 +5877,36 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
<trans-unit id="se9e9e1d6799b86a5">
|
||||
<source>WebAuthn not supported by browser.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sb39940945f8f7122">
|
||||
<source>Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se1d0e029ea650f11">
|
||||
<source>Select variables that will be made available to this expression.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s97e9ea093d519435">
|
||||
<source>Successfully updated variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s9d8d42526c7f0f0e">
|
||||
<source>Successfully created variable.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="sd483758478e36417">
|
||||
<source>Variable that can be passed to an expression policy</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s41f7dca46d3f1b4a">
|
||||
<source>Value</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="se5363f539effce3b">
|
||||
<source>Variables that can be passed on to expressions.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s4bc8b951c8067ed4">
|
||||
<source>Update Variable</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s5ab1f5073aa96219">
|
||||
<source>Variable / Variables</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="s52a2427b2afc939b">
|
||||
<source>Create Variable</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -16,6 +16,39 @@ return False
|
|||
|
||||
to fail it.
|
||||
|
||||
## Variables
|
||||
|
||||
You can create variables that will be made available to the policy under an `ak_variables` dictionary.
|
||||
|
||||
Creating a variable named `my_var` with a value `this is the value` and associating this variable to the expression will result in the following `ak_variables` dictionary being available to use in the expression:
|
||||
|
||||
```python
|
||||
ak_variables["my_var"] # This is equal to `this_is_the_value`
|
||||
```
|
||||
|
||||
### External variables
|
||||
|
||||
To use externally managed variables (for instance, to pass a secret as a file to authentik), you can use the discovery feature.
|
||||
|
||||
The docker-compose installation maps `data/variables` directory to `/data/variables`, you can simply use this directory to store your variables.
|
||||
|
||||
For Kubernetes, you can map custom configmaps/secrets/volumes under /data/variables.
|
||||
|
||||
You can also bind mount single files into the folder.
|
||||
|
||||
The name of the variable will be the full path from the `/data/variables/` directory. For instance:
|
||||
|
||||
```
|
||||
data/variables/
|
||||
├── baz
|
||||
│ └── bar.baz # The variable will be named `baz/bar.baz`
|
||||
└── foo.bar # The variable will be named `foo.bar`
|
||||
```
|
||||
|
||||
Note that file contents are not stripped, and may contain an extra `\n` at the end.
|
||||
|
||||
External variables are reloaded from disk on every policy execution. If the reload fails, the previous value is used.
|
||||
|
||||
## Available Functions
|
||||
|
||||
### `ak_message(message: str)`
|
||||
|
@ -65,6 +98,8 @@ import Objects from "../expressions/_objects.md";
|
|||
|
||||
See also [Python documentation](https://docs.python.org/3/library/ipaddress.html#ipaddress.ip_address)
|
||||
|
||||
- `ak_variables`: dictionary of name, value obtained from the variables bound to the expression policy.
|
||||
|
||||
Additionally, when the policy is executed from a flow, every variable from the flow's current context is accessible under the `context` object.
|
||||
|
||||
This includes the following:
|
||||
|
|
Reference in New Issue