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",