Merge branch 'docs-flows'
# Conflicts: # passbook/core/templates/partials/form_horizontal.html
|
@ -1,4 +1,4 @@
|
||||||
# passbook
|
<img src="passbook/static/static/passbook/logo.svg" height="50" alt="passbook logo"><img src="passbook/static/static/passbook/brand_inverted.svg" height="50" alt="passbook">
|
||||||
|
|
||||||
![](https://img.shields.io/github/workflow/status/beryju/passbook/passbook-ci?style=flat-square)
|
![](https://img.shields.io/github/workflow/status/beryju/passbook/passbook-ci?style=flat-square)
|
||||||
![](https://img.shields.io/docker/pulls/beryju/passbook.svg?style=flat-square)
|
![](https://img.shields.io/docker/pulls/beryju/passbook.svg?style=flat-square)
|
||||||
|
@ -28,12 +28,12 @@ docker-compose up -d
|
||||||
docker-compose exec server ./manage.py migrate
|
docker-compose exec server ./manage.py migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
For bigger setups, there is a Helm Chart in the `helm/` directory. This is documented [here](https://beryju.github.io/passbook/installation/kubernetes/)
|
For bigger setups, there is a Helm Chart in the `helm/` directory. This is documented [here](https://passbook.beryju.org//installation/kubernetes/)
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
![](.github/screen_apps.png)
|
![](docs/images/screen_apps.png)
|
||||||
![](.github/screen_admin.png)
|
![](docs/images/screen_admin.png)
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
FROM python:3.8-slim-buster as builder
|
|
||||||
|
|
||||||
WORKDIR /mkdocs
|
|
||||||
|
|
||||||
RUN pip install mkdocs mkdocs-material
|
|
||||||
|
|
||||||
COPY docs/ docs
|
|
||||||
COPY mkdocs.yml .
|
|
||||||
|
|
||||||
RUN mkdocs build
|
|
||||||
|
|
||||||
FROM nginx
|
|
||||||
|
|
||||||
COPY --from=builder /mkdocs/site /usr/share/nginx/html
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash -x
|
||||||
|
pip install -U mkdocs mkdocs-material
|
||||||
|
mkdocs gh-deploy
|
|
@ -1,23 +0,0 @@
|
||||||
# Factors
|
|
||||||
|
|
||||||
A factor represents a single authenticating factor for a user. Common examples of this would be a password or an OTP. These factors can be combined in any order, and can be dynamically enabled using policies.
|
|
||||||
|
|
||||||
## Password Factor
|
|
||||||
|
|
||||||
This is the standard Password Factor. It allows you to select which Backend the password is checked with. here you can also specify which Policies are used to check the password. You can also specify which Factors a User has to pass to recover their account.
|
|
||||||
|
|
||||||
## Dummy Factor
|
|
||||||
|
|
||||||
This factor waits a random amount of time. Mostly used for debugging.
|
|
||||||
|
|
||||||
## E-Mail Factor
|
|
||||||
|
|
||||||
This factor is mostly for recovery, and used in conjunction with the Password Factor.
|
|
||||||
|
|
||||||
## OTP Factor
|
|
||||||
|
|
||||||
This is your typical One-Time Password implementation, compatible with Authy and Google Authenticator. You can enfore this Factor so that every user has to configure it, or leave it optional.
|
|
||||||
|
|
||||||
## Captcha Factor
|
|
||||||
|
|
||||||
While this factor doesn't really authenticate a user, it is part of the Authentication Flow. passbook uses Google's reCaptcha implementation.
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
# Login Flow
|
||||||
|
|
||||||
|
This document describes how a simple authentication flow can be created.
|
||||||
|
|
||||||
|
This flow is created automatically when passbook is installed.
|
||||||
|
|
||||||
|
1. Create an **Identification** stage
|
||||||
|
|
||||||
|
> Here you can select whichever fields the user can identify themselves with
|
||||||
|
> Select the Template **Default Login**, as this template shows the (optional) Flows
|
||||||
|
> Here you can also link optional enrollment and recovery flows.
|
||||||
|
|
||||||
|
2. Create a **Password** stage
|
||||||
|
|
||||||
|
> Select the Backend you want the password to be checked against. Select "passbook-internal Userdatabase".
|
||||||
|
|
||||||
|
3. Create a **User Login** stage
|
||||||
|
|
||||||
|
> This stage doesn't have any options.
|
||||||
|
|
||||||
|
4. Create a flow
|
||||||
|
|
||||||
|
> Create a flow with the delegation of **Authentication**
|
||||||
|
> Assign a name and a slug. The slug is used in the URL when the flow is executed.
|
||||||
|
|
||||||
|
5. Bind the stages to the flow
|
||||||
|
|
||||||
|
> Bind the **Identification** Stage with an order of 0
|
||||||
|
> Bind the **Password** Stage with an order of 1
|
||||||
|
> Bind the **User Login** Stage with an order of 2
|
||||||
|
|
||||||
|
![](login.png)
|
||||||
|
|
||||||
|
!!! notice
|
||||||
|
|
||||||
|
This flow can used by any user, authenticated and un-authenticated. This means any authenticated user that visits this flow can login again.
|
After Width: | Height: | Size: 110 KiB |
|
@ -0,0 +1,45 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
Upon Flow execution, a plan is generated, which contains all stages. This means upon execution, all attached policies are evaluated. This behaviour can be altered by enabling the **Re-evaluate Policies** option on the binding.
|
||||||
|
|
||||||
|
To determine which flow is linked, passbook searches all Flows with the required designation and chooses the first instance the current user has access to.
|
||||||
|
|
||||||
|
## Permissions
|
||||||
|
|
||||||
|
Flows can have policies assigned to them, which determines if the current user is allowed to see and use this flow.
|
||||||
|
|
||||||
|
## Designation
|
||||||
|
|
||||||
|
Flows are designated for a single Purpose. This designation changes when a Flow is used. The following designations are available:
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
This is designates a flow to be used for authentication.
|
||||||
|
|
||||||
|
The authentication flow should always contain a [**User Login**](stages/user_login.md) stage, which attaches the staged user to the current session.
|
||||||
|
|
||||||
|
### Invalidation
|
||||||
|
|
||||||
|
This designates a flow to be used for the invalidation of a session.
|
||||||
|
|
||||||
|
This stage should always contain a [**User Logout**](stages/user_logout.md) stage, which resets the current session.
|
||||||
|
|
||||||
|
### Enrollment
|
||||||
|
|
||||||
|
This designates a flow for enrollment. This flow can contain any amount of Prompt stages, E-Mail verification or Captchas. At the end to create the user, you can use the [**User Write**](stages/user_write.md) stage, which either updates the currently staged user, or if none exists, creates a new one.
|
||||||
|
|
||||||
|
### Unenrollment
|
||||||
|
|
||||||
|
This designates a flow for unenrollment. This flow can contain any amount of verification, like [**E-Mail**](stages/email/index.md) or [**Captcha**](stages/captcha/index.md). To finally delete the account, use the [**User Delete**](stages/user_delete.md) stage.
|
||||||
|
|
||||||
|
### Recovery
|
||||||
|
|
||||||
|
This designates a flow for recovery. This flow normally contains an [**Identification**](stages/identification/index.md) stage to find the user. Then it can contain any amount of verification, like [**E-Mail**](stages/email/index.md) or [**Captcha**](stages/captcha/index.md).
|
||||||
|
Afterwards, use the [**Prompt**](stages/prompt/index.md) stage to ask the user for a new password and use [**User Write**](stages/user_write.md) to update the password.
|
||||||
|
|
||||||
|
### Change Password
|
||||||
|
|
||||||
|
This designates a flow for password changing. This flow can contain any amount of verification, like [**E-Mail**](stages/email/index.md) or [**Captcha**](stages/captcha/index.md).
|
||||||
|
Afterwards, use the [**Prompt**](stages/prompt/index.md) stage to ask the user for a new password and use [**User Write**](stages/user_write.md) to update the password.
|
After Width: | Height: | Size: 140 KiB |
|
@ -0,0 +1,7 @@
|
||||||
|
# Captcha stage
|
||||||
|
|
||||||
|
This stage adds a form of verification using [Google's ReCaptcha](https://www.google.com/recaptcha/intro/v3.html).
|
||||||
|
|
||||||
|
This stage has two required fields. You need a Public and a Private key, both of which you can acquire at https://www.google.com/recaptcha/admin.
|
||||||
|
|
||||||
|
![](captcha-admin.png)
|
After Width: | Height: | Size: 1.3 MiB |
|
@ -0,0 +1,5 @@
|
||||||
|
# Dummy stage
|
||||||
|
|
||||||
|
This stage is used for development, and has no function. It presents the User with a form, that requires a single confirmation.
|
||||||
|
|
||||||
|
![](dummy.png)
|
After Width: | Height: | Size: 51 KiB |
|
@ -0,0 +1,5 @@
|
||||||
|
# E-Mail
|
||||||
|
|
||||||
|
This stage can be used for E-Mail verification. passbook's background worker will send an E-Mail using the specified connection details. When an E-Mail can't be delivered, it is automatically periodically retried.
|
||||||
|
|
||||||
|
![](email-recovery.png)
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Identification
|
||||||
|
|
||||||
|
This stage provides a ready-to-go form for users to identify themselves.
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### User Fields
|
||||||
|
|
||||||
|
Select which fields the user can use to identify themselves. Multiple fields can be specified and separated with a comma.
|
||||||
|
Valid choices:
|
||||||
|
|
||||||
|
- email
|
||||||
|
- username
|
||||||
|
|
||||||
|
### Template
|
||||||
|
|
||||||
|
This specifies which template is rendered. Currently there are two templates.
|
||||||
|
|
||||||
|
The `Login` template shows configured Sources below the login form, as well as linking to the defined Enrollment and Recovery flows.
|
||||||
|
|
||||||
|
The `Recovery` template shows only the form.
|
||||||
|
|
||||||
|
### Enrollment/Recovery Flow
|
||||||
|
|
||||||
|
These fields specify if and which flows are linked on the form. The enrollment flow is linked as `Need an account? Sign up.`, and the recovery flow is linked as `Forgot username or password?`.
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Invitation Stage
|
||||||
|
|
||||||
|
This stage can be used to invite users. You can use this enroll users with preset values.
|
||||||
|
|
||||||
|
If the option `Continue Flow without Invitation`, this stage will continue when no invitation token is present.
|
||||||
|
|
||||||
|
If you want to check if a user has used an invitation within a policy, you can check `request.context.invitation_in_effect`.
|
|
@ -0,0 +1,7 @@
|
||||||
|
# OTP Stage
|
||||||
|
|
||||||
|
This stage offers a generic Time-based One-time Password authentication step.
|
||||||
|
|
||||||
|
You can optionally enforce this step, which will force every user without OTP setup to configure it.
|
||||||
|
|
||||||
|
This stage uses a 6-digit Code with a 30 second time-drift. This is currently not changeable.
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Password Stage
|
||||||
|
|
||||||
|
This is a generic password prompt, which authenticates the currently `pending_user`. This stage allows the selection of the Backend the user is authenticated against.
|
|
@ -0,0 +1,42 @@
|
||||||
|
# Prompt Stage
|
||||||
|
|
||||||
|
This stage is used to show the user arbitrary prompts.
|
||||||
|
|
||||||
|
## Prompt
|
||||||
|
|
||||||
|
The prompt can be any of the following types:
|
||||||
|
|
||||||
|
| | |
|
||||||
|
|----------|------------------------------------------------------------------|
|
||||||
|
| text | Arbitrary text, no client-side validation is done. |
|
||||||
|
| email | E-Mail input, requires a valid E-Mail adress |
|
||||||
|
| password | Password Input |
|
||||||
|
| number | Number Input, any number is allowed |
|
||||||
|
| checkbox | Simple Checkbox |
|
||||||
|
| hidden | Hidden Input field, allows for the pre-setting of default values |
|
||||||
|
|
||||||
|
A Prompt has the following attributes:
|
||||||
|
|
||||||
|
### `field_key`
|
||||||
|
|
||||||
|
HTML name used for the prompt. This key is also used to later retrieve the data in expression policies:
|
||||||
|
|
||||||
|
```python
|
||||||
|
request.context.get('prompt_data').get('<field_key>')
|
||||||
|
```
|
||||||
|
|
||||||
|
### `label`
|
||||||
|
|
||||||
|
Label used to describe the Field. This might not be shown depending on the template selected.
|
||||||
|
|
||||||
|
### `required`
|
||||||
|
|
||||||
|
Flag that decides whether or not this field is required.
|
||||||
|
|
||||||
|
### `placeholder`
|
||||||
|
|
||||||
|
Field placeholder, shown within the input field. This field is also used by the `hidden` type as the actual value.
|
||||||
|
|
||||||
|
### `order`
|
||||||
|
|
||||||
|
Numerical index of the prompt. This applies to all stages this prompt is a part of.
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Prompt Validation
|
||||||
|
|
||||||
|
Further validation of prompts can be done using policies.
|
||||||
|
|
||||||
|
To validate that two password fields are identical, create the following expression policy:
|
||||||
|
|
||||||
|
```python
|
||||||
|
if request.context.get('prompt_data').get('password') == request.context.get('prompt_data').get('password_repeat'):
|
||||||
|
return True
|
||||||
|
|
||||||
|
pb_message("Passwords don't match.")
|
||||||
|
return False
|
||||||
|
```
|
||||||
|
This policy expects you two have two password fields with `field_key` set to `password` and `password_repeat`.
|
||||||
|
|
||||||
|
Afterwards bind this policy to the prompt stage you want to validate.
|
|
@ -0,0 +1,8 @@
|
||||||
|
# User Delete Stage
|
||||||
|
|
||||||
|
!!! danger
|
||||||
|
This stage deletes the `pending_user` without any confirmation. You have to make sure the user is aware of this.
|
||||||
|
|
||||||
|
This stage is intended for an unenrollment flow. It deletes the currently pending user.
|
||||||
|
|
||||||
|
The pending user is also removed from the current session.
|
|
@ -0,0 +1,5 @@
|
||||||
|
# User Login Stage
|
||||||
|
|
||||||
|
This stage attaches a currently pending user to the current session.
|
||||||
|
|
||||||
|
It can be used after `user_write` during an enrollment flow, or after a `password` stage during an authentication flow.
|
|
@ -0,0 +1,3 @@
|
||||||
|
# User Logout Stage
|
||||||
|
|
||||||
|
Opposite stage of [User Login Stages](user_login.md). It removes the user from the current session.
|
|
@ -0,0 +1,3 @@
|
||||||
|
# User Write Stage
|
||||||
|
|
||||||
|
This stages writes data from the current context to the current pending user. If no user is pending, a new one is created.
|
|
@ -0,0 +1,2 @@
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="120px" height="20px" viewBox="15 0 10 10" enable-background="new 0 0 270 10" xml:space="preserve"><defs><style>.cls-1{isolation:isolate;}.cls-2{fill:#fff;}</style></defs><g class="cls-1"><path class="cls-2" d="M1.65,11V2.45H2.87V3a2.81,2.81,0,0,1,.47-.45A1.13,1.13,0,0,1,4,2.38,1.11,1.11,0,0,1,5.1,3a1.55,1.55,0,0,1,.16.5,5.61,5.61,0,0,1,0,.81V6.58c0,.45,0,.77,0,1a1.17,1.17,0,0,1-.55.9,1.23,1.23,0,0,1-.7.16,1.35,1.35,0,0,1-.64-.16A1.53,1.53,0,0,1,2.89,8h0v3ZM4.08,4.43a1.21,1.21,0,0,0-.14-.6.51.51,0,0,0-.46-.22A.54.54,0,0,0,3,3.82a.8.8,0,0,0-.17.54V6.73A.68.68,0,0,0,3,7.2a.6.6,0,0,0,.44.18A.53.53,0,0,0,4,7.17a1,1,0,0,0,.12-.5Z"/><path class="cls-2" d="M8.63,8.54V7.91h0a2.24,2.24,0,0,1-.48.52,1.13,1.13,0,0,1-.69.18A1.39,1.39,0,0,1,7,8.54a1.09,1.09,0,0,1-.43-.24,1.32,1.32,0,0,1-.33-.49A2.33,2.33,0,0,1,6.11,7a4.89,4.89,0,0,1,.08-.91,1.51,1.51,0,0,1,.31-.65,1.44,1.44,0,0,1,.59-.38A3.19,3.19,0,0,1,8,4.93h.59V4.33a1,1,0,0,0-.13-.52A.52.52,0,0,0,8,3.61a.71.71,0,0,0-.44.15.78.78,0,0,0-.26.46H6.13A2,2,0,0,1,6.69,2.9a1.73,1.73,0,0,1,.57-.38A2,2,0,0,1,8,2.38a2.18,2.18,0,0,1,.72.12,1.71,1.71,0,0,1,.59.36,2,2,0,0,1,.38.6,2.18,2.18,0,0,1,.14.84V8.54Zm0-2.62-.34,0a1.2,1.2,0,0,0-.67.18.76.76,0,0,0-.29.68.89.89,0,0,0,.17.56A.55.55,0,0,0,8,7.53a.63.63,0,0,0,.49-.2.91.91,0,0,0,.17-.58Z"/><path class="cls-2" d="M13,4.16a.59.59,0,0,0-.2-.47.65.65,0,0,0-.42-.16.59.59,0,0,0-.45.19.66.66,0,0,0-.15.43.8.8,0,0,0,.08.33.85.85,0,0,0,.44.29l.71.29a1.73,1.73,0,0,1,.95.72,2,2,0,0,1,.26,1,1.85,1.85,0,0,1-.52,1.3,1.56,1.56,0,0,1-.58.39,1.88,1.88,0,0,1-2-.32,1.58,1.58,0,0,1-.4-.57,1.81,1.81,0,0,1-.17-.8h1.15a1.11,1.11,0,0,0,.17.47.56.56,0,0,0,.49.22.71.71,0,0,0,.47-.18A.59.59,0,0,0,13,6.8a.69.69,0,0,0-.13-.43,1.08,1.08,0,0,0-.48-.32l-.59-.21a2.08,2.08,0,0,1-.9-.64,1.66,1.66,0,0,1-.33-1,1.89,1.89,0,0,1,.14-.72,1.78,1.78,0,0,1,.4-.57,1.5,1.5,0,0,1,.56-.36,1.82,1.82,0,0,1,.7-.13,1.93,1.93,0,0,1,.69.13,1.6,1.6,0,0,1,.54.38,1.85,1.85,0,0,1,.36.57,1.82,1.82,0,0,1,.13.7Z"/><path class="cls-2" d="M17.2,4.16a.63.63,0,0,0-.2-.47.69.69,0,0,0-.43-.16.55.55,0,0,0-.44.19.62.62,0,0,0-.16.43.68.68,0,0,0,.09.33.81.81,0,0,0,.43.29l.72.29a1.7,1.7,0,0,1,.94.72,2,2,0,0,1,.26,1,1.85,1.85,0,0,1-.52,1.3,1.61,1.61,0,0,1-.57.39,1.81,1.81,0,0,1-.74.15,1.76,1.76,0,0,1-1.24-.47,1.61,1.61,0,0,1-.41-.57,2,2,0,0,1-.17-.8h1.15a1.12,1.12,0,0,0,.18.47.53.53,0,0,0,.48.22.72.72,0,0,0,.48-.18.59.59,0,0,0,.21-.48.69.69,0,0,0-.14-.43,1,1,0,0,0-.48-.32l-.58-.21a2.06,2.06,0,0,1-.91-.64,1.66,1.66,0,0,1-.33-1A1.89,1.89,0,0,1,15,3.44a1.78,1.78,0,0,1,.4-.57,1.58,1.58,0,0,1,.56-.36,1.82,1.82,0,0,1,.7-.13,1.93,1.93,0,0,1,.69.13,1.75,1.75,0,0,1,.55.38,1.85,1.85,0,0,1,.36.57,2,2,0,0,1,.13.7Z"/><path class="cls-2" d="M19.2,8.54V0h1.22V3h0a1.53,1.53,0,0,1,.48-.47,1.39,1.39,0,0,1,.65-.16,1.26,1.26,0,0,1,.69.16,1.35,1.35,0,0,1,.4.39,1.18,1.18,0,0,1,.15.51,7.72,7.72,0,0,1,0,1V6.73a5.56,5.56,0,0,1-.05.8,1.56,1.56,0,0,1-.15.5,1.12,1.12,0,0,1-1.07.58,1.15,1.15,0,0,1-.7-.18A3.79,3.79,0,0,1,20.42,8v.55Zm2.44-4.21a1,1,0,0,0-.13-.51A.5.5,0,0,0,21,3.61a.57.57,0,0,0-.44.18.66.66,0,0,0-.18.48V6.63a.83.83,0,0,0,.17.54.52.52,0,0,0,.45.21.49.49,0,0,0,.45-.22,1.11,1.11,0,0,0,.15-.6Z"/><path class="cls-2" d="M23.76,4.49a4.83,4.83,0,0,1,0-.68A1.55,1.55,0,0,1,24,3.26a1.59,1.59,0,0,1,.62-.64,1.84,1.84,0,0,1,1-.24,1.87,1.87,0,0,1,1,.24,1.59,1.59,0,0,1,.62.64,1.55,1.55,0,0,1,.18.55,4.83,4.83,0,0,1,.05.68v2a4.72,4.72,0,0,1-.05.68,1.55,1.55,0,0,1-.18.55,1.59,1.59,0,0,1-.62.64,1.87,1.87,0,0,1-1,.24,1.84,1.84,0,0,1-1-.24A1.59,1.59,0,0,1,24,7.73a1.55,1.55,0,0,1-.18-.55,4.72,4.72,0,0,1,0-.68ZM25,6.69a.72.72,0,0,0,.17.52.53.53,0,0,0,.43.17A.55.55,0,0,0,26,7.21a.72.72,0,0,0,.16-.52V4.3A.74.74,0,0,0,26,3.78a.55.55,0,0,0-.44-.17.53.53,0,0,0-.43.17A.74.74,0,0,0,25,4.3Z"/><path class="cls-2" d="M28.2,4.49a4.83,4.83,0,0,1,.05-.68,1.55,1.55,0,0,1,.18-.55,1.59,1.59,0,0,1,.62-.64,1.84,1.84,0,0,1,1-.24,1.87,1.87,0,0,1,1,.24,1.59,1.59,0,0,1,.62.64,1.55,1.55,0,0,1,.18.55,4.83,4.83,0,0,1,.05.68v2a4.72,4.72,0,0,1-.05.68,1.55,1.55,0,0,1-.18.55,1.59,1.59,0,0,1-.62.64,1.87,1.87,0,0,1-1,.24,1.84,1.84,0,0,1-1-.24,1.59,1.59,0,0,1-.62-.64,1.55,1.55,0,0,1-.18-.55,4.72,4.72,0,0,1-.05-.68Zm1.22,2.2a.72.72,0,0,0,.17.52.53.53,0,0,0,.43.17.55.55,0,0,0,.44-.17.72.72,0,0,0,.16-.52V4.3a.74.74,0,0,0-.16-.52A.55.55,0,0,0,30,3.61a.53.53,0,0,0-.43.17.74.74,0,0,0-.17.52Z"/><path class="cls-2" d="M32.75,8.54V0H34V5.11h0l1.47-2.66H36.7L35.24,4.93,37,8.54H35.66l-1.1-2.63L34,6.83V8.54Z"/></g></svg>
|
After Width: | Height: | Size: 4.5 KiB |
|
@ -0,0 +1,2 @@
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="120px" height="20px" viewBox="15 0 10 10" enable-background="new 0 0 270 10" xml:space="preserve"><defs><style>.cls-1{isolation:isolate;}.cls-2{fill:#000;}</style></defs><g class="cls-1"><path class="cls-2" d="M1.65,11V2.45H2.87V3a2.81,2.81,0,0,1,.47-.45A1.13,1.13,0,0,1,4,2.38,1.11,1.11,0,0,1,5.1,3a1.55,1.55,0,0,1,.16.5,5.61,5.61,0,0,1,0,.81V6.58c0,.45,0,.77,0,1a1.17,1.17,0,0,1-.55.9,1.23,1.23,0,0,1-.7.16,1.35,1.35,0,0,1-.64-.16A1.53,1.53,0,0,1,2.89,8h0v3ZM4.08,4.43a1.21,1.21,0,0,0-.14-.6.51.51,0,0,0-.46-.22A.54.54,0,0,0,3,3.82a.8.8,0,0,0-.17.54V6.73A.68.68,0,0,0,3,7.2a.6.6,0,0,0,.44.18A.53.53,0,0,0,4,7.17a1,1,0,0,0,.12-.5Z"/><path class="cls-2" d="M8.63,8.54V7.91h0a2.24,2.24,0,0,1-.48.52,1.13,1.13,0,0,1-.69.18A1.39,1.39,0,0,1,7,8.54a1.09,1.09,0,0,1-.43-.24,1.32,1.32,0,0,1-.33-.49A2.33,2.33,0,0,1,6.11,7a4.89,4.89,0,0,1,.08-.91,1.51,1.51,0,0,1,.31-.65,1.44,1.44,0,0,1,.59-.38A3.19,3.19,0,0,1,8,4.93h.59V4.33a1,1,0,0,0-.13-.52A.52.52,0,0,0,8,3.61a.71.71,0,0,0-.44.15.78.78,0,0,0-.26.46H6.13A2,2,0,0,1,6.69,2.9a1.73,1.73,0,0,1,.57-.38A2,2,0,0,1,8,2.38a2.18,2.18,0,0,1,.72.12,1.71,1.71,0,0,1,.59.36,2,2,0,0,1,.38.6,2.18,2.18,0,0,1,.14.84V8.54Zm0-2.62-.34,0a1.2,1.2,0,0,0-.67.18.76.76,0,0,0-.29.68.89.89,0,0,0,.17.56A.55.55,0,0,0,8,7.53a.63.63,0,0,0,.49-.2.91.91,0,0,0,.17-.58Z"/><path class="cls-2" d="M13,4.16a.59.59,0,0,0-.2-.47.65.65,0,0,0-.42-.16.59.59,0,0,0-.45.19.66.66,0,0,0-.15.43.8.8,0,0,0,.08.33.85.85,0,0,0,.44.29l.71.29a1.73,1.73,0,0,1,.95.72,2,2,0,0,1,.26,1,1.85,1.85,0,0,1-.52,1.3,1.56,1.56,0,0,1-.58.39,1.88,1.88,0,0,1-2-.32,1.58,1.58,0,0,1-.4-.57,1.81,1.81,0,0,1-.17-.8h1.15a1.11,1.11,0,0,0,.17.47.56.56,0,0,0,.49.22.71.71,0,0,0,.47-.18A.59.59,0,0,0,13,6.8a.69.69,0,0,0-.13-.43,1.08,1.08,0,0,0-.48-.32l-.59-.21a2.08,2.08,0,0,1-.9-.64,1.66,1.66,0,0,1-.33-1,1.89,1.89,0,0,1,.14-.72,1.78,1.78,0,0,1,.4-.57,1.5,1.5,0,0,1,.56-.36,1.82,1.82,0,0,1,.7-.13,1.93,1.93,0,0,1,.69.13,1.6,1.6,0,0,1,.54.38,1.85,1.85,0,0,1,.36.57,1.82,1.82,0,0,1,.13.7Z"/><path class="cls-2" d="M17.2,4.16a.63.63,0,0,0-.2-.47.69.69,0,0,0-.43-.16.55.55,0,0,0-.44.19.62.62,0,0,0-.16.43.68.68,0,0,0,.09.33.81.81,0,0,0,.43.29l.72.29a1.7,1.7,0,0,1,.94.72,2,2,0,0,1,.26,1,1.85,1.85,0,0,1-.52,1.3,1.61,1.61,0,0,1-.57.39,1.81,1.81,0,0,1-.74.15,1.76,1.76,0,0,1-1.24-.47,1.61,1.61,0,0,1-.41-.57,2,2,0,0,1-.17-.8h1.15a1.12,1.12,0,0,0,.18.47.53.53,0,0,0,.48.22.72.72,0,0,0,.48-.18.59.59,0,0,0,.21-.48.69.69,0,0,0-.14-.43,1,1,0,0,0-.48-.32l-.58-.21a2.06,2.06,0,0,1-.91-.64,1.66,1.66,0,0,1-.33-1A1.89,1.89,0,0,1,15,3.44a1.78,1.78,0,0,1,.4-.57,1.58,1.58,0,0,1,.56-.36,1.82,1.82,0,0,1,.7-.13,1.93,1.93,0,0,1,.69.13,1.75,1.75,0,0,1,.55.38,1.85,1.85,0,0,1,.36.57,2,2,0,0,1,.13.7Z"/><path class="cls-2" d="M19.2,8.54V0h1.22V3h0a1.53,1.53,0,0,1,.48-.47,1.39,1.39,0,0,1,.65-.16,1.26,1.26,0,0,1,.69.16,1.35,1.35,0,0,1,.4.39,1.18,1.18,0,0,1,.15.51,7.72,7.72,0,0,1,0,1V6.73a5.56,5.56,0,0,1-.05.8,1.56,1.56,0,0,1-.15.5,1.12,1.12,0,0,1-1.07.58,1.15,1.15,0,0,1-.7-.18A3.79,3.79,0,0,1,20.42,8v.55Zm2.44-4.21a1,1,0,0,0-.13-.51A.5.5,0,0,0,21,3.61a.57.57,0,0,0-.44.18.66.66,0,0,0-.18.48V6.63a.83.83,0,0,0,.17.54.52.52,0,0,0,.45.21.49.49,0,0,0,.45-.22,1.11,1.11,0,0,0,.15-.6Z"/><path class="cls-2" d="M23.76,4.49a4.83,4.83,0,0,1,0-.68A1.55,1.55,0,0,1,24,3.26a1.59,1.59,0,0,1,.62-.64,1.84,1.84,0,0,1,1-.24,1.87,1.87,0,0,1,1,.24,1.59,1.59,0,0,1,.62.64,1.55,1.55,0,0,1,.18.55,4.83,4.83,0,0,1,.05.68v2a4.72,4.72,0,0,1-.05.68,1.55,1.55,0,0,1-.18.55,1.59,1.59,0,0,1-.62.64,1.87,1.87,0,0,1-1,.24,1.84,1.84,0,0,1-1-.24A1.59,1.59,0,0,1,24,7.73a1.55,1.55,0,0,1-.18-.55,4.72,4.72,0,0,1,0-.68ZM25,6.69a.72.72,0,0,0,.17.52.53.53,0,0,0,.43.17A.55.55,0,0,0,26,7.21a.72.72,0,0,0,.16-.52V4.3A.74.74,0,0,0,26,3.78a.55.55,0,0,0-.44-.17.53.53,0,0,0-.43.17A.74.74,0,0,0,25,4.3Z"/><path class="cls-2" d="M28.2,4.49a4.83,4.83,0,0,1,.05-.68,1.55,1.55,0,0,1,.18-.55,1.59,1.59,0,0,1,.62-.64,1.84,1.84,0,0,1,1-.24,1.87,1.87,0,0,1,1,.24,1.59,1.59,0,0,1,.62.64,1.55,1.55,0,0,1,.18.55,4.83,4.83,0,0,1,.05.68v2a4.72,4.72,0,0,1-.05.68,1.55,1.55,0,0,1-.18.55,1.59,1.59,0,0,1-.62.64,1.87,1.87,0,0,1-1,.24,1.84,1.84,0,0,1-1-.24,1.59,1.59,0,0,1-.62-.64,1.55,1.55,0,0,1-.18-.55,4.72,4.72,0,0,1-.05-.68Zm1.22,2.2a.72.72,0,0,0,.17.52.53.53,0,0,0,.43.17.55.55,0,0,0,.44-.17.72.72,0,0,0,.16-.52V4.3a.74.74,0,0,0-.16-.52A.55.55,0,0,0,30,3.61a.53.53,0,0,0-.43.17.74.74,0,0,0-.17.52Z"/><path class="cls-2" d="M32.75,8.54V0H34V5.11h0l1.47-2.66H36.7L35.24,4.93,37,8.54H35.66l-1.1-2.63L34,6.83V8.54Z"/></g></svg>
|
After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 175 KiB After Width: | Height: | Size: 175 KiB |
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 160 KiB |
|
@ -1,31 +1,16 @@
|
||||||
# Welcome
|
#
|
||||||
|
![passbook logo](images/logo.svg){: style="height:50px"}
|
||||||
|
![passbook brand](images/brand.svg){: style="height:50px"}
|
||||||
|
|
||||||
Welcome to the passbook Documentation. passbook is an open-source Identity Provider and Usermanagement software. It can be used as a central directory for users or customers and it can integrate with your existing Directory.
|
## What is passbook?
|
||||||
|
|
||||||
passbook can also be used as part of an Application to facilitate User Enrollment, Password recovery and Social Login.
|
passbook is an open-source Identity Provider focused on flexibility and versatility. You can use passbook in an existing environment to add support for new protocols. passbook is also a great solution for implementing signup/recovery/etc in your application, so you don't have to deal with it.
|
||||||
|
|
||||||
passbook uses the following Terminology:
|
## Installation
|
||||||
|
|
||||||
### Policy
|
See [Docker-compose](installation/docker-compose.md) or [Kubernetes](installation/kubernetes.md)
|
||||||
|
|
||||||
A Policy is at a base level a yes/no gate. It will either evaluate to True or False depending on the Policy Kind and settings. For example, a "Group Membership Policy" evaluates to True if the User is member of the specified Group and False if not. This can be used to conditionally apply Factors and grant/deny access.
|
## Screenshots
|
||||||
|
|
||||||
### Provider
|
![](images/screen_apps.png)
|
||||||
|
![](images/screen_admin.png)
|
||||||
A Provider is a way for other Applications to authenticate against passbook. Common Providers are OpenID Connect (OIDC) and SAML.
|
|
||||||
|
|
||||||
### Source
|
|
||||||
|
|
||||||
Sources are ways to get users into passbook. This might be an LDAP Connection to import Users from Active Directory, or an OAuth2 Connection to allow Social Logins.
|
|
||||||
|
|
||||||
### Application
|
|
||||||
|
|
||||||
An application links together Policies with a Provider, allowing you to control access. It also holds Information like UI Name, Icon and more.
|
|
||||||
|
|
||||||
### Factors
|
|
||||||
|
|
||||||
Factors represent Authentication Factors, like a Password or OTP. These Factors can be dynamically enabled using policies. This allows you to, for example, force users from a certain IP ranges to complete a Captcha to authenticate.
|
|
||||||
|
|
||||||
### Property Mappings
|
|
||||||
|
|
||||||
Property Mappings allow you to make Information available for external Applications. For example, if you want to login to AWS with passbook, you'd use Property Mappings to set the User's Roles based on their Groups.
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
# Installation
|
|
||||||
|
|
||||||
There are two supported ways to install passbook:
|
|
||||||
|
|
||||||
- [docker-compose](docker-compose.md) for test- or small productive setups
|
|
||||||
- [Kubernetes](./kubernetes.md) for larger Productive setups
|
|
|
@ -1,33 +0,0 @@
|
||||||
---
|
|
||||||
apiVersion: apps/v1beta2
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: passbook-docs
|
|
||||||
namespace: prod-passbook-docs
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: passbook-docs
|
|
||||||
app.kubernetes.io/managed-by: passbook-docs
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/name: passbook-docs
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: passbook-docs
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: passbook-docs
|
|
||||||
image: "beryju/passbook-docs:latest"
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
containerPort: 80
|
|
||||||
protocol: TCP
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpu: 10m
|
|
||||||
memory: 20Mi
|
|
||||||
requests:
|
|
||||||
cpu: 10m
|
|
||||||
memory: 20Mi
|
|
|
@ -1,21 +0,0 @@
|
||||||
---
|
|
||||||
apiVersion: extensions/v1beta1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: passbook-docs
|
|
||||||
name: passbook-docs
|
|
||||||
namespace: prod-passbook-docs
|
|
||||||
spec:
|
|
||||||
rules:
|
|
||||||
- host: docs.passbook.beryju.org
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- backend:
|
|
||||||
serviceName: passbook-docs-http
|
|
||||||
servicePort: http
|
|
||||||
path: /
|
|
||||||
tls:
|
|
||||||
- hosts:
|
|
||||||
- docs.passbook.beryju.org
|
|
||||||
secretName: passbook-docs-acme
|
|
|
@ -1,17 +0,0 @@
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: passbook-docs-http
|
|
||||||
namespace: prod-passbook-docs
|
|
||||||
labels:
|
|
||||||
app.kubernetes.io/name: passbook-docs
|
|
||||||
spec:
|
|
||||||
type: ClusterIP
|
|
||||||
ports:
|
|
||||||
- port: 80
|
|
||||||
targetPort: http
|
|
||||||
protocol: TCP
|
|
||||||
name: http
|
|
||||||
selector:
|
|
||||||
app.kubernetes.io/name: passbook-docs
|
|
|
@ -1,5 +1,8 @@
|
||||||
# Expression Policies
|
# Expression Policies
|
||||||
|
|
||||||
|
!!! notice
|
||||||
|
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 `return True` to pass a policy and `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
|
||||||
|
|
|
@ -27,11 +27,11 @@ See [Expression Policy](expression.md).
|
||||||
This Policy allows you to specify Password rules, like Length and required Characters.
|
This Policy allows you to specify Password rules, like Length and required Characters.
|
||||||
The following rules can be set:
|
The following rules can be set:
|
||||||
|
|
||||||
- Minimum amount of Uppercase Characters
|
- Minimum amount of Uppercase Characters
|
||||||
- Minimum amount of Lowercase Characters
|
- Minimum amount of Lowercase Characters
|
||||||
- Minimum amount of Symbols Characters
|
- Minimum amount of Symbols Characters
|
||||||
- Minimum Length
|
- Minimum Length
|
||||||
- Symbol charset (define which characters are counted as symbols)
|
- Symbol charset (define which characters are counted as symbols)
|
||||||
|
|
||||||
### Have I Been Pwned Policy
|
### Have I Been Pwned Policy
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
The property mapping should return a value that is expected by the Provider/Source. What types are supported, is documented in the individual Provider/Source. Returning `None` is always accepted, this simply skips this mapping.
|
The property mapping should return a value that is expected by the Provider/Source. What types are supported, is documented in the individual Provider/Source. Returning `None` is always accepted, this simply skips this mapping.
|
||||||
|
|
||||||
|
!!! notice
|
||||||
|
These variables are available in addition to the common variables/functions defined in [**Expressions**](../expressions/index.md)
|
||||||
|
|
||||||
### Context Variables
|
### Context Variables
|
||||||
|
|
||||||
- `user`: The current user, this might be `None` if there is no contextual user. ([ref](../expressions/reference/user-object.md))
|
- `user`: The current user, this might be `None` if there is no contextual user. ([ref](../expressions/reference/user-object.md))
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
mkdocs
|
||||||
|
mkdocs-material
|
|
@ -0,0 +1 @@
|
||||||
|
3.7
|
|
@ -0,0 +1,27 @@
|
||||||
|
### Policy
|
||||||
|
|
||||||
|
A Policy is at a base level a yes/no gate. It will either evaluate to True or False depending on the Policy Kind and settings. For example, a "Group Membership Policy" evaluates to True if the User is member of the specified Group and False if not. This can be used to conditionally apply Stages, grant/deny access to various objects and is also used for other custom logic.
|
||||||
|
|
||||||
|
### Provider
|
||||||
|
|
||||||
|
A Provider is a way for other Applications to authenticate against passbook. Common Providers are OpenID Connect (OIDC) and SAML.
|
||||||
|
|
||||||
|
### Source
|
||||||
|
|
||||||
|
Sources are ways to get users into passbook. This might be an LDAP Connection to import Users from Active Directory, or an OAuth2 Connection to allow Social Logins.
|
||||||
|
|
||||||
|
### Application
|
||||||
|
|
||||||
|
An application links together Policies with a Provider, allowing you to control access. It also holds Information like UI Name, Icon and more.
|
||||||
|
|
||||||
|
### Flows
|
||||||
|
|
||||||
|
Flows are a method of describing a sequence of stages. These flows can be used to defined how a user authenticates, enrolls, etc.
|
||||||
|
|
||||||
|
### Stages
|
||||||
|
|
||||||
|
A stage represents a single verification or logic step. They are used to authenticate users, enroll them, and more. These stages can optionally be applied to a flow via policies.
|
||||||
|
|
||||||
|
### Property Mappings
|
||||||
|
|
||||||
|
Property Mappings allow you to make Information available for external Applications. For example, if you want to login to AWS with passbook, you'd use Property Mappings to set the User's Roles based on their Groups.
|
32
mkdocs.yml
|
@ -1,13 +1,31 @@
|
||||||
site_name: passbook Docs
|
site_name: passbook Docs
|
||||||
site_url: https://beryju.github.io/passbook
|
site_url: https://passbook.beryju.org/
|
||||||
copyright: "Copyright © 2019 - 2020 BeryJu.org"
|
copyright: "Copyright © 2019 - 2020 BeryJu.org"
|
||||||
|
|
||||||
nav:
|
nav:
|
||||||
- Home: index.md
|
- Home: index.md
|
||||||
|
- Terminology: terminology.md
|
||||||
- Installation:
|
- Installation:
|
||||||
- Installation: installation/install.md
|
|
||||||
- docker-compose: installation/docker-compose.md
|
- docker-compose: installation/docker-compose.md
|
||||||
- Kubernetes: installation/kubernetes.md
|
- Kubernetes: installation/kubernetes.md
|
||||||
|
- Flows:
|
||||||
|
Overview: flow/flows.md
|
||||||
|
Examples:
|
||||||
|
- Login: flow/examples/login.md
|
||||||
|
- Stages:
|
||||||
|
- Captcha Stage: flow/stages/captcha/index.md
|
||||||
|
- Dummy Stage: flow/stages/dummy/index.md
|
||||||
|
- E-Mail Stage: flow/stages/email/index.md
|
||||||
|
- Identification Stage: flow/stages/identification/index.md
|
||||||
|
- Invitation Stage: flow/stages/invitation/index.md
|
||||||
|
- OTP Stage: flow/stages/otp/index.md
|
||||||
|
- Password Stage: flow/stages/password/index.md
|
||||||
|
- Prompt Stage: flow/stages/prompt/index.md
|
||||||
|
- Prompt Stage Validation: flow/stages/prompt/validation.md
|
||||||
|
- User Delete Stage: flow/stages/user_delete.md
|
||||||
|
- User Login Stage: flow/stages/user_login.md
|
||||||
|
- User Logout Stage: flow/stages/user_logout.md
|
||||||
|
- User Write Stage: flow/stages/user_write.md
|
||||||
- Sources: sources.md
|
- Sources: sources.md
|
||||||
- Providers: providers.md
|
- Providers: providers.md
|
||||||
- Expressions:
|
- Expressions:
|
||||||
|
@ -17,7 +35,6 @@ nav:
|
||||||
- Property Mappings:
|
- Property Mappings:
|
||||||
- Overview: property-mappings/index.md
|
- Overview: property-mappings/index.md
|
||||||
- Expressions: property-mappings/expression.md
|
- Expressions: property-mappings/expression.md
|
||||||
- Factors: factors.md
|
|
||||||
- Policies:
|
- Policies:
|
||||||
- Overview: policies/index.md
|
- Overview: policies/index.md
|
||||||
- Expression: policies/expression.md
|
- Expression: policies/expression.md
|
||||||
|
@ -34,8 +51,12 @@ nav:
|
||||||
repo_name: "BeryJu/passbook"
|
repo_name: "BeryJu/passbook"
|
||||||
repo_url: https://github.com/BeryJu/passbook
|
repo_url: https://github.com/BeryJu/passbook
|
||||||
theme:
|
theme:
|
||||||
name: "material"
|
name: material
|
||||||
logo: "images/logo.svg"
|
logo: images/logo.svg
|
||||||
|
favicon: images/logo.svg
|
||||||
|
palette:
|
||||||
|
scheme: slate
|
||||||
|
primary: white
|
||||||
|
|
||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
- toc:
|
- toc:
|
||||||
|
@ -46,6 +67,7 @@ markdown_extensions:
|
||||||
smart_enable: all
|
smart_enable: all
|
||||||
- pymdownx.inlinehilite
|
- pymdownx.inlinehilite
|
||||||
- pymdownx.magiclink
|
- pymdownx.magiclink
|
||||||
|
- attr_list
|
||||||
|
|
||||||
plugins:
|
plugins:
|
||||||
- search
|
- search
|
||||||
|
|
|
@ -62,6 +62,8 @@
|
||||||
<ul>
|
<ul>
|
||||||
{% for flow in stage.flow_set.all %}
|
{% for flow in stage.flow_set.all %}
|
||||||
<li><a href="{% url 'passbook_admin:flow-update' pk=flow.pk %}">{{ flow.slug }}</a></li>
|
<li><a href="{% url 'passbook_admin:flow-update' pk=flow.pk %}">{{ flow.slug }}</a></li>
|
||||||
|
{% empty %}
|
||||||
|
<li>-</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
{% block above_form %}
|
{% block above_form %}
|
||||||
<h1>
|
<h1>
|
||||||
{% blocktrans with type=form|form_verbose_name|title inst=form.instance %}
|
{% blocktrans with type=form|form_verbose_name|title inst=form.instance %}
|
||||||
Update {{ type }}: {{ inst }}
|
Update {{ inst }}
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
</h1>
|
</h1>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
{% if c.data.selected %} checked {% endif %}>
|
{% if c.data.selected %} checked {% endif %}>
|
||||||
<label class="pf-c-form__label" for="{{ field.name }}-{{ forloop.counter0 }}">{{ c.choice_label }}</label>
|
<label class="pf-c-form__label" for="{{ field.name }}-{{ forloop.counter0 }}">{{ c.choice_label }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
{% if field.help_text %}
|
||||||
|
<p class="pf-c-form__helper-text">{{ field.help_text }}</p>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% elif field.field.widget|fieldtype == 'Select' %}
|
{% elif field.field.widget|fieldtype == 'Select' %}
|
||||||
<label class="pf-c-form__label" for="{{ field.name }}-{{ forloop.counter0 }}">
|
<label class="pf-c-form__label" for="{{ field.name }}-{{ forloop.counter0 }}">
|
||||||
|
|
|
@ -44,6 +44,9 @@ class FlowStageBindingForm(forms.ModelForm):
|
||||||
"re_evaluate_policies",
|
"re_evaluate_policies",
|
||||||
"order",
|
"order",
|
||||||
]
|
]
|
||||||
|
labels = {
|
||||||
|
"re_evaluate_policies": _("Re-evaluate Policies"),
|
||||||
|
}
|
||||||
widgets = {
|
widgets = {
|
||||||
"name": forms.TextInput(),
|
"name": forms.TextInput(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
<p></p>
|
<p></p>
|
||||||
<ul class="pf-c-list pf-m-inline">
|
<ul class="pf-c-list pf-m-inline">
|
||||||
<li>
|
<li>
|
||||||
<a href="https://beryju.github.io/passbook/">{% trans 'Documentation' %}</a>
|
<a href="https://passbook.beryju.org/">{% trans 'Documentation' %}</a>
|
||||||
</li>
|
</li>
|
||||||
<!-- todo: load config.passbook.footer.links -->
|
<!-- todo: load config.passbook.footer.links -->
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -7,23 +7,8 @@
|
||||||
<label for="" class="pf-c-form__label"></label>
|
<label for="" class="pf-c-form__label"></label>
|
||||||
<div class="c-form__horizontal-group">
|
<div class="c-form__horizontal-group">
|
||||||
<p>
|
<p>
|
||||||
Expression using <a href="https://jinja.palletsprojects.com/en/2.11.x/templates/">Jinja</a>. Following variables are available:
|
Expression using Python. See <a href="https://passbook.beryju.org/policies/expression/">here</a> for a list of all variables.
|
||||||
</p>
|
</p>
|
||||||
<ul class="pf-c-list">
|
|
||||||
<li><code>request.user</code>: Passbook User Object (<a href="https://beryju.github.io/passbook/property-mappings/reference/user-object/">Reference</a>)</li>
|
|
||||||
<li><code>request.http_request</code>: Django HTTP Request Object (<a href="https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects">Reference</a>) </li>
|
|
||||||
<li><code>request.obj</code>: Model the Policy is run against. </li>
|
|
||||||
<li><code>pb_flow_plan</code>: Current Plan if Policy is called while a flow is active.</li>
|
|
||||||
<li><code>pb_is_sso_flow</code>: Boolean which is true if request was initiated by authenticating through an external Provider.</li>
|
|
||||||
<li><code>pb_is_group_member(user, group_name)</code>: Function which checks if <code>user</code> is member of a Group with Name <code>group_name</code>.</li>
|
|
||||||
<li><code>pb_logger</code>: Standard Python Logger Object, which can be used to debug expressions.</li>
|
|
||||||
<li><code>pb_client_ip</code>: Client's IP Address.</li>
|
|
||||||
</ul>
|
|
||||||
<p>Custom Filters:</p>
|
|
||||||
<ul class="pf-c-list">
|
|
||||||
<li><code>regex_match(regex)</code>: Checks if value matches <code>regex</code></li>
|
|
||||||
<li><code>regex_replace(regex, repl)</code>: Replace string matched by <code>regex</code> with <code>repl</code></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||||
|
from django.utils.html import mark_safe
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
from passbook.admin.fields import CodeMirrorWidget
|
||||||
from passbook.core.expression import PropertyMappingEvaluator
|
from passbook.core.expression import PropertyMappingEvaluator
|
||||||
from passbook.flows.models import Flow, FlowDesignation
|
from passbook.flows.models import Flow, FlowDesignation
|
||||||
from passbook.providers.saml.models import (
|
from passbook.providers.saml.models import (
|
||||||
|
@ -74,4 +76,13 @@ class SAMLPropertyMappingForm(forms.ModelForm):
|
||||||
"name": forms.TextInput(),
|
"name": forms.TextInput(),
|
||||||
"saml_name": forms.TextInput(),
|
"saml_name": forms.TextInput(),
|
||||||
"friendly_name": forms.TextInput(),
|
"friendly_name": forms.TextInput(),
|
||||||
|
"expression": CodeMirrorWidget(mode="python"),
|
||||||
|
}
|
||||||
|
help_texts = {
|
||||||
|
"saml_name": mark_safe(
|
||||||
|
_(
|
||||||
|
"URN OID used by SAML. This is optional. "
|
||||||
|
'<a href="https://www.rfc-editor.org/rfc/rfc2798.html#section-2">Reference</a>'
|
||||||
|
)
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,7 @@
|
||||||
<label for="" class="pf-c-form__label"></label>
|
<label for="" class="pf-c-form__label"></label>
|
||||||
<div class="c-form__horizontal-group">
|
<div class="c-form__horizontal-group">
|
||||||
<p>
|
<p>
|
||||||
Expression using <a href="https://jinja.palletsprojects.com/en/2.11.x/templates/">Jinja</a>. Following variables are available:
|
Expression using Python. See <a href="https://passbook.beryju.org/property-mappings/expression/">here</a> for a list of all variables.
|
||||||
<ul class="pf-c-list">
|
|
||||||
<li><code>user</code>: Passbook User Object (<a href="https://beryju.github.io/passbook/reference/property-mappings/user-object/">Reference</a>)</li>
|
|
||||||
<li><code>request</code>: Django HTTP Request Object (<a href="https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects">Reference</a>) </li>
|
|
||||||
<li><code>provider</code>: Passbook SAML Provider Object (<a href="https://github.com/BeryJu/passbook/blob/master/passbook/providers/saml/models.py#L16">Reference</a>) </li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,6 +4,7 @@ from django import forms
|
||||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from passbook.admin.fields import CodeMirrorWidget
|
||||||
from passbook.admin.forms.source import SOURCE_FORM_FIELDS
|
from passbook.admin.forms.source import SOURCE_FORM_FIELDS
|
||||||
from passbook.core.expression import PropertyMappingEvaluator
|
from passbook.core.expression import PropertyMappingEvaluator
|
||||||
from passbook.sources.ldap.models import LDAPPropertyMapping, LDAPSource
|
from passbook.sources.ldap.models import LDAPPropertyMapping, LDAPSource
|
||||||
|
@ -68,4 +69,8 @@ class LDAPPropertyMappingForm(forms.ModelForm):
|
||||||
"name": forms.TextInput(),
|
"name": forms.TextInput(),
|
||||||
"ldap_property": forms.TextInput(),
|
"ldap_property": forms.TextInput(),
|
||||||
"object_field": forms.TextInput(),
|
"object_field": forms.TextInput(),
|
||||||
|
"expression": CodeMirrorWidget(mode="python"),
|
||||||
|
}
|
||||||
|
help_texts = {
|
||||||
|
"object_field": _("Field of the user object this value is written to.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,7 @@
|
||||||
<label for="" class="pf-c-form__label"></label>
|
<label for="" class="pf-c-form__label"></label>
|
||||||
<div class="c-form__horizontal-group">
|
<div class="c-form__horizontal-group">
|
||||||
<p>
|
<p>
|
||||||
Expression using <a href="https://jinja.palletsprojects.com/en/2.11.x/templates/">Jinja</a>. Following variables are available:
|
Expression using Python. See <a href="https://passbook.beryju.org/property-mappings/expression/">here</a> for a list of all variables.
|
||||||
<ul class="pf-c-list">
|
|
||||||
<li><code>ldap</code>: A Dictionary of all values retrieved from LDAP.</li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -34,6 +34,7 @@ class EmailStageForm(forms.ModelForm):
|
||||||
widgets = {
|
widgets = {
|
||||||
"name": forms.TextInput(),
|
"name": forms.TextInput(),
|
||||||
"host": forms.TextInput(),
|
"host": forms.TextInput(),
|
||||||
|
"subject": forms.TextInput(),
|
||||||
"username": forms.TextInput(),
|
"username": forms.TextInput(),
|
||||||
"password": forms.TextInput(),
|
"password": forms.TextInput(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,9 @@ class EmailStageView(FormView, StageView):
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
def form_invalid(self, form: EmailStageSendForm) -> HttpResponse:
|
def form_invalid(self, form: EmailStageSendForm) -> HttpResponse:
|
||||||
|
if PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context:
|
||||||
|
messages.error(self.request, _("No pending user."))
|
||||||
|
return super().form_invalid(form)
|
||||||
pending_user = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
|
pending_user = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
|
||||||
valid_delta = timedelta(
|
valid_delta = timedelta(
|
||||||
minutes=self.executor.current_stage.token_expiry + 1
|
minutes=self.executor.current_stage.token_expiry + 1
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
"""passbook flows identification forms"""
|
"""passbook flows identification forms"""
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||||
from django.core.validators import validate_email
|
from django.core.validators import validate_email
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from structlog import get_logger
|
from structlog import get_logger
|
||||||
|
@ -19,6 +20,9 @@ class IdentificationStageForm(forms.ModelForm):
|
||||||
fields = ["name", "user_fields", "template", "enrollment_flow", "recovery_flow"]
|
fields = ["name", "user_fields", "template", "enrollment_flow", "recovery_flow"]
|
||||||
widgets = {
|
widgets = {
|
||||||
"name": forms.TextInput(),
|
"name": forms.TextInput(),
|
||||||
|
"user_fields": FilteredSelectMultiple(
|
||||||
|
_("fields"), False, choices=UserFields.choices
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ class IdentificationStage(Stage):
|
||||||
related_name="+",
|
related_name="+",
|
||||||
default=None,
|
default=None,
|
||||||
help_text=_(
|
help_text=_(
|
||||||
"Optional enrollment flow, which is linked at the bottom of the page."
|
"Optional recovery flow, which is linked at the bottom of the page."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ from passbook.stages.invitation.models import Invitation, InvitationStage
|
||||||
from passbook.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
from passbook.stages.prompt.stage import PLAN_CONTEXT_PROMPT
|
||||||
|
|
||||||
INVITATION_TOKEN_KEY = "token"
|
INVITATION_TOKEN_KEY = "token"
|
||||||
|
INVITATION_IN_EFFECT = "invitation_in_effect"
|
||||||
|
|
||||||
|
|
||||||
class InvitationStageView(StageView):
|
class InvitationStageView(StageView):
|
||||||
|
@ -23,4 +24,5 @@ class InvitationStageView(StageView):
|
||||||
token = request.GET[INVITATION_TOKEN_KEY]
|
token = request.GET[INVITATION_TOKEN_KEY]
|
||||||
invite: Invitation = get_object_or_404(Invitation, pk=token)
|
invite: Invitation = get_object_or_404(Invitation, pk=token)
|
||||||
self.executor.plan.context[PLAN_CONTEXT_PROMPT] = invite.fixed_data
|
self.executor.plan.context[PLAN_CONTEXT_PROMPT] = invite.fixed_data
|
||||||
|
self.executor.plan.context[INVITATION_IN_EFFECT] = True
|
||||||
return self.executor.stage_ok()
|
return self.executor.stage_ok()
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
"""passbook administration forms"""
|
"""passbook administration forms"""
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from passbook.lib.utils.reflection import path_to_class
|
|
||||||
from passbook.stages.password.models import PasswordStage
|
from passbook.stages.password.models import PasswordStage
|
||||||
|
|
||||||
|
|
||||||
def get_authentication_backends():
|
def get_authentication_backends():
|
||||||
"""Return all available authentication backends as tuple set"""
|
"""Return all available authentication backends as tuple set"""
|
||||||
for backend in settings.AUTHENTICATION_BACKENDS:
|
return [
|
||||||
klass = path_to_class(backend)
|
(
|
||||||
yield backend, getattr(
|
"django.contrib.auth.backends.ModelBackend",
|
||||||
klass(), "name", "%s (%s)" % (klass.__name__, klass.__module__)
|
_("passbook-internal Userdatabase"),
|
||||||
)
|
),
|
||||||
|
(
|
||||||
|
"passbook.sources.ldap.auth.LDAPBackend",
|
||||||
|
_("passbook LDAP (Only needed when User-Sync is not enabled."),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class PasswordForm(forms.Form):
|
class PasswordForm(forms.Form):
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="120px" height="20px" viewBox="15 0 10 10" enable-background="new 0 0 270 10" xml:space="preserve"><defs><style>.cls-1{isolation:isolate;}.cls-2{fill:#000;}</style></defs><g class="cls-1"><path class="cls-2" d="M1.65,11V2.45H2.87V3a2.81,2.81,0,0,1,.47-.45A1.13,1.13,0,0,1,4,2.38,1.11,1.11,0,0,1,5.1,3a1.55,1.55,0,0,1,.16.5,5.61,5.61,0,0,1,0,.81V6.58c0,.45,0,.77,0,1a1.17,1.17,0,0,1-.55.9,1.23,1.23,0,0,1-.7.16,1.35,1.35,0,0,1-.64-.16A1.53,1.53,0,0,1,2.89,8h0v3ZM4.08,4.43a1.21,1.21,0,0,0-.14-.6.51.51,0,0,0-.46-.22A.54.54,0,0,0,3,3.82a.8.8,0,0,0-.17.54V6.73A.68.68,0,0,0,3,7.2a.6.6,0,0,0,.44.18A.53.53,0,0,0,4,7.17a1,1,0,0,0,.12-.5Z"/><path class="cls-2" d="M8.63,8.54V7.91h0a2.24,2.24,0,0,1-.48.52,1.13,1.13,0,0,1-.69.18A1.39,1.39,0,0,1,7,8.54a1.09,1.09,0,0,1-.43-.24,1.32,1.32,0,0,1-.33-.49A2.33,2.33,0,0,1,6.11,7a4.89,4.89,0,0,1,.08-.91,1.51,1.51,0,0,1,.31-.65,1.44,1.44,0,0,1,.59-.38A3.19,3.19,0,0,1,8,4.93h.59V4.33a1,1,0,0,0-.13-.52A.52.52,0,0,0,8,3.61a.71.71,0,0,0-.44.15.78.78,0,0,0-.26.46H6.13A2,2,0,0,1,6.69,2.9a1.73,1.73,0,0,1,.57-.38A2,2,0,0,1,8,2.38a2.18,2.18,0,0,1,.72.12,1.71,1.71,0,0,1,.59.36,2,2,0,0,1,.38.6,2.18,2.18,0,0,1,.14.84V8.54Zm0-2.62-.34,0a1.2,1.2,0,0,0-.67.18.76.76,0,0,0-.29.68.89.89,0,0,0,.17.56A.55.55,0,0,0,8,7.53a.63.63,0,0,0,.49-.2.91.91,0,0,0,.17-.58Z"/><path class="cls-2" d="M13,4.16a.59.59,0,0,0-.2-.47.65.65,0,0,0-.42-.16.59.59,0,0,0-.45.19.66.66,0,0,0-.15.43.8.8,0,0,0,.08.33.85.85,0,0,0,.44.29l.71.29a1.73,1.73,0,0,1,.95.72,2,2,0,0,1,.26,1,1.85,1.85,0,0,1-.52,1.3,1.56,1.56,0,0,1-.58.39,1.88,1.88,0,0,1-2-.32,1.58,1.58,0,0,1-.4-.57,1.81,1.81,0,0,1-.17-.8h1.15a1.11,1.11,0,0,0,.17.47.56.56,0,0,0,.49.22.71.71,0,0,0,.47-.18A.59.59,0,0,0,13,6.8a.69.69,0,0,0-.13-.43,1.08,1.08,0,0,0-.48-.32l-.59-.21a2.08,2.08,0,0,1-.9-.64,1.66,1.66,0,0,1-.33-1,1.89,1.89,0,0,1,.14-.72,1.78,1.78,0,0,1,.4-.57,1.5,1.5,0,0,1,.56-.36,1.82,1.82,0,0,1,.7-.13,1.93,1.93,0,0,1,.69.13,1.6,1.6,0,0,1,.54.38,1.85,1.85,0,0,1,.36.57,1.82,1.82,0,0,1,.13.7Z"/><path class="cls-2" d="M17.2,4.16a.63.63,0,0,0-.2-.47.69.69,0,0,0-.43-.16.55.55,0,0,0-.44.19.62.62,0,0,0-.16.43.68.68,0,0,0,.09.33.81.81,0,0,0,.43.29l.72.29a1.7,1.7,0,0,1,.94.72,2,2,0,0,1,.26,1,1.85,1.85,0,0,1-.52,1.3,1.61,1.61,0,0,1-.57.39,1.81,1.81,0,0,1-.74.15,1.76,1.76,0,0,1-1.24-.47,1.61,1.61,0,0,1-.41-.57,2,2,0,0,1-.17-.8h1.15a1.12,1.12,0,0,0,.18.47.53.53,0,0,0,.48.22.72.72,0,0,0,.48-.18.59.59,0,0,0,.21-.48.69.69,0,0,0-.14-.43,1,1,0,0,0-.48-.32l-.58-.21a2.06,2.06,0,0,1-.91-.64,1.66,1.66,0,0,1-.33-1A1.89,1.89,0,0,1,15,3.44a1.78,1.78,0,0,1,.4-.57,1.58,1.58,0,0,1,.56-.36,1.82,1.82,0,0,1,.7-.13,1.93,1.93,0,0,1,.69.13,1.75,1.75,0,0,1,.55.38,1.85,1.85,0,0,1,.36.57,2,2,0,0,1,.13.7Z"/><path class="cls-2" d="M19.2,8.54V0h1.22V3h0a1.53,1.53,0,0,1,.48-.47,1.39,1.39,0,0,1,.65-.16,1.26,1.26,0,0,1,.69.16,1.35,1.35,0,0,1,.4.39,1.18,1.18,0,0,1,.15.51,7.72,7.72,0,0,1,0,1V6.73a5.56,5.56,0,0,1-.05.8,1.56,1.56,0,0,1-.15.5,1.12,1.12,0,0,1-1.07.58,1.15,1.15,0,0,1-.7-.18A3.79,3.79,0,0,1,20.42,8v.55Zm2.44-4.21a1,1,0,0,0-.13-.51A.5.5,0,0,0,21,3.61a.57.57,0,0,0-.44.18.66.66,0,0,0-.18.48V6.63a.83.83,0,0,0,.17.54.52.52,0,0,0,.45.21.49.49,0,0,0,.45-.22,1.11,1.11,0,0,0,.15-.6Z"/><path class="cls-2" d="M23.76,4.49a4.83,4.83,0,0,1,0-.68A1.55,1.55,0,0,1,24,3.26a1.59,1.59,0,0,1,.62-.64,1.84,1.84,0,0,1,1-.24,1.87,1.87,0,0,1,1,.24,1.59,1.59,0,0,1,.62.64,1.55,1.55,0,0,1,.18.55,4.83,4.83,0,0,1,.05.68v2a4.72,4.72,0,0,1-.05.68,1.55,1.55,0,0,1-.18.55,1.59,1.59,0,0,1-.62.64,1.87,1.87,0,0,1-1,.24,1.84,1.84,0,0,1-1-.24A1.59,1.59,0,0,1,24,7.73a1.55,1.55,0,0,1-.18-.55,4.72,4.72,0,0,1,0-.68ZM25,6.69a.72.72,0,0,0,.17.52.53.53,0,0,0,.43.17A.55.55,0,0,0,26,7.21a.72.72,0,0,0,.16-.52V4.3A.74.74,0,0,0,26,3.78a.55.55,0,0,0-.44-.17.53.53,0,0,0-.43.17A.74.74,0,0,0,25,4.3Z"/><path class="cls-2" d="M28.2,4.49a4.83,4.83,0,0,1,.05-.68,1.55,1.55,0,0,1,.18-.55,1.59,1.59,0,0,1,.62-.64,1.84,1.84,0,0,1,1-.24,1.87,1.87,0,0,1,1,.24,1.59,1.59,0,0,1,.62.64,1.55,1.55,0,0,1,.18.55,4.83,4.83,0,0,1,.05.68v2a4.72,4.72,0,0,1-.05.68,1.55,1.55,0,0,1-.18.55,1.59,1.59,0,0,1-.62.64,1.87,1.87,0,0,1-1,.24,1.84,1.84,0,0,1-1-.24,1.59,1.59,0,0,1-.62-.64,1.55,1.55,0,0,1-.18-.55,4.72,4.72,0,0,1-.05-.68Zm1.22,2.2a.72.72,0,0,0,.17.52.53.53,0,0,0,.43.17.55.55,0,0,0,.44-.17.72.72,0,0,0,.16-.52V4.3a.74.74,0,0,0-.16-.52A.55.55,0,0,0,30,3.61a.53.53,0,0,0-.43.17.74.74,0,0,0-.17.52Z"/><path class="cls-2" d="M32.75,8.54V0H34V5.11h0l1.47-2.66H36.7L35.24,4.93,37,8.54H35.66l-1.1-2.63L34,6.83V8.54Z"/></g></svg>
|
After Width: | Height: | Size: 4.5 KiB |
|
@ -5968,7 +5968,7 @@ definitions:
|
||||||
x-nullable: true
|
x-nullable: true
|
||||||
recovery_flow:
|
recovery_flow:
|
||||||
title: Recovery flow
|
title: Recovery flow
|
||||||
description: Optional enrollment flow, which is linked at the bottom of the
|
description: Optional recovery flow, which is linked at the bottom of the
|
||||||
page.
|
page.
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
|
|