From a4c28a28b4592704a1ea34d7502d704a4f867b62 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 29 May 2021 21:47:35 +0200 Subject: [PATCH] website/docs: improve docs for expressions Signed-off-by: Jens Langhammer --- authentik/lib/expression/evaluator.py | 4 +- authentik/policies/expression/evaluator.py | 6 +- .../expressions/{index.md => _functions.md} | 23 +------ website/docs/expressions/_objects.md | 11 +++ .../docs/expressions/reference/user-object.md | 11 ++- website/docs/flow/flows.md | 6 ++ .../{expression.md => expression.mdx} | 40 +++++++---- .../{expression.md => expression.mdx} | 21 ++++-- website/docs/releases/next.md | 12 ++++ website/sidebars.js | 69 +++++++++---------- 10 files changed, 122 insertions(+), 81 deletions(-) rename website/docs/expressions/{index.md => _functions.md} (61%) create mode 100644 website/docs/expressions/_objects.md rename website/docs/policies/{expression.md => expression.mdx} (50%) rename website/docs/property-mappings/{expression.md => expression.mdx} (55%) diff --git a/authentik/lib/expression/evaluator.py b/authentik/lib/expression/evaluator.py index 3c6c7087c..7681ef6c0 100644 --- a/authentik/lib/expression/evaluator.py +++ b/authentik/lib/expression/evaluator.py @@ -26,8 +26,8 @@ class BaseEvaluator: _filename: str def __init__(self): - # update authentik/policies/expression/templates/policy/expression/form.html - # update website/docs/policies/expression.md + # update website/docs/expressions/_objects.md + # update website/docs/expressions/_functions.md self._globals = { "regex_match": BaseEvaluator.expr_filter_regex_match, "regex_replace": BaseEvaluator.expr_filter_regex_replace, diff --git a/authentik/policies/expression/evaluator.py b/authentik/policies/expression/evaluator.py index 89d22a467..8f28ee9f3 100644 --- a/authentik/policies/expression/evaluator.py +++ b/authentik/policies/expression/evaluator.py @@ -38,7 +38,8 @@ class PolicyEvaluator(BaseEvaluator): def set_policy_request(self, request: PolicyRequest): """Update context based on policy request (if http request is given, update that too)""" - # update website/docs/policies/expression.md + # update website/docs/expressions/_objects.md + # update website/docs/expressions/_functions.md self._context["ak_is_sso_flow"] = request.context.get(PLAN_CONTEXT_SSO, False) if request.http_request: self.set_http_request(request.http_request) @@ -47,7 +48,8 @@ class PolicyEvaluator(BaseEvaluator): def set_http_request(self, request: HttpRequest): """Update context based on http request""" - # update website/docs/policies/expression.md + # update website/docs/expressions/_objects.md + # update website/docs/expressions/_functions.md self._context["ak_client_ip"] = ip_address( get_client_ip(request) or "255.255.255.255" ) diff --git a/website/docs/expressions/index.md b/website/docs/expressions/_functions.md similarity index 61% rename from website/docs/expressions/index.md rename to website/docs/expressions/_functions.md index ca305aab9..7a05dd828 100644 --- a/website/docs/expressions/index.md +++ b/website/docs/expressions/_functions.md @@ -1,22 +1,3 @@ ---- -title: Expressions ---- - -Expressions allow you to write custom logic using Python code. - -Expressions are used in different places throughout authentik, and can do different things. - -:::info -These functions/objects are available wherever expressions are used. For more specific information, see [Expression Policies](../policies/expression.md) and [Property Mappings](../property-mappings/expression.md) -::: - -## Global objects - -- `ak_logger`: structlog BoundLogger. ([ref](https://www.structlog.org/en/stable/api.html#structlog.BoundLogger)) -- `requests`: requests Session object. ([ref](https://requests.readthedocs.io/en/master/user/advanced/)) - -## Generally available functions - ### `regex_match(value: Any, regex: str) -> bool` Check if `value` matches Regular Expression `regex`. @@ -49,7 +30,9 @@ return ak_is_group_member(request.user, name="test_group") ### `ak_user_by(**filters) -> Optional[User]` -Fetch a user matching `**filters`. Returns "None" if no user was found. +Fetch a user matching `**filters`. + +Returns "None" if no user was found, otherwise [User](/docs/expressions/reference/user-object) Example: diff --git a/website/docs/expressions/_objects.md b/website/docs/expressions/_objects.md new file mode 100644 index 000000000..9690eb83e --- /dev/null +++ b/website/docs/expressions/_objects.md @@ -0,0 +1,11 @@ +- `ak_logger`: structlog BoundLogger. See ([structlog documentation](https://www.structlog.org/en/stable/api.html#structlog.BoundLogger)) + + Example: + + ```python + ak_logger.debug("This is a test message") + ak_logger.warning("This will be logged with a warning level") + ak_logger.info("Passing structured data", request=request) + ``` + +- `requests`: requests Session object. See ([request documentation](https://requests.readthedocs.io/en/master/user/advanced/)) diff --git a/website/docs/expressions/reference/user-object.md b/website/docs/expressions/reference/user-object.md index 7f2ab93a0..f56560b66 100644 --- a/website/docs/expressions/reference/user-object.md +++ b/website/docs/expressions/reference/user-object.md @@ -15,9 +15,16 @@ The User object has the following attributes: - `group_attributes` Merged attributes of all groups the user is member of and the user's own attributes. - `ak_groups` This is a queryset of all the user's groups. - You can do additional filtering like `user.ak_groups.filter(name__startswith='test')`, see [here](https://docs.djangoproject.com/en/3.1/ref/models/querysets/#id4) + You can do additional filtering like + ```python + user.ak_groups.filter(name__startswith='test') + ``` + see [here](https://docs.djangoproject.com/en/3.1/ref/models/querysets/#id4) - To get the name of all groups, you can do `[group.name for group in user.ak_groups.all()]` + To get the name of all groups, you can do + ```python + [group.name for group in user.ak_groups.all()] + ``` ## Examples diff --git a/website/docs/flow/flows.md b/website/docs/flow/flows.md index b57eb3a50..8cc5ca84f 100644 --- a/website/docs/flow/flows.md +++ b/website/docs/flow/flows.md @@ -4,6 +4,12 @@ title: Flows Flows are a method of describing a sequence of stages. A stage represents a single verification or logic step. They are used to authenticate users, enroll them, and more. +For example, a standard login flow would consist of the following stages: + +- Identification, user identifies themselves via a username or email address +- Password, the user's password is checked against the hash in the database +- Log the user in + Upon flow execution, a plan containing all stages is generated. This means that all attached policies are evaluated upon execution. This behaviour can be altered by enabling the **Re-evaluate Policies** option on the binding. To determine which flow is linked, authentik searches all flows with the required designation and chooses the first instance the current user has access to. diff --git a/website/docs/policies/expression.md b/website/docs/policies/expression.mdx similarity index 50% rename from website/docs/policies/expression.md rename to website/docs/policies/expression.mdx index 6acf2a44e..53d901351 100644 --- a/website/docs/policies/expression.md +++ b/website/docs/policies/expression.mdx @@ -2,15 +2,19 @@ title: Expression Policies --- -:::note -These variables are available in addition to the common variables/functions defined in [**Expressions**](../expressions/index.md) -::: +The passing of the policy is determined by the return value of the code. Use +```python +return True +``` +to pass a policy and +```python +return False +``` +to fail it. -The passing of the policy is determined by the return value of the code. Use `return True` to pass a policy and `return False` to fail it. +## Available Functions -### Available Functions - -#### `ak_message(message: str)` +### `ak_message(message: str)` Add a message, visible by the end user. This can be used to show the reason why they were denied. @@ -21,16 +25,24 @@ ak_message("Access denied") return False ``` -### Context variables +import Functions from '../expressions/_functions.md' + + + +## Variables + +import Objects from '../expressions/_objects.md' + + - `request`: A PolicyRequest object, which has the following properties: - - `request.user`: ([ref](../expressions/reference/user-object.md)) The current user, against which the policy is applied. - - `request.http_request`: ([ref](https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects)) The Django HTTP Request. + - `request.user`: The current user, against which the policy is applied. See [User](../expressions/reference/user-object.md) + - `request.http_request`: The Django HTTP Request. See ([Django documentation](https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects)) - `request.obj`: A Django Model instance. This is only set if the policy is ran against an object. - `request.context`: A dictionary with dynamic data. This depends on the origin of the execution. -- `geoip`: ([ref](https://geoip2.readthedocs.io/en/latest/#geoip2.models.City)) GeoIP object, which is added when GeoIP is enabled. +- `geoip`: GeoIP object, which is added when GeoIP is enabled. See [GeoIP](https://geoip2.readthedocs.io/en/latest/#geoip2.models.City) - `ak_is_sso_flow`: Boolean which is true if request was initiated by authenticating through an external provider. -- `ak_client_ip`: ([ref](https://docs.python.org/3/library/ipaddress.html#ipaddress.ip_address)) Client's IP Address or 255.255.255.255 if no IP Address could be extracted. Can be [compared](../expressions/index.md#comparing-ip-addresses), for example +- `ak_client_ip`: Client's IP Address or 255.255.255.255 if no IP Address could be extracted. Can be [compared](../expressions/index.md#comparing-ip-addresses), for example ```python return ak_client_ip in ip_network('10.0.0.0/24') @@ -38,10 +50,12 @@ return False return ak_client_ip.is_private ``` + See also [Python documetnation](https://docs.python.org/3/library/ipaddress.html#ipaddress.ip_address) + 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: - `prompt_data`: Data which has been saved from a prompt stage or an external source. - `application`: The application the user is in the process of authorizing. -- `pending_user`: The currently pending user +- `pending_user`: The currently pending user, see [User](/docs/expressions/reference/user-object) diff --git a/website/docs/property-mappings/expression.md b/website/docs/property-mappings/expression.mdx similarity index 55% rename from website/docs/property-mappings/expression.md rename to website/docs/property-mappings/expression.mdx index 37c8cdfdc..2621d1549 100644 --- a/website/docs/property-mappings/expression.md +++ b/website/docs/property-mappings/expression.mdx @@ -1,15 +1,22 @@ --- -title: Property Mapping Expressions +title: Expressions --- The property mapping should return a value that is expected by the Provider/Source. Supported types are documented in the individual Provider/Source. Returning `None` is always accepted and would simply skip the mapping for which `None` was returned. -:::note -These variables are available in addition to the common variables/functions defined in [**Expressions**](../expressions/index.md) -::: -### Context Variables +## Available Functions -- `user`: The current user. This may be `None` if there is no contextual user. ([ref](../expressions/reference/user-object.md)) -- `request`: The current request. This may be `None` if there is no contextual request. ([ref](https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects)) +import Functions from '../expressions/_functions.md' + + + +## Variables + +import Objects from '../expressions/_objects.md' + + + +- `user`: The current user. This may be `None` if there is no contextual user. See ([User](../expressions/reference/user-object.md)) +- `request`: The current request. This may be `None` if there is no contextual request. See ([Django documentation](https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects)) - Other arbitrary arguments given by the provider, this is documented on the Provider/Source. diff --git a/website/docs/releases/next.md b/website/docs/releases/next.md index 126adba27..c8d49c274 100644 --- a/website/docs/releases/next.md +++ b/website/docs/releases/next.md @@ -10,6 +10,18 @@ title: Next Currently, only Duo push notifications are supported. Because no additional input is required, Duo also works with the LDAP Outpost. +- Multi-tenancy + + This version adds soft multi-tenancy. This means you can configure different branding settings and different default flows per domain. + + This also changes how a default flow is determined. Previously, for defaults flow, authentik would pick the first flow that + + - matches the required designation + - comes first sorted by slug + - is allowed by policies + + Now, authentik first checks if the current tenant has a default flow configured for the selected designation. If not, it behaves the same as before, meaning that if you want to select a default flow based on policy, you can just leave the tenant default empty. + ## Minor changes - You can now specify which sources should be shown on an Identification stage. diff --git a/website/sidebars.js b/website/sidebars.js index 1ea9bc634..de458d234 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -41,6 +41,40 @@ module.exports = { "outposts/manual-deploy-kubernetes", ], }, + { + type: "category", + label: "Integrations", + items: [ + { + type: "category", + label: "as Source", + items: ["integrations/sources/active-directory/index"], + }, + { + type: "category", + label: "as Provider", + items: [ + "integrations/services/apache-guacamole/index", + "integrations/services/aws/index", + "integrations/services/awx-tower/index", + "integrations/services/gitlab/index", + "integrations/services/grafana/index", + "integrations/services/harbor/index", + "integrations/services/home-assistant/index", + "integrations/services/minio/index", + "integrations/services/nextcloud/index", + "integrations/services/rancher/index", + "integrations/services/sentry/index", + "integrations/services/sonarr/index", + "integrations/services/tautulli/index", + "integrations/services/ubuntu-landscape/index", + "integrations/services/veeam-enterprise-manager/index", + "integrations/services/vmware-vcenter/index", + "integrations/services/wiki-js/index", + ], + }, + ], + }, { type: "category", label: "Flows", @@ -83,7 +117,6 @@ module.exports = { type: "category", label: "Expressions", items: [ - "expressions/index", { type: "category", label: "Reference", @@ -100,40 +133,6 @@ module.exports = { "events/transports" ], }, - { - type: "category", - label: "Integrations", - items: [ - { - type: "category", - label: "as Source", - items: ["integrations/sources/active-directory/index"], - }, - { - type: "category", - label: "as Provider", - items: [ - "integrations/services/apache-guacamole/index", - "integrations/services/aws/index", - "integrations/services/awx-tower/index", - "integrations/services/gitlab/index", - "integrations/services/grafana/index", - "integrations/services/harbor/index", - "integrations/services/home-assistant/index", - "integrations/services/minio/index", - "integrations/services/nextcloud/index", - "integrations/services/rancher/index", - "integrations/services/sentry/index", - "integrations/services/sonarr/index", - "integrations/services/tautulli/index", - "integrations/services/ubuntu-landscape/index", - "integrations/services/veeam-enterprise-manager/index", - "integrations/services/vmware-vcenter/index", - "integrations/services/wiki-js/index", - ], - }, - ], - }, { type: "category", label: "Maintenance",