Compare commits
70 Commits
version/20
...
version/20
Author | SHA1 | Date | |
---|---|---|---|
e6b515e3f7 | |||
36eaecfdec | |||
3973efae19 | |||
d8492e0df5 | |||
b64da0dd28 | |||
c3ae3e02f3 | |||
7c6a96394b | |||
0fe43f8319 | |||
7e32723748 | |||
577aa7ba79 | |||
b752540800 | |||
64c8ca9b5d | |||
5552e0ffa7 | |||
e7b7bfddd6 | |||
28f970c795 | |||
d1dbdfa9fe | |||
c4f4e3eac7 | |||
f21ebf5488 | |||
5615613ed1 | |||
669329e49c | |||
0587ab26e8 | |||
3c9cc9d421 | |||
1972464a20 | |||
3041a30193 | |||
1e28a1e311 | |||
5a1b912b76 | |||
464c27ef17 | |||
a745022f06 | |||
0b34f70205 | |||
a4b051fcc1 | |||
5ff3e9b418 | |||
8ae7403abc | |||
f6e1bfdfc8 | |||
aca3a5c458 | |||
d16c24fd53 | |||
6a8be0dc71 | |||
81b9b37e5e | |||
22b01962fb | |||
86cc99be35 | |||
416f917c4a | |||
f77bece790 | |||
a8dd846437 | |||
4c50769040 | |||
34189fcc06 | |||
fb5c8f3d7f | |||
049a55a761 | |||
4cd53f3d11 | |||
0d0dcf8de0 | |||
8cd1223081 | |||
1b4654bb1d | |||
0a3fade1fd | |||
ff64814f40 | |||
cbeb6e58ac | |||
285a9b8b1d | |||
66bfa6879d | |||
c05240afbf | |||
7370dd5f3f | |||
477c8b099e | |||
2c761da883 | |||
75070232b1 | |||
690b35e1a3 | |||
bd67f2362f | |||
896e5adce2 | |||
7f25b6311d | |||
253f345fc4 | |||
a3abbcec6a | |||
70e000d327 | |||
a7467e6740 | |||
b3da94bbb8 | |||
e62f5a75e4 |
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 2021.7.1-rc1
|
||||
current_version = 2021.7.1
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*)
|
||||
|
19
.github/pull_request_template.md
vendored
Normal file
19
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<!--
|
||||
👋 Hello there! Welcome.
|
||||
|
||||
Please check the [Contributing guidelines](https://github.com/goauthentik/authentik/blob/master/CONTRIBUTING.md#how-can-i-contribute).
|
||||
-->
|
||||
|
||||
# Details
|
||||
* **Does this resolve an issue?**
|
||||
Resolves #
|
||||
|
||||
## Changes
|
||||
### New Features
|
||||
* Adds feature which does x, y, and z.
|
||||
|
||||
### Breaking Changes
|
||||
* Adds breaking change which causes \<issue\>.
|
||||
|
||||
## Additional
|
||||
Any further notes or comments you want to make.
|
21
.github/workflows/release.yml
vendored
21
.github/workflows/release.yml
vendored
@ -33,14 +33,14 @@ jobs:
|
||||
with:
|
||||
push: ${{ github.event_name == 'release' }}
|
||||
tags: |
|
||||
beryju/authentik:2021.7.1-rc1,
|
||||
beryju/authentik:2021.7.1,
|
||||
beryju/authentik:latest,
|
||||
ghcr.io/goauthentik/server:2021.7.1-rc1,
|
||||
ghcr.io/goauthentik/server:2021.7.1,
|
||||
ghcr.io/goauthentik/server:latest
|
||||
platforms: linux/amd64,linux/arm64
|
||||
context: .
|
||||
- name: Building Docker Image (stable)
|
||||
if: ${{ github.event_name == 'release' && !contains('2021.7.1-rc1', 'rc') }}
|
||||
if: ${{ github.event_name == 'release' && !contains('2021.7.1', 'rc') }}
|
||||
run: |
|
||||
docker pull beryju/authentik:latest
|
||||
docker tag beryju/authentik:latest beryju/authentik:stable
|
||||
@ -75,14 +75,14 @@ jobs:
|
||||
with:
|
||||
push: ${{ github.event_name == 'release' }}
|
||||
tags: |
|
||||
beryju/authentik-proxy:2021.7.1-rc1,
|
||||
beryju/authentik-proxy:2021.7.1,
|
||||
beryju/authentik-proxy:latest,
|
||||
ghcr.io/goauthentik/proxy:2021.7.1-rc1,
|
||||
ghcr.io/goauthentik/proxy:2021.7.1,
|
||||
ghcr.io/goauthentik/proxy:latest
|
||||
file: proxy.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
- name: Building Docker Image (stable)
|
||||
if: ${{ github.event_name == 'release' && !contains('2021.7.1-rc1', 'rc') }}
|
||||
if: ${{ github.event_name == 'release' && !contains('2021.7.1', 'rc') }}
|
||||
run: |
|
||||
docker pull beryju/authentik-proxy:latest
|
||||
docker tag beryju/authentik-proxy:latest beryju/authentik-proxy:stable
|
||||
@ -117,14 +117,14 @@ jobs:
|
||||
with:
|
||||
push: ${{ github.event_name == 'release' }}
|
||||
tags: |
|
||||
beryju/authentik-ldap:2021.7.1-rc1,
|
||||
beryju/authentik-ldap:2021.7.1,
|
||||
beryju/authentik-ldap:latest,
|
||||
ghcr.io/goauthentik/ldap:2021.7.1-rc1,
|
||||
ghcr.io/goauthentik/ldap:2021.7.1,
|
||||
ghcr.io/goauthentik/ldap:latest
|
||||
file: ldap.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
- name: Building Docker Image (stable)
|
||||
if: ${{ github.event_name == 'release' && !contains('2021.7.1-rc1', 'rc') }}
|
||||
if: ${{ github.event_name == 'release' && !contains('2021.7.1', 'rc') }}
|
||||
run: |
|
||||
docker pull beryju/authentik-ldap:latest
|
||||
docker tag beryju/authentik-ldap:latest beryju/authentik-ldap:stable
|
||||
@ -176,6 +176,7 @@ jobs:
|
||||
SENTRY_PROJECT: authentik
|
||||
SENTRY_URL: https://sentry.beryju.org
|
||||
with:
|
||||
version: authentik@2021.7.1-rc1
|
||||
version: authentik@2021.7.1
|
||||
environment: beryjuorg-prod
|
||||
sourcemaps: './web/dist'
|
||||
url_prefix: /static/dist
|
||||
|
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
hello@beryju.org.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
180
CONTRIBUTING.md
Normal file
180
CONTRIBUTING.md
Normal file
@ -0,0 +1,180 @@
|
||||
# Contributing to authentik
|
||||
|
||||
:+1::tada: Thanks for taking the time to contribute! :tada::+1:
|
||||
|
||||
The following is a set of guidelines for contributing to authentik and its components, which are hosted in the [goauthentik Organization](https://github.com/goauthentik) on GitHub. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
|
||||
|
||||
#### Table Of Contents
|
||||
|
||||
[Code of Conduct](#code-of-conduct)
|
||||
|
||||
[I don't want to read this whole thing, I just have a question!!!](#i-dont-want-to-read-this-whole-thing-i-just-have-a-question)
|
||||
|
||||
[What should I know before I get started?](#what-should-i-know-before-i-get-started)
|
||||
* [Atom and Packages](#atom-and-packages)
|
||||
* [Atom Design Decisions](#design-decisions)
|
||||
|
||||
[How Can I Contribute?](#how-can-i-contribute)
|
||||
* [Reporting Bugs](#reporting-bugs)
|
||||
* [Suggesting Enhancements](#suggesting-enhancements)
|
||||
* [Your First Code Contribution](#your-first-code-contribution)
|
||||
* [Pull Requests](#pull-requests)
|
||||
|
||||
[Styleguides](#styleguides)
|
||||
* [Git Commit Messages](#git-commit-messages)
|
||||
* [JavaScript Styleguide](#javascript-styleguide)
|
||||
* [CoffeeScript Styleguide](#coffeescript-styleguide)
|
||||
* [Specs Styleguide](#specs-styleguide)
|
||||
* [Documentation Styleguide](#documentation-styleguide)
|
||||
|
||||
[Additional Notes](#additional-notes)
|
||||
* [Issue and Pull Request Labels](#issue-and-pull-request-labels)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Basically, don't be a dickhead. This is an open-source non-profit project, that is made in the free time of Volunteers. If there's something you dislike or think can be done better, tell us! We'd love to hear any suggestions for improvement.
|
||||
|
||||
## I don't want to read this whole thing I just have a question!!!
|
||||
|
||||
Either [create a question on GitHub](https://github.com/goauthentik/authentik/issues/new?assignees=&labels=question&template=question.md&title=) or join [the Discord server](https://discord.gg/jg33eMhnj6)
|
||||
|
||||
## What should I know before I get started?
|
||||
|
||||
### The components
|
||||
|
||||
authentik consists of a few larger components:
|
||||
|
||||
- *authentik* the actual application server, is described below.
|
||||
- *outpost-proxy* is a Go application based on a forked version of oauth2_proxy, which does identity-aware reverse proxying.
|
||||
- *outpost-ldap* is a Go LDAP server that uses the *authentik* application server as its backend
|
||||
- *web* is the web frontend, both for administrating and using authentik. It is written in TypeScript using lit-html and the PatternFly CSS Library.
|
||||
- *website* is the Website/documentation, which uses docusaurus.
|
||||
|
||||
### authentik's structure
|
||||
|
||||
authentik is at it's very core a Django project. It consists of many individual django applications. These applications are intended to separate concerns, and they may share code between each other.
|
||||
|
||||
These are the current packages:
|
||||
<a id="authentik-packages"/>
|
||||
|
||||
```
|
||||
authentik
|
||||
├── admin - Administrative tasks and APIs, no models (Version updates, Metrics, system tasks)
|
||||
├── api - General API Configuration (Routes, Schema and general API utilities)
|
||||
├── core - Core authentik functionality, central routes, core Models
|
||||
├── crypto - Cryptography, currently used to generate and hold Certificates and Private Keys
|
||||
├── events - Event Log, middleware and signals to generate signals
|
||||
├── flows - Flows, the FlowPlanner and the FlowExecutor, used for all flows for authentication, authorization, etc
|
||||
├── lib - Generic library of functions, few dependencies on other packages.
|
||||
├── managed - Handle managed models and their state.
|
||||
├── outposts - Configure and deploy outposts on kubernetes and docker.
|
||||
├── policies - General PolicyEngine
|
||||
│ ├── dummy - A Dummy policy used for testing
|
||||
│ ├── event_matcher - Match events based on different criteria
|
||||
│ ├── expiry - Check when a user's password was last set
|
||||
│ ├── expression - Execute any arbitrary python code
|
||||
│ ├── hibp - Check a password against HaveIBeenPwned
|
||||
│ ├── password - Check a password against several rules
|
||||
│ └── reputation - Check the user's/client's reputation
|
||||
├── providers
|
||||
│ ├── ldap - Provide LDAP access to authentik users/groups using an outpost
|
||||
│ ├── oauth2 - OIDC-compliant OAuth2 provider
|
||||
│ ├── proxy - Provides an identity-aware proxy using an outpost
|
||||
│ └── saml - SAML2 Provider
|
||||
├── recovery - Generate keys to use in case you lock yourself out
|
||||
├── root - Root django application, contains global settings and routes
|
||||
├── sources
|
||||
│ ├── ldap - Sync LDAP users from OpenLDAP or Active Directory into authentik
|
||||
│ ├── oauth - OAuth1 and OAuth2 Source
|
||||
│ ├── plex - Plex source
|
||||
│ └── saml - SAML2 Source
|
||||
├── stages
|
||||
│ ├── authenticator_duo - Configure a DUO authenticator
|
||||
│ ├── authenticator_static - Configure TOTP backup keys
|
||||
│ ├── authenticator_totp - Configure a TOTP authenticator
|
||||
│ ├── authenticator_validate - Validate any authenticator
|
||||
│ ├── authenticator_webauthn - Configure a WebAuthn authenticator
|
||||
│ ├── captcha - Make the user pass a captcha
|
||||
│ ├── consent - Let the user decide if they want to consent to an action
|
||||
│ ├── deny - Static deny, can be used with policies
|
||||
│ ├── dummy - Dummy stage to test
|
||||
│ ├── email - Send the user an email and block execution until they click the link
|
||||
│ ├── identification - Identify a user with any combination of fields
|
||||
│ ├── invitation - Invitation system to limit flows to certain users
|
||||
│ ├── password - Password authentication
|
||||
│ ├── prompt - Arbitrary prompts
|
||||
│ ├── user_delete - Delete the currently pending user
|
||||
│ ├── user_login - Login the currently pending user
|
||||
│ ├── user_logout - Logout the currently pending user
|
||||
│ └── user_write - Write any currenetly pending data to the user.
|
||||
└── tenants - Soft tennancy, configure defaults and branding per domain
|
||||
```
|
||||
|
||||
This django project is running in gunicorn, which spawns multiple workers and threads. Gunicorn is run from a lightweight Go application which reverse-proxies it, handles static files and will eventually gain more functionality as more code is migrated to go.
|
||||
|
||||
There are also several background tasks which run in Celery, the root celery application is defined in `authentik.root.celery`.
|
||||
|
||||
## How Can I Contribute?
|
||||
|
||||
### Reporting Bugs
|
||||
|
||||
This section guides you through submitting a bug report for authentik. Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports.
|
||||
|
||||
Whenever authentik encounters an error, it will be logged as an Event with the type `system_exception`. This event type has a button to directly open a pre-filled GitHub issue form.
|
||||
|
||||
This form will have the full stack trace of the error that ocurred and shouldn't contain any sensitive data.
|
||||
|
||||
### Suggesting Enhancements
|
||||
|
||||
This section guides you through submitting an enhancement suggestion for authentik, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions.
|
||||
|
||||
When you are creating an enhancement suggestion, please fill in [the template](https://github.com/goauthentik/authentik/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=), including the steps that you imagine you would take if the feature you're requesting existed.
|
||||
|
||||
### Your First Code Contribution
|
||||
|
||||
#### Local development
|
||||
|
||||
authentik can be run locally, all though depending on which part you want to work on, different pre-requisites are required.
|
||||
|
||||
This is documented in the [developer docs](https://goauthentik.io/developer-docs/)
|
||||
|
||||
### Pull Requests
|
||||
|
||||
The process described here has several goals:
|
||||
|
||||
- Maintain authentik's quality
|
||||
- Fix problems that are important to users
|
||||
- Engage the community in working toward the best possible authentik
|
||||
- Enable a sustainable system for authentik's maintainers to review contributions
|
||||
|
||||
Please follow these steps to have your contribution considered by the maintainers:
|
||||
|
||||
1. Follow the [styleguides](#styleguides)
|
||||
2. After you submit your pull request, verify that all [status checks](https://help.github.com/articles/about-status-checks/) are passing <details><summary>What if the status checks are failing?</summary>If a status check is failing, and you believe that the failure is unrelated to your change, please leave a comment on the pull request explaining why you believe the failure is unrelated. A maintainer will re-run the status check for you. If we conclude that the failure was a false positive, then we will open an issue to track that problem with our status check suite.</details>
|
||||
3. Ensure your Code has tests. While it is not always possible to test every single case, the majority of the code should be tested.
|
||||
|
||||
While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted.
|
||||
|
||||
## Styleguides
|
||||
|
||||
### Git Commit Messages
|
||||
|
||||
* Use the format of `<package>: <verb> <description>`
|
||||
- See [here](#authentik-packages) for `package`
|
||||
- Example: `providers/saml2: fix parsing of requests`
|
||||
* Reference issues and pull requests liberally after the first line
|
||||
|
||||
### Python Styleguide
|
||||
|
||||
All Python code is linted with [black](https://black.readthedocs.io/en/stable/), [PyLint](https://www.pylint.org/) and [isort](https://pycqa.github.io/isort/).
|
||||
|
||||
authentik runs on Python 3.9 at the time of writing this.
|
||||
|
||||
* Use native type-annotations wherever possible.
|
||||
* Add meaningful docstrings when possible.
|
||||
* Ensure any database migrations work properly from the last stable version (this is checked via CI)
|
||||
* If your code changes central functions, make sure nothing else is broken.
|
||||
|
||||
### Documentation Styleguide
|
||||
|
||||
* Use [MDX](https://mdxjs.com/) whenever appropriate.
|
66
Pipfile.lock
generated
66
Pipfile.lock
generated
@ -122,19 +122,19 @@
|
||||
},
|
||||
"boto3": {
|
||||
"hashes": [
|
||||
"sha256:13e60f88d13161df951d6e52bd483cdbe1a36a31f818746289d8ba0879465710",
|
||||
"sha256:3be2f259b279d69495433e3288db3670817fdb1813cfde92abf867bba3ad8148"
|
||||
"sha256:a012570d3535ec6c4db97e60ef51c2f39f38246429e1455cecc26c633ed81c10",
|
||||
"sha256:c7f45b0417395d3020c98cdc10f942939883018210e29dbfe6fbfc0a74e503ec"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.18.3"
|
||||
"version": "==1.18.7"
|
||||
},
|
||||
"botocore": {
|
||||
"hashes": [
|
||||
"sha256:0b6f378c9efbc72eee61aba1e16cab90bde53a37bd2d861f6435552fd7030adf",
|
||||
"sha256:285ab9459cdd49d4a9322692c6e13772b97af723a03c0eed519b589446491a5b"
|
||||
"sha256:34c8b151a25616ed7791218f6d7780c3a97725fe3ceeaa28085b345a8513af6e",
|
||||
"sha256:dcf399d21170bb899e00d2a693bddcc79e61471fbfead8500a65578700a3190a"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==1.21.3"
|
||||
"version": "==1.21.7"
|
||||
},
|
||||
"cachetools": {
|
||||
"hashes": [
|
||||
@ -146,22 +146,22 @@
|
||||
},
|
||||
"cbor2": {
|
||||
"hashes": [
|
||||
"sha256:059363ae716c60f6ba29aa61b3d9c57896189c351c4119095f0542aec169e4dc",
|
||||
"sha256:0b80a4a4fca830af3d3cf36b725c31f0a98106e9c2b02004ab73b0ec7f139446",
|
||||
"sha256:0d22b47fb24b384200277fcfb0582c3a3551c413ad51f3bd3ee334caaf79a483",
|
||||
"sha256:3c586a6e328ba5020802346f5e0304f81b982dcafeb51ee4109c9be9cccbc4a0",
|
||||
"sha256:4dd142764607b1a8b5e3e3b474d2b84099e9cbb323596a15ee8db0d78901d95f",
|
||||
"sha256:6f8a7911c2307ee8f8d4940bdcfb8bd21608f14203a83b651fcd7868bce377a5",
|
||||
"sha256:7ecc4e9c548282a5d296d4535244efa69c7f67cda959f28e14929cf1d6af8a97",
|
||||
"sha256:8bc9f5054650d05e6d3e90f6490dcd6ef6c01ad9c1568958a48dde2702824cb1",
|
||||
"sha256:98410520482796a547af2d5ffe11a8a2dc3b9f2124834fa7c12db8264935ed61",
|
||||
"sha256:a7926f7244b08c413f1a4fa71a81aa256771c75bdf1a4fd77308547a2d63dd48",
|
||||
"sha256:ae31d3b5966807fdff6c9e6f894b0aa10474295d9ff8467a8b978a569c8fec47",
|
||||
"sha256:ce6219986385778b1ab7f9b542f160bb4d3558f52975e914a27b774e47016fb7",
|
||||
"sha256:d562b2773e14ee1d65ea5b85351a83a64d4f3fd011bc2b4c70a6e813e78203ce"
|
||||
"sha256:0144ba1f44e4e36f7a8e8408eea72e1af6fc3ee42a704dacd4446307024e5231",
|
||||
"sha256:15102b45dd8b1879b8743159af4538cbf4b3240fe3ebc4e747f6842cd7775888",
|
||||
"sha256:40aa7c9dc9f69c38a2f9954e0adec266b04c55ed3188dc7a0213a92a2054220e",
|
||||
"sha256:4b62aa7a95960d1c382e858c2c4cb24375cde3cae137d11875bb9a4667731011",
|
||||
"sha256:6288b22cd3c0c842db2a4896473512fe83d24fa8ef4bc592d970635a2bb42e0e",
|
||||
"sha256:81676dc7802029299dc168a1240cf1058c1fe5303fbc64598fe14bdb1f8bc076",
|
||||
"sha256:8684c6ffbd35258cb9790ef2722559f585fb971288d6f55ee5efd9ba75dcc81b",
|
||||
"sha256:8ce511337cbac10ccb97093649d6597aacb648ce3198e6afe8b4931fd1cabc61",
|
||||
"sha256:986a8a9a4d3598008ece7241b746261118ef8d7c0efe7e6e9ce8b275f0421646",
|
||||
"sha256:a8bf432f6cb595f50aeb8fed2a4aa3b3f7caa7f135fb57e4378eaa39242feac9",
|
||||
"sha256:ba5e8065ca901ebec7ae390a183f3c13560454b6bd7dd81bf72c320e252b6461",
|
||||
"sha256:d66350d1323460e1e9dcb2f9caa591b60833623f909173b840a0891a245cad83",
|
||||
"sha256:e921d445575fbbe62ae68dc8ff3c6e05b341077fd24c6310c917b96fabe5e64a"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==5.4.0"
|
||||
"version": "==5.4.1"
|
||||
},
|
||||
"celery": {
|
||||
"hashes": [
|
||||
@ -446,11 +446,11 @@
|
||||
},
|
||||
"drf-spectacular": {
|
||||
"hashes": [
|
||||
"sha256:6ffbfde7d96a4a2febd19182cc405217e1e86a50280fc739402291c93d1a32b7",
|
||||
"sha256:77593024bb899f69227abedcf87def7851a11c9978f781aa4b385a10f67a38b7"
|
||||
"sha256:f080128c42183fcaed6b9e8e5afcd2e5cd68426b1f80bfc85938f25e62db7fe5",
|
||||
"sha256:fb19aa69fcfcd37b0c9dfb9989c0671e1bb47af332ca2171378c7f840263788c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.17.2"
|
||||
"version": "==0.17.3"
|
||||
},
|
||||
"duo-client": {
|
||||
"hashes": [
|
||||
@ -995,10 +995,10 @@
|
||||
},
|
||||
"python-dotenv": {
|
||||
"hashes": [
|
||||
"sha256:dd8fe852847f4fbfadabf6183ddd4c824a9651f02d51714fa075c95561959c7d",
|
||||
"sha256:effaac3c1e58d89b3ccb4d04a40dc7ad6e0275fda25fd75ae9d323e2465e202d"
|
||||
"sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1",
|
||||
"sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172"
|
||||
],
|
||||
"version": "==0.18.0"
|
||||
"version": "==0.19.0"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
@ -1436,11 +1436,11 @@
|
||||
},
|
||||
"astroid": {
|
||||
"hashes": [
|
||||
"sha256:6021561b2e87ed6b3c93c2682ac50079c65ab08f1e4e0277ba38f97e0e492185",
|
||||
"sha256:a670dd7af3fe603f51aa7117462588b7c3bdcd58007edfaee752bf82eceecd28"
|
||||
"sha256:7b963d1c590d490f60d2973e57437115978d3a2529843f160b5003b721e1e925",
|
||||
"sha256:83e494b02d75d07d4e347b27c066fd791c0c74fc96c613d1ea3de0c82c48168f"
|
||||
],
|
||||
"markers": "python_version ~= '3.6'",
|
||||
"version": "==2.6.4"
|
||||
"version": "==2.6.5"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
@ -1598,7 +1598,7 @@
|
||||
"sha256:eed17b53c3e7912425579853d078a0832820f023191561fcee9d7cae424e0813",
|
||||
"sha256:f65ce5bd4cbc6abdfbe29afc2f0245538ab358c14590912df638033f157d555e"
|
||||
],
|
||||
"markers": "python_version < '4.0' and python_full_version >= '3.6.1'",
|
||||
"markers": "python_version < '4' and python_full_version >= '3.6.1'",
|
||||
"version": "==5.9.2"
|
||||
},
|
||||
"lazy-object-proxy": {
|
||||
@ -1684,11 +1684,11 @@
|
||||
},
|
||||
"pylint": {
|
||||
"hashes": [
|
||||
"sha256:2a971129fb2d594068913a7e531d4b6d2785b2a68c6857e2baa40d3214da30f4",
|
||||
"sha256:a622c4c4c79dc8fe5e784efccacec3afe9d5e5ffab5fda2264fb5afa7c9b5797"
|
||||
"sha256:1f333dc72ef7f5ea166b3230936ebcfb1f3b722e76c980cb9fe6b9f95e8d3172",
|
||||
"sha256:748f81e5776d6273a6619506e08f1b48ff9bcb8198366a56821cf11aac14fc87"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.9.4"
|
||||
"version": "==2.9.5"
|
||||
},
|
||||
"pylint-django": {
|
||||
"hashes": [
|
||||
|
@ -2,10 +2,13 @@
|
||||
|
||||
## Supported Versions
|
||||
|
||||
(.x being the latest patch release for each version)
|
||||
|
||||
| Version | Supported |
|
||||
| ---------- | ------------------ |
|
||||
| 2021.4.x | :white_check_mark: |
|
||||
| 2021.5.x | :white_check_mark: |
|
||||
| 2021.6.x | :white_check_mark: |
|
||||
| 2021.7.x | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
"""authentik"""
|
||||
__version__ = "2021.7.1-rc1"
|
||||
__version__ = "2021.7.1"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
@ -114,23 +114,23 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
||||
},
|
||||
)
|
||||
@action(detail=True, methods=["GET"])
|
||||
# pylint: disable=unused-argument
|
||||
def check_access(self, request: Request, slug: str) -> Response:
|
||||
"""Check access to a single application by slug"""
|
||||
# Don't use self.get_object as that checks for view_application permission
|
||||
# which the user might not have, even if they have access
|
||||
application = get_object_or_404(Application, slug=slug)
|
||||
# If the current user is superuser, they can set `for_user`
|
||||
for_user = self.request.user
|
||||
if self.request.user.is_superuser and "for_user" in request.data:
|
||||
for_user = get_object_or_404(User, pk=request.data.get("for_user"))
|
||||
engine = PolicyEngine(application, for_user, self.request)
|
||||
for_user = request.user
|
||||
if request.user.is_superuser and "for_user" in request.query_params:
|
||||
for_user = get_object_or_404(User, pk=request.query_params.get("for_user"))
|
||||
engine = PolicyEngine(application, for_user, request)
|
||||
engine.use_cache = False
|
||||
engine.build()
|
||||
result = engine.result
|
||||
response = PolicyTestResultSerializer(PolicyResult(False))
|
||||
if result.passing:
|
||||
response = PolicyTestResultSerializer(PolicyResult(True))
|
||||
if self.request.user.is_superuser:
|
||||
if request.user.is_superuser:
|
||||
response = PolicyTestResultSerializer(result)
|
||||
return Response(response.data)
|
||||
|
||||
@ -145,18 +145,20 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
|
||||
)
|
||||
def list(self, request: Request) -> Response:
|
||||
"""Custom list method that checks Policy based access instead of guardian"""
|
||||
self.request.session.pop(USER_LOGIN_AUTHENTICATED, None)
|
||||
queryset = self._filter_queryset_for_list(self.get_queryset())
|
||||
self.paginate_queryset(queryset)
|
||||
|
||||
should_cache = request.GET.get("search", "") == ""
|
||||
|
||||
superuser_full_list = (
|
||||
str(request.GET.get("superuser_full_list", "false")).lower() == "true"
|
||||
)
|
||||
if superuser_full_list and request.user.is_superuser:
|
||||
serializer = self.get_serializer(queryset, many=True)
|
||||
return self.get_paginated_response(serializer.data)
|
||||
return super().list(request)
|
||||
|
||||
# To prevent the user from having to double login when prompt is set to login
|
||||
# and the user has just signed it. This session variable is set in the UserLoginStage
|
||||
# and is (quite hackily) removed from the session in applications's API's List method
|
||||
self.request.session.pop(USER_LOGIN_AUTHENTICATED, None)
|
||||
queryset = self._filter_queryset_for_list(self.get_queryset())
|
||||
self.paginate_queryset(queryset)
|
||||
|
||||
allowed_applications = []
|
||||
if not should_cache:
|
||||
|
@ -1,5 +1,7 @@
|
||||
"""Groups API Viewset"""
|
||||
from django.db.models.query import QuerySet
|
||||
from django_filters.filters import ModelMultipleChoiceFilter
|
||||
from django_filters.filterset import FilterSet
|
||||
from rest_framework.fields import BooleanField, CharField, JSONField
|
||||
from rest_framework.serializers import ListSerializer, ModelSerializer
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
@ -57,13 +59,32 @@ class GroupSerializer(ModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class GroupFilter(FilterSet):
|
||||
"""Filter for groups"""
|
||||
|
||||
members_by_username = ModelMultipleChoiceFilter(
|
||||
field_name="users__username",
|
||||
to_field_name="username",
|
||||
queryset=User.objects.all(),
|
||||
)
|
||||
members_by_pk = ModelMultipleChoiceFilter(
|
||||
field_name="users",
|
||||
queryset=User.objects.all(),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Group
|
||||
fields = ["name", "is_superuser", "members_by_pk", "members_by_username"]
|
||||
|
||||
|
||||
class GroupViewSet(UsedByMixin, ModelViewSet):
|
||||
"""Group Viewset"""
|
||||
|
||||
queryset = Group.objects.all()
|
||||
serializer_class = GroupSerializer
|
||||
search_fields = ["name", "is_superuser"]
|
||||
filterset_fields = ["name", "is_superuser"]
|
||||
filterset_class = GroupFilter
|
||||
ordering = ["name"]
|
||||
|
||||
def _filter_queryset_for_list(self, queryset: QuerySet) -> QuerySet:
|
||||
|
@ -128,7 +128,14 @@ class UsersFilter(FilterSet):
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ["username", "name", "is_active", "is_superuser", "attributes"]
|
||||
fields = [
|
||||
"username",
|
||||
"email",
|
||||
"name",
|
||||
"is_active",
|
||||
"is_superuser",
|
||||
"attributes",
|
||||
]
|
||||
|
||||
|
||||
class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
@ -136,7 +143,7 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
|
||||
queryset = User.objects.none()
|
||||
serializer_class = UserSerializer
|
||||
search_fields = ["username", "name", "is_active"]
|
||||
search_fields = ["username", "name", "is_active", "email"]
|
||||
filterset_class = UsersFilter
|
||||
|
||||
def get_queryset(self): # pragma: no cover
|
||||
@ -206,6 +213,6 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||
return queryset
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
if self.request.user.has_perm("authentik_core.view_group"):
|
||||
if self.request.user.has_perm("authentik_core.view_user"):
|
||||
return self._filter_queryset_for_list(queryset)
|
||||
return super().filter_queryset(queryset)
|
||||
|
@ -491,8 +491,8 @@ class PropertyMapping(SerializerModel, ManagedModel):
|
||||
evaluator.set_context(user, request, self, **kwargs)
|
||||
try:
|
||||
return evaluator.evaluate(self.expression)
|
||||
except (ValueError, SyntaxError) as exc:
|
||||
raise PropertyMappingExpressionException from exc
|
||||
except Exception as exc:
|
||||
raise PropertyMappingExpressionException(str(exc)) from exc
|
||||
|
||||
def __str__(self):
|
||||
return f"Property Mapping {self.name}"
|
||||
|
@ -141,11 +141,11 @@ class SourceFlowManager:
|
||||
self._logger.info("denying source because user exists", user=user)
|
||||
return Action.DENY, None
|
||||
# Should never get here as default enroll case is returned above.
|
||||
return Action.DENY, None
|
||||
return Action.DENY, None # pragma: no cover
|
||||
|
||||
def update_connection(
|
||||
self, connection: UserSourceConnection, **kwargs
|
||||
) -> UserSourceConnection:
|
||||
) -> UserSourceConnection: # pragma: no cover
|
||||
"""Optionally make changes to the connection after it is looked up/created."""
|
||||
return connection
|
||||
|
||||
@ -178,7 +178,7 @@ class SourceFlowManager:
|
||||
% {"source": self.source.name}
|
||||
),
|
||||
)
|
||||
return redirect("/")
|
||||
return redirect(reverse("authentik_core:root-redirect"))
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def get_stages_to_append(self, flow: Flow) -> list[Stage]:
|
||||
|
@ -31,7 +31,7 @@ class TestPropertyMappings(TestCase):
|
||||
"""Test expression error"""
|
||||
expr = "return aaa"
|
||||
mapping = PropertyMapping.objects.create(name="test", expression=expr)
|
||||
with self.assertRaises(NameError):
|
||||
with self.assertRaises(PropertyMappingExpressionException):
|
||||
mapping.evaluate(None, None)
|
||||
events = Event.objects.filter(
|
||||
action=EventAction.PROPERTY_MAPPING_EXCEPTION, context__expression=expr
|
||||
@ -44,7 +44,7 @@ class TestPropertyMappings(TestCase):
|
||||
expr = "return aaa"
|
||||
request = self.factory.get("/")
|
||||
mapping = PropertyMapping.objects.create(name="test", expression=expr)
|
||||
with self.assertRaises(NameError):
|
||||
with self.assertRaises(PropertyMappingExpressionException):
|
||||
mapping.evaluate(get_anonymous_user(), request)
|
||||
events = Event.objects.filter(
|
||||
action=EventAction.PROPERTY_MAPPING_EXCEPTION, context__expression=expr
|
||||
|
160
authentik/core/tests/test_source_flow_manager.py
Normal file
160
authentik/core/tests/test_source_flow_manager.py
Normal file
@ -0,0 +1,160 @@
|
||||
"""Test Source flow_manager"""
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.contrib.messages.middleware import MessageMiddleware
|
||||
from django.contrib.sessions.middleware import SessionMiddleware
|
||||
from django.http.request import HttpRequest
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from guardian.utils import get_anonymous_user
|
||||
|
||||
from authentik.core.models import SourceUserMatchingModes, User
|
||||
from authentik.core.sources.flow_manager import Action
|
||||
from authentik.flows.tests.test_planner import dummy_get_response
|
||||
from authentik.providers.oauth2.generators import generate_client_id
|
||||
from authentik.sources.oauth.models import OAuthSource, UserOAuthSourceConnection
|
||||
from authentik.sources.oauth.views.callback import OAuthSourceFlowManager
|
||||
|
||||
|
||||
class TestSourceFlowManager(TestCase):
|
||||
"""Test Source flow_manager"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
self.source = OAuthSource.objects.create(name="test")
|
||||
self.factory = RequestFactory()
|
||||
self.identifier = generate_client_id()
|
||||
|
||||
def get_request(self, user: User) -> HttpRequest:
|
||||
"""Helper to create a get request with session and message middleware"""
|
||||
request = self.factory.get("/")
|
||||
request.user = user
|
||||
middleware = SessionMiddleware(dummy_get_response)
|
||||
middleware.process_request(request)
|
||||
request.session.save()
|
||||
middleware = MessageMiddleware(dummy_get_response)
|
||||
middleware.process_request(request)
|
||||
request.session.save()
|
||||
return request
|
||||
|
||||
def test_unauthenticated_enroll(self):
|
||||
"""Test un-authenticated user enrolling"""
|
||||
flow_manager = OAuthSourceFlowManager(
|
||||
self.source, self.get_request(AnonymousUser()), self.identifier, {}
|
||||
)
|
||||
action, _ = flow_manager.get_action()
|
||||
self.assertEqual(action, Action.ENROLL)
|
||||
flow_manager.get_flow()
|
||||
|
||||
def test_unauthenticated_auth(self):
|
||||
"""Test un-authenticated user authenticating"""
|
||||
UserOAuthSourceConnection.objects.create(
|
||||
user=get_anonymous_user(), source=self.source, identifier=self.identifier
|
||||
)
|
||||
|
||||
flow_manager = OAuthSourceFlowManager(
|
||||
self.source, self.get_request(AnonymousUser()), self.identifier, {}
|
||||
)
|
||||
action, _ = flow_manager.get_action()
|
||||
self.assertEqual(action, Action.AUTH)
|
||||
flow_manager.get_flow()
|
||||
|
||||
def test_authenticated_link(self):
|
||||
"""Test authenticated user linking"""
|
||||
UserOAuthSourceConnection.objects.create(
|
||||
user=get_anonymous_user(), source=self.source, identifier=self.identifier
|
||||
)
|
||||
user = User.objects.create(username="foo", email="foo@bar.baz")
|
||||
flow_manager = OAuthSourceFlowManager(
|
||||
self.source, self.get_request(user), self.identifier, {}
|
||||
)
|
||||
action, _ = flow_manager.get_action()
|
||||
self.assertEqual(action, Action.LINK)
|
||||
flow_manager.get_flow()
|
||||
|
||||
def test_unauthenticated_enroll_email(self):
|
||||
"""Test un-authenticated user enrolling (link on email)"""
|
||||
User.objects.create(username="foo", email="foo@bar.baz")
|
||||
self.source.user_matching_mode = SourceUserMatchingModes.EMAIL_LINK
|
||||
|
||||
# Without email, deny
|
||||
flow_manager = OAuthSourceFlowManager(
|
||||
self.source, self.get_request(AnonymousUser()), self.identifier, {}
|
||||
)
|
||||
action, _ = flow_manager.get_action()
|
||||
self.assertEqual(action, Action.DENY)
|
||||
flow_manager.get_flow()
|
||||
# With email
|
||||
flow_manager = OAuthSourceFlowManager(
|
||||
self.source,
|
||||
self.get_request(AnonymousUser()),
|
||||
self.identifier,
|
||||
{"email": "foo@bar.baz"},
|
||||
)
|
||||
action, _ = flow_manager.get_action()
|
||||
self.assertEqual(action, Action.LINK)
|
||||
flow_manager.get_flow()
|
||||
|
||||
def test_unauthenticated_enroll_username(self):
|
||||
"""Test un-authenticated user enrolling (link on username)"""
|
||||
User.objects.create(username="foo", email="foo@bar.baz")
|
||||
self.source.user_matching_mode = SourceUserMatchingModes.USERNAME_LINK
|
||||
|
||||
# Without username, deny
|
||||
flow_manager = OAuthSourceFlowManager(
|
||||
self.source, self.get_request(AnonymousUser()), self.identifier, {}
|
||||
)
|
||||
action, _ = flow_manager.get_action()
|
||||
self.assertEqual(action, Action.DENY)
|
||||
flow_manager.get_flow()
|
||||
# With username
|
||||
flow_manager = OAuthSourceFlowManager(
|
||||
self.source,
|
||||
self.get_request(AnonymousUser()),
|
||||
self.identifier,
|
||||
{"username": "foo"},
|
||||
)
|
||||
action, _ = flow_manager.get_action()
|
||||
self.assertEqual(action, Action.LINK)
|
||||
flow_manager.get_flow()
|
||||
|
||||
def test_unauthenticated_enroll_username_deny(self):
|
||||
"""Test un-authenticated user enrolling (deny on username)"""
|
||||
User.objects.create(username="foo", email="foo@bar.baz")
|
||||
self.source.user_matching_mode = SourceUserMatchingModes.USERNAME_DENY
|
||||
|
||||
# With non-existent username, enroll
|
||||
flow_manager = OAuthSourceFlowManager(
|
||||
self.source,
|
||||
self.get_request(AnonymousUser()),
|
||||
self.identifier,
|
||||
{
|
||||
"username": "bar",
|
||||
},
|
||||
)
|
||||
action, _ = flow_manager.get_action()
|
||||
self.assertEqual(action, Action.ENROLL)
|
||||
flow_manager.get_flow()
|
||||
# With username
|
||||
flow_manager = OAuthSourceFlowManager(
|
||||
self.source,
|
||||
self.get_request(AnonymousUser()),
|
||||
self.identifier,
|
||||
{"username": "foo"},
|
||||
)
|
||||
action, _ = flow_manager.get_action()
|
||||
self.assertEqual(action, Action.DENY)
|
||||
flow_manager.get_flow()
|
||||
|
||||
def test_unauthenticated_enroll_link_non_existent(self):
|
||||
"""Test un-authenticated user enrolling (link on username), username doesn't exist"""
|
||||
self.source.user_matching_mode = SourceUserMatchingModes.USERNAME_LINK
|
||||
|
||||
flow_manager = OAuthSourceFlowManager(
|
||||
self.source,
|
||||
self.get_request(AnonymousUser()),
|
||||
self.identifier,
|
||||
{"username": "foo"},
|
||||
)
|
||||
action, _ = flow_manager.get_action()
|
||||
self.assertEqual(action, Action.ENROLL)
|
||||
flow_manager.get_flow()
|
@ -24,8 +24,10 @@ from authentik.events.geo import GEOIP_READER
|
||||
from authentik.events.utils import cleanse_dict, get_user, model_to_dict, sanitize_dict
|
||||
from authentik.lib.sentry import SentryIgnoredException
|
||||
from authentik.lib.utils.http import get_client_ip
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.policies.models import PolicyBindingModel
|
||||
from authentik.stages.email.utils import TemplateEmailMessage
|
||||
from authentik.tenants.models import Tenant
|
||||
from authentik.tenants.utils import DEFAULT_TENANT
|
||||
|
||||
LOGGER = get_logger("authentik.events")
|
||||
@ -37,7 +39,8 @@ GAUGE_EVENTS = Gauge(
|
||||
|
||||
|
||||
def default_event_duration():
|
||||
"""Default duration an Event is saved"""
|
||||
"""Default duration an Event is saved.
|
||||
This is used as a fallback when no tenant is available"""
|
||||
return now() + timedelta(days=365)
|
||||
|
||||
|
||||
@ -147,7 +150,12 @@ class Event(ExpiringModel):
|
||||
"method": request.method,
|
||||
}
|
||||
if hasattr(request, "tenant"):
|
||||
self.tenant = sanitize_dict(model_to_dict(request.tenant))
|
||||
tenant: Tenant = request.tenant
|
||||
# Because self.created only gets set on save, we can't use it's value here
|
||||
# hence we set self.created to now and then use it
|
||||
self.created = now()
|
||||
self.expires = self.created + timedelta_from_string(tenant.event_retention)
|
||||
self.tenant = sanitize_dict(model_to_dict(tenant))
|
||||
if hasattr(request, "user"):
|
||||
original_user = None
|
||||
if hasattr(request, "session"):
|
||||
|
67
authentik/lib/tests/test_http.py
Normal file
67
authentik/lib/tests/test_http.py
Normal file
@ -0,0 +1,67 @@
|
||||
"""Test HTTP Helpers"""
|
||||
from django.test import RequestFactory, TestCase
|
||||
|
||||
from authentik.core.models import (
|
||||
USER_ATTRIBUTE_CAN_OVERRIDE_IP,
|
||||
Token,
|
||||
TokenIntents,
|
||||
User,
|
||||
)
|
||||
from authentik.lib.utils.http import (
|
||||
OUTPOST_REMOTE_IP_HEADER,
|
||||
OUTPOST_TOKEN_HEADER,
|
||||
get_client_ip,
|
||||
)
|
||||
|
||||
|
||||
class TestHTTP(TestCase):
|
||||
"""Test HTTP Helpers"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.user = User.objects.get(username="akadmin")
|
||||
self.factory = RequestFactory()
|
||||
|
||||
def test_normal(self):
|
||||
"""Test normal request"""
|
||||
request = self.factory.get("/")
|
||||
self.assertEqual(get_client_ip(request), "127.0.0.1")
|
||||
|
||||
def test_forward_for(self):
|
||||
"""Test x-forwarded-for request"""
|
||||
request = self.factory.get("/", HTTP_X_FORWARDED_FOR="127.0.0.2")
|
||||
self.assertEqual(get_client_ip(request), "127.0.0.2")
|
||||
|
||||
def test_fake_outpost(self):
|
||||
"""Test faked IP which is overridden by an outpost"""
|
||||
token = Token.objects.create(
|
||||
identifier="test", user=self.user, intent=TokenIntents.INTENT_API
|
||||
)
|
||||
# Invalid, non-existant token
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
**{
|
||||
OUTPOST_REMOTE_IP_HEADER: "1.2.3.4",
|
||||
OUTPOST_TOKEN_HEADER: "abc",
|
||||
},
|
||||
)
|
||||
self.assertEqual(get_client_ip(request), "127.0.0.1")
|
||||
# Invalid, user doesn't have permisions
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
**{
|
||||
OUTPOST_REMOTE_IP_HEADER: "1.2.3.4",
|
||||
OUTPOST_TOKEN_HEADER: token.key,
|
||||
},
|
||||
)
|
||||
self.assertEqual(get_client_ip(request), "127.0.0.1")
|
||||
# Valid
|
||||
self.user.attributes[USER_ATTRIBUTE_CAN_OVERRIDE_IP] = True
|
||||
self.user.save()
|
||||
request = self.factory.get(
|
||||
"/",
|
||||
**{
|
||||
OUTPOST_REMOTE_IP_HEADER: "1.2.3.4",
|
||||
OUTPOST_TOKEN_HEADER: token.key,
|
||||
},
|
||||
)
|
||||
self.assertEqual(get_client_ip(request), "1.2.3.4")
|
@ -40,24 +40,30 @@ def _get_outpost_override_ip(request: HttpRequest) -> Optional[str]:
|
||||
or OUTPOST_TOKEN_HEADER not in request.META
|
||||
):
|
||||
return None
|
||||
fake_ip = request.META[OUTPOST_REMOTE_IP_HEADER]
|
||||
tokens = Token.filter_not_expired(
|
||||
key=request.META.get(OUTPOST_TOKEN_HEADER), intent=TokenIntents.INTENT_API
|
||||
)
|
||||
if not tokens.exists():
|
||||
LOGGER.warning("Attempted remote-ip override without token")
|
||||
LOGGER.warning("Attempted remote-ip override without token", fake_ip=fake_ip)
|
||||
return None
|
||||
user = tokens.first().user
|
||||
if user.group_attributes().get(USER_ATTRIBUTE_CAN_OVERRIDE_IP, False):
|
||||
if not user.group_attributes().get(USER_ATTRIBUTE_CAN_OVERRIDE_IP, False):
|
||||
LOGGER.warning(
|
||||
"Remote-IP override: user doesn't have permission",
|
||||
user=user,
|
||||
fake_ip=fake_ip,
|
||||
)
|
||||
return None
|
||||
return request.META[OUTPOST_REMOTE_IP_HEADER]
|
||||
return fake_ip
|
||||
|
||||
|
||||
def get_client_ip(request: Optional[HttpRequest]) -> str:
|
||||
"""Attempt to get the client's IP by checking common HTTP Headers.
|
||||
Returns none if no IP Could be found"""
|
||||
if request:
|
||||
override = _get_outpost_override_ip(request)
|
||||
if override:
|
||||
return override
|
||||
return _get_client_ip_from_meta(request.META)
|
||||
return DEFAULT_IP
|
||||
if not request:
|
||||
return DEFAULT_IP
|
||||
override = _get_outpost_override_ip(request)
|
||||
if override:
|
||||
return override
|
||||
return _get_client_ip_from_meta(request.META)
|
||||
|
@ -5,12 +5,12 @@ from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
ALLOWED_KEYS = (
|
||||
"days",
|
||||
"seconds",
|
||||
"microseconds",
|
||||
"milliseconds",
|
||||
"seconds",
|
||||
"minutes",
|
||||
"hours",
|
||||
"days",
|
||||
"weeks",
|
||||
)
|
||||
|
||||
|
@ -344,12 +344,13 @@ class Outpost(ManagedModel):
|
||||
users = User.objects.filter(username=self.user_identifier)
|
||||
if not users.exists():
|
||||
user: User = User.objects.create(username=self.user_identifier)
|
||||
user.attributes[USER_ATTRIBUTE_SA] = True
|
||||
user.attributes[USER_ATTRIBUTE_CAN_OVERRIDE_IP] = True
|
||||
user.set_unusable_password()
|
||||
user.save()
|
||||
else:
|
||||
user = users.first()
|
||||
user.attributes[USER_ATTRIBUTE_SA] = True
|
||||
user.attributes[USER_ATTRIBUTE_CAN_OVERRIDE_IP] = True
|
||||
user.save()
|
||||
# To ensure the user only has the correct permissions, we delete all of them and re-add
|
||||
# the ones the user needs
|
||||
with transaction.atomic():
|
||||
|
111
authentik/providers/oauth2/tests/test_userinfo.py
Normal file
111
authentik/providers/oauth2/tests/test_userinfo.py
Normal file
@ -0,0 +1,111 @@
|
||||
"""Test userinfo view"""
|
||||
import json
|
||||
from dataclasses import asdict
|
||||
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
from authentik.core.models import Application, User
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.managed.manager import ObjectManager
|
||||
from authentik.providers.oauth2.generators import (
|
||||
generate_client_id,
|
||||
generate_client_secret,
|
||||
)
|
||||
from authentik.providers.oauth2.models import (
|
||||
IDToken,
|
||||
OAuth2Provider,
|
||||
RefreshToken,
|
||||
ScopeMapping,
|
||||
)
|
||||
from authentik.providers.oauth2.tests.utils import OAuthTestCase
|
||||
|
||||
|
||||
class TestUserinfo(OAuthTestCase):
|
||||
"""Test token view"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
ObjectManager().run()
|
||||
self.app = Application.objects.create(name="test", slug="test")
|
||||
self.provider: OAuth2Provider = OAuth2Provider.objects.create(
|
||||
name="test",
|
||||
client_id=generate_client_id(),
|
||||
client_secret=generate_client_secret(),
|
||||
authorization_flow=Flow.objects.first(),
|
||||
redirect_uris="",
|
||||
rsa_key=CertificateKeyPair.objects.first(),
|
||||
)
|
||||
self.provider.property_mappings.set(ScopeMapping.objects.all())
|
||||
# Needs to be assigned to an application for iss to be set
|
||||
self.app.provider = self.provider
|
||||
self.app.save()
|
||||
self.user = User.objects.get(username="akadmin")
|
||||
self.token: RefreshToken = RefreshToken.objects.create(
|
||||
provider=self.provider,
|
||||
user=self.user,
|
||||
access_token=generate_client_id(),
|
||||
refresh_token=generate_client_id(),
|
||||
_scope="openid user profile",
|
||||
_id_token=json.dumps(
|
||||
asdict(
|
||||
IDToken("foo", "bar"),
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
def test_userinfo_normal(self):
|
||||
"""test user info with all normal scopes"""
|
||||
res = self.client.get(
|
||||
reverse("authentik_providers_oauth2:userinfo"),
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.token.access_token}",
|
||||
)
|
||||
self.assertJSONEqual(
|
||||
force_str(res.content),
|
||||
{
|
||||
"name": "authentik Default Admin",
|
||||
"given_name": "authentik Default Admin",
|
||||
"family_name": "",
|
||||
"preferred_username": "akadmin",
|
||||
"nickname": "akadmin",
|
||||
"groups": ["authentik Admins"],
|
||||
"sub": "bar",
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
def test_userinfo_invalid_scope(self):
|
||||
"""test user info with a broken scope"""
|
||||
scope = ScopeMapping.objects.create(
|
||||
name="test", scope_name="openid", expression="q"
|
||||
)
|
||||
self.provider.property_mappings.add(scope)
|
||||
|
||||
res = self.client.get(
|
||||
reverse("authentik_providers_oauth2:userinfo"),
|
||||
HTTP_AUTHORIZATION=f"Bearer {self.token.access_token}",
|
||||
)
|
||||
self.assertJSONEqual(
|
||||
force_str(res.content),
|
||||
{
|
||||
"name": "authentik Default Admin",
|
||||
"given_name": "authentik Default Admin",
|
||||
"family_name": "",
|
||||
"preferred_username": "akadmin",
|
||||
"nickname": "akadmin",
|
||||
"groups": ["authentik Admins"],
|
||||
"sub": "bar",
|
||||
},
|
||||
)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
|
||||
events = Event.objects.filter(
|
||||
action=EventAction.CONFIGURATION_ERROR,
|
||||
)
|
||||
self.assertTrue(events.exists())
|
||||
self.assertEqual(
|
||||
events.first().context["message"],
|
||||
"Failed to evaluate property-mapping: name 'q' is not defined",
|
||||
)
|
@ -126,7 +126,15 @@ class TokenParams:
|
||||
LOGGER.warning("Missing authorization code")
|
||||
raise TokenError("invalid_grant")
|
||||
|
||||
if self.redirect_uri not in self.provider.redirect_uris.split():
|
||||
allowed_redirect_urls = self.provider.redirect_uris.split()
|
||||
if len(allowed_redirect_urls) < 1:
|
||||
LOGGER.warning(
|
||||
"Provider has no allowed redirect_uri set, allowing all.",
|
||||
allow=self.redirect_uri.lower(),
|
||||
)
|
||||
elif self.redirect_uri.lower() not in [
|
||||
x.lower() for x in allowed_redirect_urls
|
||||
]:
|
||||
LOGGER.warning(
|
||||
"Invalid redirect uri",
|
||||
uri=self.redirect_uri,
|
||||
|
@ -7,6 +7,8 @@ from django.http.response import HttpResponseBadRequest
|
||||
from django.views import View
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.exceptions import PropertyMappingExpressionException
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.providers.oauth2.constants import (
|
||||
SCOPE_GITHUB_ORG_READ,
|
||||
SCOPE_GITHUB_USER,
|
||||
@ -63,12 +65,20 @@ class UserInfoView(View):
|
||||
for scope in ScopeMapping.objects.filter(
|
||||
provider=token.provider, scope_name__in=scopes_from_client
|
||||
).order_by("scope_name"):
|
||||
value = scope.evaluate(
|
||||
user=token.user,
|
||||
request=self.request,
|
||||
provider=token.provider,
|
||||
token=token,
|
||||
)
|
||||
value = None
|
||||
try:
|
||||
value = scope.evaluate(
|
||||
user=token.user,
|
||||
request=self.request,
|
||||
provider=token.provider,
|
||||
token=token,
|
||||
)
|
||||
except PropertyMappingExpressionException as exc:
|
||||
Event.new(
|
||||
EventAction.CONFIGURATION_ERROR,
|
||||
message=f"Failed to evaluate property-mapping: {str(exc)}",
|
||||
mapping=scope,
|
||||
).from_http(self.request)
|
||||
if value is None:
|
||||
continue
|
||||
if not isinstance(value, dict):
|
||||
|
@ -90,12 +90,6 @@ class ProxyOutpostConfigSerializer(ModelSerializer):
|
||||
"""Proxy provider serializer for outposts"""
|
||||
|
||||
oidc_configuration = SerializerMethodField()
|
||||
forward_auth_mode = SerializerMethodField()
|
||||
|
||||
def get_forward_auth_mode(self, instance: ProxyProvider) -> bool:
|
||||
"""Legacy field for 2021.5 outposts"""
|
||||
# TODO: remove in 2021.7
|
||||
return instance.mode in [ProxyMode.FORWARD_SINGLE, ProxyMode.FORWARD_DOMAIN]
|
||||
|
||||
class Meta:
|
||||
|
||||
@ -117,8 +111,6 @@ class ProxyOutpostConfigSerializer(ModelSerializer):
|
||||
"basic_auth_user_attribute",
|
||||
"mode",
|
||||
"cookie_domain",
|
||||
# Legacy field, remove in 2021.7
|
||||
"forward_auth_mode",
|
||||
]
|
||||
|
||||
@extend_schema_field(OpenIDConnectConfigurationSerializer)
|
||||
|
@ -113,6 +113,7 @@ class TraefikMiddlewareReconciler(KubernetesObjectReconciler[TraefikMiddleware])
|
||||
authResponseHeaders=[
|
||||
"Set-Cookie",
|
||||
"X-Auth-Username",
|
||||
"X-Auth-Groups",
|
||||
"X-Forwarded-Email",
|
||||
"X-Forwarded-Preferred-Username",
|
||||
"X-Forwarded-User",
|
||||
|
@ -9,6 +9,7 @@ from lxml.etree import Element, SubElement # nosec
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.core.exceptions import PropertyMappingExpressionException
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
|
||||
from authentik.providers.saml.processors.request_parser import AuthNRequest
|
||||
@ -99,7 +100,11 @@ class AssertionProcessor:
|
||||
attribute_statement.append(attribute)
|
||||
|
||||
except PropertyMappingExpressionException as exc:
|
||||
LOGGER.warning(str(exc))
|
||||
Event.new(
|
||||
EventAction.CONFIGURATION_ERROR,
|
||||
message=f"Failed to evaluate property-mapping: {str(exc)}",
|
||||
mapping=mapping,
|
||||
).from_http(self.http_request)
|
||||
continue
|
||||
return attribute_statement
|
||||
|
||||
@ -161,7 +166,11 @@ class AssertionProcessor:
|
||||
name_id.text = value
|
||||
return name_id
|
||||
except PropertyMappingExpressionException as exc:
|
||||
LOGGER.warning(str(exc))
|
||||
Event.new(
|
||||
EventAction.CONFIGURATION_ERROR,
|
||||
message=f"Failed to evaluate property-mapping: {str(exc)}",
|
||||
mapping=self.provider.name_id_mapping,
|
||||
).from_http(self.http_request)
|
||||
return name_id
|
||||
if name_id.attrib["Format"] == SAML_NAME_ID_FORMAT_EMAIL:
|
||||
name_id.text = self.http_request.user.email
|
||||
|
@ -6,9 +6,12 @@ from django.http.request import QueryDict
|
||||
from django.test import RequestFactory, TestCase
|
||||
from guardian.utils import get_anonymous_user
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.crypto.models import CertificateKeyPair
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.flows.tests.test_planner import dummy_get_response
|
||||
from authentik.managed.manager import ObjectManager
|
||||
from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
|
||||
from authentik.providers.saml.processors.assertion import AssertionProcessor
|
||||
from authentik.providers.saml.processors.request_parser import AuthNRequestParser
|
||||
@ -79,6 +82,7 @@ class TestAuthNRequest(TestCase):
|
||||
"""Test AuthN Request generator and parser"""
|
||||
|
||||
def setUp(self):
|
||||
ObjectManager().run()
|
||||
cert = CertificateKeyPair.objects.first()
|
||||
self.provider: SAMLProvider = SAMLProvider.objects.create(
|
||||
authorization_flow=Flow.objects.get(
|
||||
@ -243,3 +247,60 @@ class TestAuthNRequest(TestCase):
|
||||
parsed_request = AuthNRequestParser(provider).parse(POST_REQUEST)
|
||||
self.assertEqual(parsed_request.id, "aws_LDxLGeubpc5lx12gxCgS6uPbix1yd5re")
|
||||
self.assertEqual(parsed_request.name_id_policy, SAML_NAME_ID_FORMAT_EMAIL)
|
||||
|
||||
def test_request_attributes(self):
|
||||
"""Test full SAML Request/Response flow, fully signed"""
|
||||
http_request = self.factory.get("/")
|
||||
http_request.user = User.objects.get(username="akadmin")
|
||||
|
||||
middleware = SessionMiddleware(dummy_get_response)
|
||||
middleware.process_request(http_request)
|
||||
http_request.session.save()
|
||||
|
||||
# First create an AuthNRequest
|
||||
request_proc = RequestProcessor(self.source, http_request, "test_state")
|
||||
request = request_proc.build_auth_n()
|
||||
|
||||
# To get an assertion we need a parsed request (parsed by provider)
|
||||
parsed_request = AuthNRequestParser(self.provider).parse(
|
||||
b64encode(request.encode()).decode(), "test_state"
|
||||
)
|
||||
# Now create a response and convert it to string (provider)
|
||||
response_proc = AssertionProcessor(self.provider, http_request, parsed_request)
|
||||
self.assertIn("akadmin", response_proc.build_response())
|
||||
|
||||
def test_request_attributes_invalid(self):
|
||||
"""Test full SAML Request/Response flow, fully signed"""
|
||||
http_request = self.factory.get("/")
|
||||
http_request.user = User.objects.get(username="akadmin")
|
||||
|
||||
middleware = SessionMiddleware(dummy_get_response)
|
||||
middleware.process_request(http_request)
|
||||
http_request.session.save()
|
||||
|
||||
# First create an AuthNRequest
|
||||
request_proc = RequestProcessor(self.source, http_request, "test_state")
|
||||
request = request_proc.build_auth_n()
|
||||
|
||||
# Create invalid PropertyMapping
|
||||
scope = SAMLPropertyMapping.objects.create(
|
||||
name="test", saml_name="test", expression="q"
|
||||
)
|
||||
self.provider.property_mappings.add(scope)
|
||||
|
||||
# To get an assertion we need a parsed request (parsed by provider)
|
||||
parsed_request = AuthNRequestParser(self.provider).parse(
|
||||
b64encode(request.encode()).decode(), "test_state"
|
||||
)
|
||||
# Now create a response and convert it to string (provider)
|
||||
response_proc = AssertionProcessor(self.provider, http_request, parsed_request)
|
||||
self.assertIn("akadmin", response_proc.build_response())
|
||||
|
||||
events = Event.objects.filter(
|
||||
action=EventAction.CONFIGURATION_ERROR,
|
||||
)
|
||||
self.assertTrue(events.exists())
|
||||
self.assertEqual(
|
||||
events.first().context["message"],
|
||||
"Failed to evaluate property-mapping: name 'q' is not defined",
|
||||
)
|
||||
|
@ -27,6 +27,7 @@ class ChannelsStorage(BaseStorage):
|
||||
uid,
|
||||
{
|
||||
"type": "event.update",
|
||||
"message_type": "message",
|
||||
"level": message.level_tag,
|
||||
"tags": message.tags,
|
||||
"message": message.message,
|
||||
|
@ -5,6 +5,7 @@ from django.db.models.query import QuerySet
|
||||
from structlog.stdlib import BoundLogger, get_logger
|
||||
|
||||
from authentik.core.exceptions import PropertyMappingExpressionException
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.sources.ldap.auth import LDAP_DISTINGUISHED_NAME
|
||||
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
|
||||
|
||||
@ -83,6 +84,11 @@ class BaseLDAPSynchronizer:
|
||||
else:
|
||||
properties[object_field] = self._flatten(value)
|
||||
except PropertyMappingExpressionException as exc:
|
||||
Event.new(
|
||||
EventAction.CONFIGURATION_ERROR,
|
||||
message=f"Failed to evaluate property-mapping: {str(exc)}",
|
||||
mapping=mapping,
|
||||
).save()
|
||||
self._logger.warning(
|
||||
"Mapping failed to evaluate", exc=exc, mapping=mapping
|
||||
)
|
||||
|
@ -5,6 +5,7 @@ from django.db.models import Q
|
||||
from django.test import TestCase
|
||||
|
||||
from authentik.core.models import Group, User
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.managed.manager import ObjectManager
|
||||
from authentik.providers.oauth2.generators import generate_client_secret
|
||||
from authentik.sources.ldap.models import LDAPPropertyMapping, LDAPSource
|
||||
@ -31,6 +32,33 @@ class LDAPSyncTests(TestCase):
|
||||
additional_group_dn="ou=groups",
|
||||
)
|
||||
|
||||
def test_sync_error(self):
|
||||
"""Test user sync"""
|
||||
self.source.property_mappings.set(
|
||||
LDAPPropertyMapping.objects.filter(
|
||||
Q(managed__startswith="goauthentik.io/sources/ldap/default")
|
||||
| Q(managed__startswith="goauthentik.io/sources/ldap/ms")
|
||||
)
|
||||
)
|
||||
mapping = LDAPPropertyMapping.objects.create(
|
||||
name="name",
|
||||
object_field="name",
|
||||
expression="q",
|
||||
)
|
||||
self.source.property_mappings.set([mapping])
|
||||
self.source.save()
|
||||
connection = PropertyMock(return_value=mock_ad_connection(LDAP_PASSWORD))
|
||||
with patch("authentik.sources.ldap.models.LDAPSource.connection", connection):
|
||||
user_sync = UserLDAPSynchronizer(self.source)
|
||||
user_sync.sync()
|
||||
self.assertFalse(User.objects.filter(username="user0_sn").exists())
|
||||
self.assertFalse(User.objects.filter(username="user1_sn").exists())
|
||||
events = Event.objects.filter(
|
||||
action=EventAction.CONFIGURATION_ERROR,
|
||||
context__message="Failed to evaluate property-mapping: name 'q' is not defined",
|
||||
)
|
||||
self.assertTrue(events.exists())
|
||||
|
||||
def test_sync_users_ad(self):
|
||||
"""Test user sync"""
|
||||
self.source.property_mappings.set(
|
||||
|
@ -38,6 +38,7 @@ class TenantSerializer(ModelSerializer):
|
||||
"flow_invalidation",
|
||||
"flow_recovery",
|
||||
"flow_unenrollment",
|
||||
"event_retention",
|
||||
]
|
||||
|
||||
|
||||
|
@ -3,6 +3,7 @@ from typing import Callable
|
||||
|
||||
from django.http.request import HttpRequest
|
||||
from django.http.response import HttpResponse
|
||||
from sentry_sdk.api import set_tag
|
||||
|
||||
from authentik.tenants.utils import get_tenant_for_request
|
||||
|
||||
@ -19,4 +20,6 @@ class TenantMiddleware:
|
||||
if not hasattr(request, "tenant"):
|
||||
tenant = get_tenant_for_request(request)
|
||||
setattr(request, "tenant", tenant)
|
||||
set_tag("authentik.tenant_uuid", tenant.tenant_uuid.hex)
|
||||
set_tag("authentik.tenant_domain", tenant.domain)
|
||||
return self.get_response(request)
|
||||
|
24
authentik/tenants/migrations/0004_tenant_event_retention.py
Normal file
24
authentik/tenants/migrations/0004_tenant_event_retention.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Generated by Django 3.2.5 on 2021-07-24 17:06
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
import authentik.lib.utils.time
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_tenants", "0003_tenant_branding_favicon"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="tenant",
|
||||
name="event_retention",
|
||||
field=models.TextField(
|
||||
default="days=365",
|
||||
help_text="Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).",
|
||||
validators=[authentik.lib.utils.time.timedelta_string_validator],
|
||||
),
|
||||
),
|
||||
]
|
@ -5,6 +5,7 @@ from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from authentik.flows.models import Flow
|
||||
from authentik.lib.utils.time import timedelta_string_validator
|
||||
|
||||
|
||||
class Tenant(models.Model):
|
||||
@ -22,6 +23,7 @@ class Tenant(models.Model):
|
||||
)
|
||||
|
||||
branding_title = models.TextField(default="authentik")
|
||||
|
||||
branding_logo = models.TextField(
|
||||
default="/static/dist/assets/icons/icon_left_brand.svg"
|
||||
)
|
||||
@ -40,6 +42,17 @@ class Tenant(models.Model):
|
||||
Flow, null=True, on_delete=models.SET_NULL, related_name="tenant_unenrollment"
|
||||
)
|
||||
|
||||
event_retention = models.TextField(
|
||||
default="days=365",
|
||||
validators=[timedelta_string_validator],
|
||||
help_text=_(
|
||||
(
|
||||
"Events will be deleted after this duration."
|
||||
"(Format: weeks=3;days=2;hours=3,seconds=2)."
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
if self.default:
|
||||
return "Default tenant"
|
||||
|
@ -1,9 +1,12 @@
|
||||
"""Test tenants"""
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
from authentik.events.models import Event, EventAction
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.tenants.models import Tenant
|
||||
|
||||
|
||||
@ -57,3 +60,28 @@ class TestTenants(TestCase):
|
||||
"ui_footer_links": CONFIG.y("footer_links"),
|
||||
},
|
||||
)
|
||||
|
||||
def test_event_retention(self):
|
||||
"""Test tenant's event retention"""
|
||||
tenant = Tenant.objects.create(
|
||||
domain="foo",
|
||||
default=True,
|
||||
branding_title="custom",
|
||||
event_retention="weeks=3",
|
||||
)
|
||||
factory = RequestFactory()
|
||||
request = factory.get("/")
|
||||
request.tenant = tenant
|
||||
event = Event.new(
|
||||
action=EventAction.SYSTEM_EXCEPTION, message="test"
|
||||
).from_http(request)
|
||||
self.assertEqual(
|
||||
event.expires.day, (event.created + timedelta_from_string("weeks=3")).day
|
||||
)
|
||||
self.assertEqual(
|
||||
event.expires.month,
|
||||
(event.created + timedelta_from_string("weeks=3")).month,
|
||||
)
|
||||
self.assertEqual(
|
||||
event.expires.year, (event.created + timedelta_from_string("weeks=3")).year
|
||||
)
|
||||
|
@ -6,12 +6,6 @@ trigger:
|
||||
- next
|
||||
- version-*
|
||||
|
||||
variables:
|
||||
${{ if startsWith(variables['Build.SourceBranch'], 'refs/pull/') }}:
|
||||
branchName: ${{ replace(variables['System.PullRequest.SourceBranch'], '/', '-') }}
|
||||
${{ if startsWith(variables['Build.SourceBranch'], 'refs/heads/') }}:
|
||||
branchName: ${{ replace(variables['Build.SourceBranchName'], 'refs/heads/', '') }}
|
||||
|
||||
stages:
|
||||
- stage: generate
|
||||
jobs:
|
||||
|
@ -14,13 +14,13 @@ resources:
|
||||
- repo: self
|
||||
|
||||
variables:
|
||||
POSTGRES_DB: authentik
|
||||
POSTGRES_USER: authentik
|
||||
POSTGRES_PASSWORD: "EK-5jnKfjrGRm<77"
|
||||
${{ if startsWith(variables['Build.SourceBranch'], 'refs/pull/') }}:
|
||||
branchName: ${{ replace(variables['System.PullRequest.SourceBranch'], '/', '-') }}
|
||||
${{ if startsWith(variables['Build.SourceBranch'], 'refs/heads/') }}:
|
||||
branchName: ${{ replace(variables['Build.SourceBranchName'], 'refs/heads/', '') }}
|
||||
- name: POSTGRES_DB
|
||||
value: authentik
|
||||
- name: POSTGRES_USER
|
||||
value: authentik
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: "EK-5jnKfjrGRm<77"
|
||||
- group: coverage
|
||||
|
||||
stages:
|
||||
- stage: Lint_and_test
|
||||
@ -397,6 +397,16 @@ stages:
|
||||
- task: CmdLine@2
|
||||
inputs:
|
||||
script: bash <(curl -s https://codecov.io/bash)
|
||||
- task: CmdLine@2
|
||||
inputs:
|
||||
script: |
|
||||
npm install -g @zeus-ci/cli
|
||||
npx zeus job update -b $BUILD_BUILDID -j $BUILD_BUILDNUMBER -r $BUILD_SOURCEVERSION
|
||||
npx zeus upload -b $BUILD_BUILDID -j $BUILD_BUILDNUMBER -t "application/x-cobertura+xml" coverage.xml
|
||||
npx zeus upload -b $BUILD_BUILDID -j $BUILD_BUILDNUMBER -t "application/x-junit+xml" coverage-e2e/unittest.xml
|
||||
npx zeus upload -b $BUILD_BUILDID -j $BUILD_BUILDNUMBER -t "application/x-junit+xml" coverage-integration/unittest.xml
|
||||
npx zeus upload -b $BUILD_BUILDID -j $BUILD_BUILDNUMBER -t "application/x-junit+xml" coverage-unittest/unittest.xml
|
||||
npx zeus job update --status=passed -b $BUILD_BUILDID -j $BUILD_BUILDNUMBER -r $BUILD_SOURCEVERSION
|
||||
- stage: Build
|
||||
jobs:
|
||||
- job: build_server
|
||||
|
@ -21,7 +21,7 @@ services:
|
||||
networks:
|
||||
- internal
|
||||
server:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.7.1-rc1}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.7.1}
|
||||
restart: unless-stopped
|
||||
command: server
|
||||
environment:
|
||||
@ -44,7 +44,7 @@ services:
|
||||
- "0.0.0.0:9000:9000"
|
||||
- "0.0.0.0:9443:9443"
|
||||
worker:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.7.1-rc1}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2021.7.1}
|
||||
restart: unless-stopped
|
||||
command: worker
|
||||
networks:
|
||||
|
@ -17,4 +17,4 @@ func OutpostUserAgent() string {
|
||||
return fmt.Sprintf("authentik-outpost@%s (%s)", VERSION, BUILD())
|
||||
}
|
||||
|
||||
const VERSION = "2021.7.1-rc1"
|
||||
const VERSION = "2021.7.1"
|
||||
|
@ -44,7 +44,7 @@ func NewAPIController(akURL url.URL, token string) *APIController {
|
||||
config.Host = akURL.Host
|
||||
config.Scheme = akURL.Scheme
|
||||
config.HTTPClient = &http.Client{
|
||||
Transport: GetTLSTransport(),
|
||||
Transport: NewTracingTransport(GetTLSTransport()),
|
||||
}
|
||||
config.AddDefaultHeader("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
|
||||
|
@ -71,14 +71,12 @@ func (ac *APIController) Shutdown() {
|
||||
func (ac *APIController) startWSHandler() {
|
||||
logger := ac.logger.WithField("loop", "ws-handler")
|
||||
for {
|
||||
if !ac.wsConn.IsConnected() {
|
||||
continue
|
||||
}
|
||||
var wsMsg websocketMessage
|
||||
err := ac.wsConn.ReadJSON(&wsMsg)
|
||||
if err != nil {
|
||||
logger.WithError(err).Warning("ws write error, reconnecting")
|
||||
ac.wsConn.CloseAndReconnect()
|
||||
time.Sleep(time.Second * 5)
|
||||
continue
|
||||
}
|
||||
if wsMsg.Instruction == WebsocketInstructionTriggerUpdate {
|
||||
|
85
internal/outpost/ak/crypto.go
Normal file
85
internal/outpost/ak/crypto.go
Normal file
@ -0,0 +1,85 @@
|
||||
package ak
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/api"
|
||||
)
|
||||
|
||||
type CryptoStore struct {
|
||||
api *api.CryptoApiService
|
||||
|
||||
log *log.Entry
|
||||
|
||||
fingerprints map[string]string
|
||||
certificates map[string]*tls.Certificate
|
||||
}
|
||||
|
||||
func NewCryptoStore(cryptoApi *api.CryptoApiService) *CryptoStore {
|
||||
return &CryptoStore{
|
||||
api: cryptoApi,
|
||||
log: log.WithField("logger", "authentik.outpost.cryptostore"),
|
||||
fingerprints: make(map[string]string),
|
||||
certificates: make(map[string]*tls.Certificate),
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *CryptoStore) AddKeypair(uuid string) error {
|
||||
// If they keypair was already added, don't
|
||||
// do it again
|
||||
if _, ok := cs.fingerprints[uuid]; ok {
|
||||
return nil
|
||||
}
|
||||
// reset fingerprint to force update
|
||||
cs.fingerprints[uuid] = ""
|
||||
err := cs.Fetch(uuid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cs.fingerprints[uuid] = cs.getFingerprint(uuid)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs *CryptoStore) getFingerprint(uuid string) string {
|
||||
kp, _, err := cs.api.CryptoCertificatekeypairsRetrieve(context.Background(), uuid).Execute()
|
||||
if err != nil {
|
||||
cs.log.WithField("uuid", uuid).WithError(err).Warning("Failed to fetch certificate's fingerprint")
|
||||
return ""
|
||||
}
|
||||
return kp.FingerprintSha256
|
||||
}
|
||||
|
||||
func (cs *CryptoStore) Fetch(uuid string) error {
|
||||
cfp := cs.getFingerprint(uuid)
|
||||
if cfp == cs.fingerprints[uuid] {
|
||||
cs.log.WithField("uuid", uuid).Info("Fingerprint hasn't changed, not fetching cert")
|
||||
return nil
|
||||
}
|
||||
cs.log.WithField("uuid", uuid).Info("Fetching certificate and private key")
|
||||
|
||||
cert, _, err := cs.api.CryptoCertificatekeypairsViewCertificateRetrieve(context.Background(), uuid).Execute()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key, _, err := cs.api.CryptoCertificatekeypairsViewPrivateKeyRetrieve(context.Background(), uuid).Execute()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x509cert, err := tls.X509KeyPair([]byte(cert.Data), []byte(key.Data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cs.certificates[uuid] = &x509cert
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs *CryptoStore) Get(uuid string) *tls.Certificate {
|
||||
err := cs.Fetch(uuid)
|
||||
if err != nil {
|
||||
cs.log.WithError(err).Warning("failed to fetch certificate")
|
||||
}
|
||||
return cs.certificates[uuid]
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
package ak
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
@ -11,7 +9,6 @@ import (
|
||||
"github.com/getsentry/sentry-go"
|
||||
httptransport "github.com/go-openapi/runtime/client"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/api"
|
||||
"goauthentik.io/internal/constants"
|
||||
)
|
||||
|
||||
@ -38,15 +35,17 @@ func doGlobalSetup(config map[string]interface{}) {
|
||||
}
|
||||
log.WithField("buildHash", constants.BUILD()).WithField("version", constants.VERSION).Info("Starting authentik outpost")
|
||||
|
||||
env := config[ConfigErrorReportingEnvironment].(string)
|
||||
var dsn string
|
||||
if config[ConfigErrorReportingEnabled].(bool) {
|
||||
dsn = "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8"
|
||||
log.Debug("Error reporting enabled")
|
||||
log.WithField("env", env).Debug("Error reporting enabled")
|
||||
}
|
||||
|
||||
err := sentry.Init(sentry.ClientOptions{
|
||||
Dsn: dsn,
|
||||
Environment: config[ConfigErrorReportingEnvironment].(string),
|
||||
Dsn: dsn,
|
||||
Environment: env,
|
||||
TracesSampleRate: 1,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("sentry.Init: %s", err)
|
||||
@ -69,21 +68,3 @@ func GetTLSTransport() http.RoundTripper {
|
||||
}
|
||||
return tlsTransport
|
||||
}
|
||||
|
||||
// ParseCertificate Load certificate from Keyepair UUID and parse it into a go Certificate
|
||||
func ParseCertificate(kpUuid string, cryptoApi *api.CryptoApiService) (*tls.Certificate, error) {
|
||||
cert, _, err := cryptoApi.CryptoCertificatekeypairsViewCertificateRetrieve(context.Background(), kpUuid).Execute()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, _, err := cryptoApi.CryptoCertificatekeypairsViewPrivateKeyRetrieve(context.Background(), kpUuid).Execute()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
x509cert, err := tls.X509KeyPair([]byte(cert.Data), []byte(key.Data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &x509cert, nil
|
||||
}
|
||||
|
23
internal/outpost/ak/tracing.go
Normal file
23
internal/outpost/ak/tracing.go
Normal file
@ -0,0 +1,23 @@
|
||||
package ak
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
type tracingTransport struct {
|
||||
inner http.RoundTripper
|
||||
}
|
||||
|
||||
func NewTracingTransport(inner http.RoundTripper) *tracingTransport {
|
||||
return &tracingTransport{inner}
|
||||
}
|
||||
|
||||
func (tt *tracingTransport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
span := sentry.StartSpan(r.Context(), "authentik.go.http_request")
|
||||
span.SetTag("url", r.URL.String())
|
||||
span.SetTag("method", r.Method)
|
||||
defer span.Finish()
|
||||
return tt.inner.RoundTrip(r.WithContext(span.Context()))
|
||||
}
|
@ -11,6 +11,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/api"
|
||||
"goauthentik.io/internal/constants"
|
||||
@ -34,14 +35,19 @@ const (
|
||||
type FlowExecutor struct {
|
||||
Params url.Values
|
||||
Answers map[StageComponent]string
|
||||
Context context.Context
|
||||
|
||||
api *api.APIClient
|
||||
flowSlug string
|
||||
log *log.Entry
|
||||
token string
|
||||
|
||||
sp *sentry.Span
|
||||
}
|
||||
|
||||
func NewFlowExecutor(flowSlug string, refConfig *api.Configuration, logFields log.Fields) *FlowExecutor {
|
||||
func NewFlowExecutor(ctx context.Context, flowSlug string, refConfig *api.Configuration, logFields log.Fields) *FlowExecutor {
|
||||
rsp := sentry.StartSpan(ctx, "authentik.outposts.flow_executor")
|
||||
|
||||
l := log.WithField("flow", flowSlug).WithFields(logFields)
|
||||
jar, err := cookiejar.New(nil)
|
||||
if err != nil {
|
||||
@ -55,16 +61,18 @@ func NewFlowExecutor(flowSlug string, refConfig *api.Configuration, logFields lo
|
||||
config.UserAgent = constants.OutpostUserAgent()
|
||||
config.HTTPClient = &http.Client{
|
||||
Jar: jar,
|
||||
Transport: ak.GetTLSTransport(),
|
||||
Transport: ak.NewTracingTransport(ak.GetTLSTransport()),
|
||||
}
|
||||
apiClient := api.NewAPIClient(config)
|
||||
return &FlowExecutor{
|
||||
Params: url.Values{},
|
||||
Answers: make(map[StageComponent]string),
|
||||
Context: rsp.Context(),
|
||||
api: apiClient,
|
||||
flowSlug: flowSlug,
|
||||
log: l,
|
||||
token: strings.Split(refConfig.DefaultHeader["Authorization"], " ")[1],
|
||||
sp: rsp,
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,7 +97,9 @@ func (fe *FlowExecutor) DelegateClientIP(a net.Addr) {
|
||||
}
|
||||
|
||||
func (fe *FlowExecutor) CheckApplicationAccess(appSlug string) (bool, error) {
|
||||
p, _, err := fe.api.CoreApi.CoreApplicationsCheckAccessRetrieve(context.Background(), appSlug).Execute()
|
||||
acsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.check_access")
|
||||
defer acsp.Finish()
|
||||
p, _, err := fe.api.CoreApi.CoreApplicationsCheckAccessRetrieve(acsp.Context(), appSlug).Execute()
|
||||
if !p.Passing {
|
||||
fe.log.Info("Access denied for user")
|
||||
return false, nil
|
||||
@ -113,14 +123,24 @@ func (fe *FlowExecutor) Execute() (bool, error) {
|
||||
}
|
||||
|
||||
func (fe *FlowExecutor) solveFlowChallenge(depth int) (bool, error) {
|
||||
req := fe.api.FlowsApi.FlowsExecutorGet(context.Background(), fe.flowSlug).Query(fe.Params.Encode())
|
||||
defer fe.sp.Finish()
|
||||
|
||||
// Get challenge
|
||||
gcsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.get_challenge")
|
||||
req := fe.api.FlowsApi.FlowsExecutorGet(gcsp.Context(), fe.flowSlug).Query(fe.Params.Encode())
|
||||
challenge, _, err := req.Execute()
|
||||
if err != nil {
|
||||
return false, errors.New("failed to get challenge")
|
||||
}
|
||||
ch := challenge.GetActualInstance().(ChallengeInt)
|
||||
fe.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got challenge")
|
||||
responseReq := fe.api.FlowsApi.FlowsExecutorSolve(context.Background(), fe.flowSlug).Query(fe.Params.Encode())
|
||||
gcsp.SetTag("ak_challenge", string(ch.GetType()))
|
||||
gcsp.SetTag("ak_component", ch.GetComponent())
|
||||
gcsp.Finish()
|
||||
|
||||
// Resole challenge
|
||||
scsp := sentry.StartSpan(fe.Context, "authentik.outposts.flow_executor.solve_challenge")
|
||||
responseReq := fe.api.FlowsApi.FlowsExecutorSolve(scsp.Context(), fe.flowSlug).Query(fe.Params.Encode())
|
||||
switch ch.GetComponent() {
|
||||
case string(StageIdentification):
|
||||
responseReq = responseReq.FlowChallengeResponseRequest(api.IdentificationChallengeResponseRequestAsFlowChallengeResponseRequest(api.NewIdentificationChallengeResponseRequest(fe.getAnswer(StageIdentification))))
|
||||
@ -150,9 +170,14 @@ func (fe *FlowExecutor) solveFlowChallenge(depth int) (bool, error) {
|
||||
default:
|
||||
return false, fmt.Errorf("unsupported challenge type %s", ch.GetComponent())
|
||||
}
|
||||
|
||||
response, _, err := responseReq.Execute()
|
||||
ch = response.GetActualInstance().(ChallengeInt)
|
||||
fe.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got response")
|
||||
scsp.SetTag("ak_challenge", string(ch.GetType()))
|
||||
scsp.SetTag("ak_component", ch.GetComponent())
|
||||
scsp.Finish()
|
||||
|
||||
switch ch.GetComponent() {
|
||||
case string(StageAccessDenied):
|
||||
return false, errors.New("got ak-stage-access-denied")
|
||||
|
@ -13,7 +13,6 @@ import (
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/pires/go-proxyproto"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/internal/outpost/ak"
|
||||
)
|
||||
|
||||
func (ls *LDAPServer) Refresh() error {
|
||||
@ -45,14 +44,12 @@ func (ls *LDAPServer) Refresh() error {
|
||||
gidStartNumber: *provider.GidStartNumber,
|
||||
}
|
||||
if provider.Certificate.Get() != nil {
|
||||
logger.WithField("provider", provider.Name).Debug("Enabling TLS")
|
||||
cert, err := ak.ParseCertificate(*provider.Certificate.Get(), ls.ac.Client.CryptoApi)
|
||||
kp := provider.Certificate.Get()
|
||||
err := ls.cs.AddKeypair(*kp)
|
||||
if err != nil {
|
||||
logger.WithField("provider", provider.Name).WithError(err).Warning("Failed to fetch certificate")
|
||||
} else {
|
||||
providers[idx].cert = cert
|
||||
logger.WithField("provider", provider.Name).Debug("Loaded certificates")
|
||||
ls.log.WithError(err).Warning("Failed to initially fetch certificate")
|
||||
}
|
||||
providers[idx].cert = ls.cs.Get(*kp)
|
||||
}
|
||||
}
|
||||
ls.providers = providers
|
||||
|
@ -1,9 +1,11 @@
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/google/uuid"
|
||||
"github.com/nmcclain/ldap"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@ -15,9 +17,15 @@ type BindRequest struct {
|
||||
id string
|
||||
conn net.Conn
|
||||
log *log.Entry
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LDAPResultCode, error) {
|
||||
span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.bind",
|
||||
sentry.TransactionName("authentik.providers.ldap.bind"))
|
||||
span.SetTag("user.username", bindDN)
|
||||
defer span.Finish()
|
||||
|
||||
bindDN = strings.ToLower(bindDN)
|
||||
rid := uuid.New().String()
|
||||
req := BindRequest{
|
||||
@ -26,6 +34,7 @@ func (ls *LDAPServer) Bind(bindDN string, bindPW string, conn net.Conn) (ldap.LD
|
||||
conn: conn,
|
||||
log: ls.log.WithField("bindDN", bindDN).WithField("requestId", rid).WithField("client", conn.RemoteAddr().String()),
|
||||
id: rid,
|
||||
ctx: span.Context(),
|
||||
}
|
||||
req.log.Info("Bind request")
|
||||
for _, instance := range ls.providers {
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
"github.com/nmcclain/ldap"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@ -34,7 +35,7 @@ func (pi *ProviderInstance) getUsername(dn string) (string, error) {
|
||||
}
|
||||
|
||||
func (pi *ProviderInstance) Bind(username string, req BindRequest) (ldap.LDAPResultCode, error) {
|
||||
fe := outpost.NewFlowExecutor(pi.flowSlug, pi.s.ac.Client.GetConfig(), log.Fields{
|
||||
fe := outpost.NewFlowExecutor(req.ctx, pi.flowSlug, pi.s.ac.Client.GetConfig(), log.Fields{
|
||||
"bindDN": req.BindDN,
|
||||
"client": req.conn.RemoteAddr().String(),
|
||||
"requestId": req.id,
|
||||
@ -53,6 +54,7 @@ func (pi *ProviderInstance) Bind(username string, req BindRequest) (ldap.LDAPRes
|
||||
req.log.WithError(err).Warning("failed to execute flow")
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
|
||||
access, err := fe.CheckApplicationAccess(pi.appSlug)
|
||||
if !access {
|
||||
req.log.Info("Access denied for user")
|
||||
@ -63,6 +65,7 @@ func (pi *ProviderInstance) Bind(username string, req BindRequest) (ldap.LDAPRes
|
||||
return ldap.LDAPResultOperationsError, nil
|
||||
}
|
||||
req.log.Info("User has access")
|
||||
uisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.bind.user_info")
|
||||
// Get user info to store in context
|
||||
userInfo, _, err := fe.ApiClient().CoreApi.CoreUsersMeRetrieve(context.Background()).Execute()
|
||||
if err != nil {
|
||||
@ -78,7 +81,7 @@ func (pi *ProviderInstance) Bind(username string, req BindRequest) (ldap.LDAPRes
|
||||
if pi.boundUsers[req.BindDN].CanSearch {
|
||||
req.log.WithField("group", cs).Info("Allowed access to search")
|
||||
}
|
||||
|
||||
uisp.Finish()
|
||||
defer pi.boundUsersMutex.Unlock()
|
||||
pi.delayDeleteUserInfo(username)
|
||||
return ldap.LDAPResultSuccess, nil
|
||||
|
@ -1,11 +1,12 @@
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/nmcclain/ldap"
|
||||
"goauthentik.io/api"
|
||||
)
|
||||
@ -17,6 +18,7 @@ func (pi *ProviderInstance) SearchMe(user api.User) (ldap.ServerSearchResult, er
|
||||
}
|
||||
|
||||
func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult, error) {
|
||||
accsp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.check_access")
|
||||
baseDN := strings.ToLower("," + pi.BaseDN)
|
||||
|
||||
entries := []*ldap.Entry{}
|
||||
@ -32,8 +34,8 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
|
||||
}
|
||||
|
||||
pi.boundUsersMutex.RLock()
|
||||
defer pi.boundUsersMutex.RUnlock()
|
||||
flags, ok := pi.boundUsers[req.BindDN]
|
||||
pi.boundUsersMutex.RUnlock()
|
||||
if !ok {
|
||||
pi.log.Debug("User info not cached")
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultInsufficientAccessRights}, errors.New("access denied")
|
||||
@ -42,31 +44,60 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
|
||||
pi.log.Debug("User can't search, showing info about user")
|
||||
return pi.SearchMe(flags.UserInfo)
|
||||
}
|
||||
accsp.Finish()
|
||||
|
||||
parsedFilter, err := ldap.CompileFilter(req.Filter)
|
||||
if err != nil {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: error parsing filter: %s", req.Filter)
|
||||
}
|
||||
|
||||
switch filterEntity {
|
||||
default:
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("Search Error: unhandled filter type: %s [%s]", filterEntity, req.Filter)
|
||||
case GroupObjectClass:
|
||||
groups, _, err := pi.s.ac.Client.CoreApi.CoreGroupsList(context.Background()).Execute()
|
||||
if err != nil {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("API Error: %s", err)
|
||||
}
|
||||
pi.log.WithField("count", len(groups.Results)).Trace("Got results from API")
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
|
||||
for _, g := range groups.Results {
|
||||
entries = append(entries, pi.GroupEntry(pi.APIGroupToLDAPGroup(g)))
|
||||
}
|
||||
gEntries := make([]*ldap.Entry, 0)
|
||||
uEntries := make([]*ldap.Entry, 0)
|
||||
|
||||
users, _, err := pi.s.ac.Client.CoreApi.CoreUsersList(context.Background()).Execute()
|
||||
if err != nil {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("API Error: %s", err)
|
||||
}
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
gapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_group")
|
||||
groups, _, err := parseFilterForGroup(pi.s.ac.Client.CoreApi.CoreGroupsList(gapisp.Context()), parsedFilter).Execute()
|
||||
gapisp.Finish()
|
||||
if err != nil {
|
||||
req.log.WithError(err).Warning("failed to get groups")
|
||||
return
|
||||
}
|
||||
pi.log.WithField("count", len(groups.Results)).Trace("Got results from API")
|
||||
|
||||
for _, u := range users.Results {
|
||||
entries = append(entries, pi.GroupEntry(pi.APIUserToLDAPGroup(u)))
|
||||
}
|
||||
for _, g := range groups.Results {
|
||||
gEntries = append(gEntries, pi.GroupEntry(pi.APIGroupToLDAPGroup(g)))
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
uapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_user")
|
||||
users, _, err := parseFilterForUser(pi.s.ac.Client.CoreApi.CoreUsersList(uapisp.Context()), parsedFilter).Execute()
|
||||
uapisp.Finish()
|
||||
if err != nil {
|
||||
req.log.WithError(err).Warning("failed to get groups")
|
||||
return
|
||||
}
|
||||
|
||||
for _, u := range users.Results {
|
||||
uEntries = append(uEntries, pi.GroupEntry(pi.APIUserToLDAPGroup(u)))
|
||||
}
|
||||
}()
|
||||
wg.Wait()
|
||||
entries = append(gEntries, uEntries...)
|
||||
case UserObjectClass, "":
|
||||
users, _, err := pi.s.ac.Client.CoreApi.CoreUsersList(context.Background()).Execute()
|
||||
uapisp := sentry.StartSpan(req.ctx, "authentik.providers.ldap.search.api_user")
|
||||
users, _, err := parseFilterForUser(pi.s.ac.Client.CoreApi.CoreUsersList(uapisp.Context()), parsedFilter).Execute()
|
||||
uapisp.Finish()
|
||||
|
||||
if err != nil {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultOperationsError}, fmt.Errorf("API Error: %s", err)
|
||||
}
|
||||
@ -74,7 +105,6 @@ func (pi *ProviderInstance) Search(req SearchRequest) (ldap.ServerSearchResult,
|
||||
entries = append(entries, pi.UserEntry(u))
|
||||
}
|
||||
}
|
||||
pi.log.WithField("filter", req.Filter).Debug("Search OK")
|
||||
return ldap.ServerSearchResult{Entries: entries, Referrals: []string{}, Controls: []ldap.Control{}, ResultCode: ldap.LDAPResultSuccess}, nil
|
||||
}
|
||||
|
||||
|
57
internal/outpost/ldap/instance_search_group.go
Normal file
57
internal/outpost/ldap/instance_search_group.go
Normal file
@ -0,0 +1,57 @@
|
||||
package ldap
|
||||
|
||||
import (
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
ber "github.com/nmcclain/asn1-ber"
|
||||
"github.com/nmcclain/ldap"
|
||||
"goauthentik.io/api"
|
||||
)
|
||||
|
||||
func parseFilterForGroup(req api.ApiCoreGroupsListRequest, f *ber.Packet) api.ApiCoreGroupsListRequest {
|
||||
switch f.Tag {
|
||||
case ldap.FilterEqualityMatch:
|
||||
return parseFilterForGroupSingle(req, f)
|
||||
case ldap.FilterAnd:
|
||||
for _, child := range f.Children {
|
||||
req = parseFilterForGroup(req, child)
|
||||
}
|
||||
return req
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func parseFilterForGroupSingle(req api.ApiCoreGroupsListRequest, f *ber.Packet) api.ApiCoreGroupsListRequest {
|
||||
// We can only handle key = value pairs here
|
||||
if len(f.Children) < 2 {
|
||||
return req
|
||||
}
|
||||
k := f.Children[0].Value
|
||||
// Ensure key is string
|
||||
if _, ok := k.(string); !ok {
|
||||
return req
|
||||
}
|
||||
v := f.Children[1].Value
|
||||
// Null values are ignored
|
||||
if v == nil {
|
||||
return req
|
||||
}
|
||||
// Switch on type of the value, then check the key
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
switch k {
|
||||
case "cn":
|
||||
return req.Name(vv)
|
||||
case "member":
|
||||
userDN, err := goldap.ParseDN(vv)
|
||||
if err != nil {
|
||||
return req
|
||||
}
|
||||
username := userDN.RDNs[0].Attributes[0].Value
|
||||
return req.MembersByUsername([]string{username})
|
||||
}
|
||||
// TODO: Support int
|
||||
default:
|
||||
return req
|
||||
}
|
||||
return req
|
||||
}
|
54
internal/outpost/ldap/instance_search_user.go
Normal file
54
internal/outpost/ldap/instance_search_user.go
Normal file
@ -0,0 +1,54 @@
|
||||
package ldap
|
||||
|
||||
import (
|
||||
ber "github.com/nmcclain/asn1-ber"
|
||||
"github.com/nmcclain/ldap"
|
||||
"goauthentik.io/api"
|
||||
)
|
||||
|
||||
func parseFilterForUser(req api.ApiCoreUsersListRequest, f *ber.Packet) api.ApiCoreUsersListRequest {
|
||||
switch f.Tag {
|
||||
case ldap.FilterEqualityMatch:
|
||||
return parseFilterForUserSingle(req, f)
|
||||
case ldap.FilterAnd:
|
||||
for _, child := range f.Children {
|
||||
req = parseFilterForUser(req, child)
|
||||
}
|
||||
return req
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
func parseFilterForUserSingle(req api.ApiCoreUsersListRequest, f *ber.Packet) api.ApiCoreUsersListRequest {
|
||||
// We can only handle key = value pairs here
|
||||
if len(f.Children) < 2 {
|
||||
return req
|
||||
}
|
||||
k := f.Children[0].Value
|
||||
// Ensure key is string
|
||||
if _, ok := k.(string); !ok {
|
||||
return req
|
||||
}
|
||||
v := f.Children[1].Value
|
||||
// Null values are ignored
|
||||
if v == nil {
|
||||
return req
|
||||
}
|
||||
// Switch on type of the value, then check the key
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
switch k {
|
||||
case "cn":
|
||||
return req.Username(vv)
|
||||
case "name":
|
||||
case "displayName":
|
||||
return req.Name(vv)
|
||||
case "mail":
|
||||
return req.Email(vv)
|
||||
}
|
||||
// TODO: Support int
|
||||
default:
|
||||
return req
|
||||
}
|
||||
return req
|
||||
}
|
@ -47,6 +47,7 @@ type LDAPServer struct {
|
||||
s *ldap.Server
|
||||
log *log.Entry
|
||||
ac *ak.APIController
|
||||
cs *ak.CryptoStore
|
||||
defaultCert *tls.Certificate
|
||||
providers []*ProviderInstance
|
||||
}
|
||||
@ -69,6 +70,7 @@ func NewServer(ac *ak.APIController) *LDAPServer {
|
||||
s: s,
|
||||
log: log.WithField("logger", "authentik.outpost.ldap"),
|
||||
ac: ac,
|
||||
cs: ak.NewCryptoStore(ac.Client.CryptoApi),
|
||||
providers: []*ProviderInstance{},
|
||||
}
|
||||
defaultCert, err := crypto.GenerateSelfSignedCert()
|
||||
|
@ -1,10 +1,12 @@
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
"github.com/google/uuid"
|
||||
"github.com/nmcclain/ldap"
|
||||
@ -17,9 +19,15 @@ type SearchRequest struct {
|
||||
id string
|
||||
conn net.Conn
|
||||
log *log.Entry
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (ldap.ServerSearchResult, error) {
|
||||
span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.search", sentry.TransactionName("authentik.providers.ldap.search"))
|
||||
span.SetTag("user.username", bindDN)
|
||||
span.SetTag("ak_filter", searchReq.Filter)
|
||||
span.SetTag("ak_base_dn", searchReq.BaseDN)
|
||||
|
||||
bindDN = strings.ToLower(bindDN)
|
||||
rid := uuid.New().String()
|
||||
req := SearchRequest{
|
||||
@ -28,8 +36,22 @@ func (ls *LDAPServer) Search(bindDN string, searchReq ldap.SearchRequest, conn n
|
||||
conn: conn,
|
||||
log: ls.log.WithField("bindDN", bindDN).WithField("requestId", rid).WithField("client", conn.RemoteAddr().String()).WithField("filter", searchReq.Filter).WithField("baseDN", searchReq.BaseDN),
|
||||
id: rid,
|
||||
ctx: span.Context(),
|
||||
}
|
||||
req.log.Info("Search request")
|
||||
|
||||
defer func() {
|
||||
span.Finish()
|
||||
req.log.WithField("took-ms", span.EndTime.Sub(span.StartTime).Milliseconds()).Info("Search request")
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
log.WithError(err.(error)).Error("recover in serach request")
|
||||
sentry.CaptureException(err.(error))
|
||||
}()
|
||||
|
||||
if searchReq.BaseDN == "" {
|
||||
return ldap.ServerSearchResult{ResultCode: ldap.LDAPResultSuccess}, nil
|
||||
|
@ -15,7 +15,6 @@ import (
|
||||
"github.com/oauth2-proxy/oauth2-proxy/pkg/validation"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"goauthentik.io/api"
|
||||
"goauthentik.io/internal/outpost/ak"
|
||||
)
|
||||
|
||||
type providerBundle struct {
|
||||
@ -89,14 +88,12 @@ func (pb *providerBundle) prepareOpts(provider api.ProxyOutpostConfig) *options.
|
||||
}
|
||||
|
||||
if provider.Certificate.Get() != nil {
|
||||
pb.log.WithField("provider", provider.Name).Debug("Enabling TLS")
|
||||
cert, err := ak.ParseCertificate(*provider.Certificate.Get(), pb.s.ak.Client.CryptoApi)
|
||||
kp := provider.Certificate.Get()
|
||||
err := pb.s.cs.AddKeypair(*kp)
|
||||
if err != nil {
|
||||
pb.log.WithField("provider", provider.Name).WithError(err).Warning("Failed to fetch certificate")
|
||||
return providerOpts
|
||||
pb.log.WithError(err).Warning("Failed to initially fetch certificate")
|
||||
}
|
||||
pb.cert = cert
|
||||
pb.log.WithField("provider", provider.Name).Debug("Loaded certificates")
|
||||
pb.cert = pb.s.cs.Get(*kp)
|
||||
}
|
||||
return providerOpts
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ type Claims struct {
|
||||
Proxy struct {
|
||||
UserAttributes map[string]interface{} `json:"user_attributes"`
|
||||
} `json:"ak_proxy"`
|
||||
Groups []string `json:"groups"`
|
||||
}
|
||||
|
||||
func (c *Claims) FromIDToken(idToken string) error {
|
||||
|
@ -428,6 +428,10 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req
|
||||
if err != nil {
|
||||
log.WithError(err).Warning("Failed to parse IDToken")
|
||||
}
|
||||
// Set groups in header
|
||||
groups := strings.Join(claims.Groups, "|")
|
||||
req.Header["X-Auth-Groups"] = []string{groups}
|
||||
|
||||
userAttributes := claims.Proxy.UserAttributes
|
||||
// Attempt to set basic auth based on user's attributes
|
||||
if p.SetBasicAuth {
|
||||
@ -461,6 +465,7 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req
|
||||
func (p *OAuthProxy) stripAuthHeaders(req *http.Request) {
|
||||
if p.PassUserHeaders {
|
||||
req.Header.Del("X-Forwarded-User")
|
||||
req.Header.Del("X-Auth-Groups")
|
||||
req.Header.Del("X-Forwarded-Email")
|
||||
req.Header.Del("X-Forwarded-Preferred-Username")
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ type Server struct {
|
||||
stop chan struct{} // channel for waiting shutdown
|
||||
logger *log.Entry
|
||||
ak *ak.APIController
|
||||
cs *ak.CryptoStore
|
||||
defaultCert tls.Certificate
|
||||
}
|
||||
|
||||
@ -35,6 +36,7 @@ func NewServer(ac *ak.APIController) *Server {
|
||||
logger: log.WithField("logger", "authentik.outpost.proxy-http-server"),
|
||||
defaultCert: defaultCert,
|
||||
ak: ac,
|
||||
cs: ak.NewCryptoStore(ac.Client.CryptoApi),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,6 @@ RUN docker-entrypoint.sh generate \
|
||||
|
||||
# Stage 2: Build
|
||||
FROM golang:1.16.6 AS builder
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
WORKDIR /go/src/goauthentik.io
|
||||
|
||||
@ -28,6 +26,9 @@ RUN go build -o /go/ldap ./cmd/ldap
|
||||
# Stage 3: Run
|
||||
FROM gcr.io/distroless/base-debian10:debug
|
||||
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
COPY --from=builder /go/ldap /
|
||||
|
||||
HEALTHCHECK CMD [ "wget", "--spider", "http://localhost:4180/akprox/ping" ]
|
||||
|
@ -15,8 +15,6 @@ RUN docker-entrypoint.sh generate \
|
||||
|
||||
# Stage 2: Build
|
||||
FROM golang:1.16.6 AS builder
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
WORKDIR /go/src/goauthentik.io
|
||||
|
||||
@ -28,6 +26,9 @@ RUN go build -o /go/proxy ./cmd/proxy
|
||||
# Stage 3: Run
|
||||
FROM gcr.io/distroless/base-debian10:debug
|
||||
|
||||
ARG GIT_BUILD_HASH
|
||||
ENV GIT_BUILD_HASH=$GIT_BUILD_HASH
|
||||
|
||||
COPY --from=builder /go/proxy /
|
||||
|
||||
HEALTHCHECK CMD [ "wget", "--spider", "http://localhost:4180/akprox/ping" ]
|
||||
|
38
schema.yml
38
schema.yml
@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: authentik
|
||||
version: 2021.7.1-rc1
|
||||
version: 2021.7.1
|
||||
description: Making authentication simple.
|
||||
contact:
|
||||
email: hello@beryju.org
|
||||
@ -1932,6 +1932,24 @@ paths:
|
||||
name: is_superuser
|
||||
schema:
|
||||
type: boolean
|
||||
- in: query
|
||||
name: members_by_pk
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: integer
|
||||
explode: true
|
||||
style: form
|
||||
- in: query
|
||||
name: members_by_username
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_
|
||||
only.
|
||||
explode: true
|
||||
style: form
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
@ -2843,6 +2861,10 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
description: Attributes
|
||||
- in: query
|
||||
name: email
|
||||
schema:
|
||||
type: string
|
||||
- in: query
|
||||
name: is_active
|
||||
schema:
|
||||
@ -25880,6 +25902,9 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
event_retention:
|
||||
type: string
|
||||
description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).'
|
||||
PatchedTokenRequest:
|
||||
type: object
|
||||
description: Token Serializer
|
||||
@ -26694,13 +26719,8 @@ components:
|
||||
Exclusive with internal_host.
|
||||
cookie_domain:
|
||||
type: string
|
||||
forward_auth_mode:
|
||||
type: boolean
|
||||
readOnly: true
|
||||
deprecated: true
|
||||
required:
|
||||
- external_host
|
||||
- forward_auth_mode
|
||||
- name
|
||||
- oidc_configuration
|
||||
- pk
|
||||
@ -27960,6 +27980,9 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
event_retention:
|
||||
type: string
|
||||
description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).'
|
||||
required:
|
||||
- domain
|
||||
- tenant_uuid
|
||||
@ -27995,6 +28018,9 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
event_retention:
|
||||
type: string
|
||||
description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).'
|
||||
required:
|
||||
- domain
|
||||
Token:
|
||||
|
334
web/package-lock.json
generated
334
web/package-lock.json
generated
@ -24,20 +24,20 @@
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-replace": "^3.0.0",
|
||||
"@rollup/plugin-typescript": "^8.2.3",
|
||||
"@sentry/browser": "^6.9.0",
|
||||
"@sentry/tracing": "^6.9.0",
|
||||
"@sentry/browser": "^6.10.0",
|
||||
"@sentry/tracing": "^6.10.0",
|
||||
"@types/chart.js": "^2.9.34",
|
||||
"@types/codemirror": "5.60.2",
|
||||
"@types/grecaptcha": "^3.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.28.4",
|
||||
"@typescript-eslint/parser": "^4.28.4",
|
||||
"@typescript-eslint/eslint-plugin": "^4.28.5",
|
||||
"@typescript-eslint/parser": "^4.28.5",
|
||||
"@webcomponents/webcomponentsjs": "^2.5.0",
|
||||
"authentik-api": "file:api",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"chart.js": "^3.4.1",
|
||||
"chart.js": "^3.5.0",
|
||||
"chartjs-adapter-moment": "^1.0.0",
|
||||
"codemirror": "^5.62.1",
|
||||
"codemirror": "^5.62.2",
|
||||
"construct-style-sheets-polyfill": "^2.4.16",
|
||||
"eslint": "^7.31.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
@ -48,7 +48,7 @@
|
||||
"lit-html": "^1.4.1",
|
||||
"moment": "^2.29.1",
|
||||
"rapidoc": "^9.0.0",
|
||||
"rollup": "^2.53.2",
|
||||
"rollup": "^2.54.0",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"rollup-plugin-cssimport": "^1.0.2",
|
||||
@ -2341,13 +2341,13 @@
|
||||
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg=="
|
||||
},
|
||||
"node_modules/@sentry/browser": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.9.0.tgz",
|
||||
"integrity": "sha512-4JnEPcwoNs6JqeEd4wscBq+hxpotEJ0DJ4eOIsaNZIMyqEHXBHTXCk/gfrSsiZFrkHM4PgvUHOxaC0HcZ92oBA==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.10.0.tgz",
|
||||
"integrity": "sha512-H0Blgp8f8bomebkkGWIgxHVjabtQAlsKJDiFXBg7gIc75YcarRxwH0R3hMog1/h8mmv4CGGUsy5ljYW6jsNnvA==",
|
||||
"dependencies": {
|
||||
"@sentry/core": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/core": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -2360,14 +2360,14 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/core": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.9.0.tgz",
|
||||
"integrity": "sha512-oFX2qQcMLujCeIuCQGlhpTUIOXiU5n6V2lqDnvMXUV8gKpplBPalwdlR9bgbSi+VO8u7LjHR1IKM0RAPWgNHWw==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.10.0.tgz",
|
||||
"integrity": "sha512-5KlxHJlbD7AMo+b9pMGkjxUOfMILtsqCtGgI7DMvZNfEkdohO8QgUY+hPqr540kmwArFS91ipQYWhqzGaOhM3Q==",
|
||||
"dependencies": {
|
||||
"@sentry/hub": "6.9.0",
|
||||
"@sentry/minimal": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/hub": "6.10.0",
|
||||
"@sentry/minimal": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -2380,12 +2380,12 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/hub": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.9.0.tgz",
|
||||
"integrity": "sha512-5mors7ojbo7G85ZmoVPQBgFBMONAJwyZfV0LNLy14GenoaVNuxTPyvAQiJb1FYq+x6YZ3CvqGX6r74KRKQU87w==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.10.0.tgz",
|
||||
"integrity": "sha512-MV8wjhWiFAXZAhmj7Ef5QdBr2IF93u8xXiIo2J+dRZ7eVa4/ZszoUiDbhUcl/TPxczaw4oW2a6tINBNFLzXiig==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -2398,12 +2398,12 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/minimal": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.9.0.tgz",
|
||||
"integrity": "sha512-GBZ6wG2Rc1wInYEl2BZTZc/t57O1Da876ifLsSPpEQAEnGWbqZWb8RLjZskH09ZIL/K4XCIDDi5ySzN8kFUWJw==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.10.0.tgz",
|
||||
"integrity": "sha512-yarm046UgUFIBoxqnBan2+BEgaO9KZCrLzsIsmALiQvpfW92K1lHurSawl5W6SR7wCYBnNn7CPvPE/BHFdy4YA==",
|
||||
"dependencies": {
|
||||
"@sentry/hub": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/hub": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -2416,14 +2416,14 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/tracing": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.9.0.tgz",
|
||||
"integrity": "sha512-gogVTypolhPazXr3Lue8HgzBg5Sy1cQpEp5Iq9LtECs+TlOlxJ+S+P+EIjEZ0f1AHVu706jr5cY2G2Shluli9g==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.10.0.tgz",
|
||||
"integrity": "sha512-jZj6Aaf8kU5wgyNXbAJHosHn8OOFdK14lgwYPb/AIDsY35g9a9ncTOqIOBp8X3KkmSR8lcBzAEyiUzCxAis2jA==",
|
||||
"dependencies": {
|
||||
"@sentry/hub": "6.9.0",
|
||||
"@sentry/minimal": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/hub": "6.10.0",
|
||||
"@sentry/minimal": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -2436,19 +2436,19 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/types": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.9.0.tgz",
|
||||
"integrity": "sha512-v52HJqLoLapEnqS2NdVtUXPvT+aezQgNXQkp8hiQ3RUdTm5cffwBVG7wlbpE6OsOOIZxd6p1zKylFkwCypiIIA==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.10.0.tgz",
|
||||
"integrity": "sha512-M7s0JFgG7/6/yNVYoPUbxzaXDhnzyIQYRRJJKRaTD77YO4MHvi4Ke8alBWqD5fer0cPIfcSkBqa9BLdqRqcMWw==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/utils": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.9.0.tgz",
|
||||
"integrity": "sha512-PimDr6KAi4cCp5hQZ8Az2/pDcdfhTu7WAU30Dd9MZwknpHSTmD4G6QvkdrB5er6kMMnNQOC7rMo6w/Do3m6X3w==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.10.0.tgz",
|
||||
"integrity": "sha512-F9OczOcZMFtazYVZ6LfRIe65/eOfQbiAedIKS0li4npuMz0jKYRbxrjd/U7oLiNQkPAp4/BujU4m1ZIwq6a+tg==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -2606,12 +2606,12 @@
|
||||
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.4.tgz",
|
||||
"integrity": "sha512-s1oY4RmYDlWMlcV0kKPBaADn46JirZzvvH7c2CtAqxCY96S538JRBAzt83RrfkDheV/+G/vWNK0zek+8TB3Gmw==",
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.5.tgz",
|
||||
"integrity": "sha512-m31cPEnbuCqXtEZQJOXAHsHvtoDi9OVaeL5wZnO2KZTnkvELk+u6J6jHg+NzvWQxk+87Zjbc4lJS4NHmgImz6Q==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/experimental-utils": "4.28.4",
|
||||
"@typescript-eslint/scope-manager": "4.28.4",
|
||||
"@typescript-eslint/experimental-utils": "4.28.5",
|
||||
"@typescript-eslint/scope-manager": "4.28.5",
|
||||
"debug": "^4.3.1",
|
||||
"functional-red-black-tree": "^1.0.1",
|
||||
"regexpp": "^3.1.0",
|
||||
@ -2636,14 +2636,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/experimental-utils": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.4.tgz",
|
||||
"integrity": "sha512-OglKWOQRWTCoqMSy6pm/kpinEIgdcXYceIcH3EKWUl4S8xhFtN34GQRaAvTIZB9DD94rW7d/U7tUg3SYeDFNHA==",
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.5.tgz",
|
||||
"integrity": "sha512-bGPLCOJAa+j49hsynTaAtQIWg6uZd8VLiPcyDe4QPULsvQwLHGLSGKKcBN8/lBxIX14F74UEMK2zNDI8r0okwA==",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.7",
|
||||
"@typescript-eslint/scope-manager": "4.28.4",
|
||||
"@typescript-eslint/types": "4.28.4",
|
||||
"@typescript-eslint/typescript-estree": "4.28.4",
|
||||
"@typescript-eslint/scope-manager": "4.28.5",
|
||||
"@typescript-eslint/types": "4.28.5",
|
||||
"@typescript-eslint/typescript-estree": "4.28.5",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0"
|
||||
},
|
||||
@ -2676,13 +2676,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.4.tgz",
|
||||
"integrity": "sha512-4i0jq3C6n+og7/uCHiE6q5ssw87zVdpUj1k6VlVYMonE3ILdFApEzTWgppSRG4kVNB/5jxnH+gTeKLMNfUelQA==",
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.5.tgz",
|
||||
"integrity": "sha512-NPCOGhTnkXGMqTznqgVbA5LqVsnw+i3+XA1UKLnAb+MG1Y1rP4ZSK9GX0kJBmAZTMIktf+dTwXToT6kFwyimbw==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "4.28.4",
|
||||
"@typescript-eslint/types": "4.28.4",
|
||||
"@typescript-eslint/typescript-estree": "4.28.4",
|
||||
"@typescript-eslint/scope-manager": "4.28.5",
|
||||
"@typescript-eslint/types": "4.28.5",
|
||||
"@typescript-eslint/typescript-estree": "4.28.5",
|
||||
"debug": "^4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
@ -2702,12 +2702,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.4.tgz",
|
||||
"integrity": "sha512-ZJBNs4usViOmlyFMt9X9l+X0WAFcDH7EdSArGqpldXu7aeZxDAuAzHiMAeI+JpSefY2INHrXeqnha39FVqXb8w==",
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.5.tgz",
|
||||
"integrity": "sha512-PHLq6n9nTMrLYcVcIZ7v0VY1X7dK309NM8ya9oL/yG8syFINIMHxyr2GzGoBYUdv3NUfCOqtuqps0ZmcgnZTfQ==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "4.28.4",
|
||||
"@typescript-eslint/visitor-keys": "4.28.4"
|
||||
"@typescript-eslint/types": "4.28.5",
|
||||
"@typescript-eslint/visitor-keys": "4.28.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^8.10.0 || ^10.13.0 || >=11.10.1"
|
||||
@ -2718,9 +2718,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.4.tgz",
|
||||
"integrity": "sha512-3eap4QWxGqkYuEmVebUGULMskR6Cuoc/Wii0oSOddleP4EGx1tjLnZQ0ZP33YRoMDCs5O3j56RBV4g14T4jvww==",
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.5.tgz",
|
||||
"integrity": "sha512-MruOu4ZaDOLOhw4f/6iudyks/obuvvZUAHBDSW80Trnc5+ovmViLT2ZMDXhUV66ozcl6z0LJfKs1Usldgi/WCA==",
|
||||
"engines": {
|
||||
"node": "^8.10.0 || ^10.13.0 || >=11.10.1"
|
||||
},
|
||||
@ -2730,12 +2730,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.4.tgz",
|
||||
"integrity": "sha512-z7d8HK8XvCRyN2SNp+OXC2iZaF+O2BTquGhEYLKLx5k6p0r05ureUtgEfo5f6anLkhCxdHtCf6rPM1p4efHYDQ==",
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.5.tgz",
|
||||
"integrity": "sha512-FzJUKsBX8poCCdve7iV7ShirP8V+ys2t1fvamVeD1rWpiAnIm550a+BX/fmTHrjEpQJ7ZAn+Z7ZZwJjytk9rZw==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "4.28.4",
|
||||
"@typescript-eslint/visitor-keys": "4.28.4",
|
||||
"@typescript-eslint/types": "4.28.5",
|
||||
"@typescript-eslint/visitor-keys": "4.28.5",
|
||||
"debug": "^4.3.1",
|
||||
"globby": "^11.0.3",
|
||||
"is-glob": "^4.0.1",
|
||||
@ -2775,11 +2775,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.4.tgz",
|
||||
"integrity": "sha512-NIAXAdbz1XdOuzqkJHjNKXKj8QQ4cv5cxR/g0uQhCYf/6//XrmfpaYsM7PnBcNbfvTDLUkqQ5TPNm1sozDdTWg==",
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.5.tgz",
|
||||
"integrity": "sha512-dva/7Rr+EkxNWdJWau26xU/0slnFlkh88v3TsyTgRS/IIYFi5iIfpCFM4ikw0vQTFUR9FYSSyqgK4w64gsgxhg==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "4.28.4",
|
||||
"@typescript-eslint/types": "4.28.5",
|
||||
"eslint-visitor-keys": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -3343,9 +3343,9 @@
|
||||
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
|
||||
},
|
||||
"node_modules/chart.js": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.4.1.tgz",
|
||||
"integrity": "sha512-0R4mL7WiBcYoazIhrzSYnWcOw6RmrRn7Q4nKZNsBQZCBrlkZKodQbfeojCCo8eETPRCs1ZNTsAcZhIfyhyP61g=="
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.5.0.tgz",
|
||||
"integrity": "sha512-J1a4EAb1Gi/KbhwDRmoovHTRuqT8qdF0kZ4XgwxpGethJHUdDrkqyPYwke0a+BuvSeUxPf8Cos6AX2AB8H8GLA=="
|
||||
},
|
||||
"node_modules/chartjs-adapter-moment": {
|
||||
"version": "1.0.0",
|
||||
@ -3502,9 +3502,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/codemirror": {
|
||||
"version": "5.62.1",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.1.tgz",
|
||||
"integrity": "sha512-39ce8tHh/M9J+Epa90R5zMGg06pxVXc1+Y0SRR6eKaUjjzuj5iYkk7rHc2uU+FzvfsWYGEYKPFf0pBVBLmYXNQ=="
|
||||
"version": "5.62.2",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.2.tgz",
|
||||
"integrity": "sha512-tVFMUa4J3Q8JUd1KL9yQzQB0/BJt7ZYZujZmTPgo/54Lpuq3ez4C8x/ATUY/wv7b7X3AUq8o3Xd+2C5ZrCGWHw=="
|
||||
},
|
||||
"node_modules/collection-visit": {
|
||||
"version": "1.0.0",
|
||||
@ -6790,9 +6790,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "2.53.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.53.2.tgz",
|
||||
"integrity": "sha512-1CtEYuS5CRCzFZ7SNW5528SlDlk4VDXIRGwbm/2POQxA/G4+7/crIqJwkmnj8Q/74hGx4oVlNvh4E1CJQ5hZ6w==",
|
||||
"version": "2.54.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.54.0.tgz",
|
||||
"integrity": "sha512-RHzvstAVwm9A751NxWIbGPFXs3zL4qe/eYg+N7WwGtIXVLy1cK64MiU37+hXeFm1jqipK6DGgMi6Z2hhPuCC3A==",
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
@ -9716,13 +9716,13 @@
|
||||
}
|
||||
},
|
||||
"@sentry/browser": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.9.0.tgz",
|
||||
"integrity": "sha512-4JnEPcwoNs6JqeEd4wscBq+hxpotEJ0DJ4eOIsaNZIMyqEHXBHTXCk/gfrSsiZFrkHM4PgvUHOxaC0HcZ92oBA==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.10.0.tgz",
|
||||
"integrity": "sha512-H0Blgp8f8bomebkkGWIgxHVjabtQAlsKJDiFXBg7gIc75YcarRxwH0R3hMog1/h8mmv4CGGUsy5ljYW6jsNnvA==",
|
||||
"requires": {
|
||||
"@sentry/core": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/core": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -9734,14 +9734,14 @@
|
||||
}
|
||||
},
|
||||
"@sentry/core": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.9.0.tgz",
|
||||
"integrity": "sha512-oFX2qQcMLujCeIuCQGlhpTUIOXiU5n6V2lqDnvMXUV8gKpplBPalwdlR9bgbSi+VO8u7LjHR1IKM0RAPWgNHWw==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.10.0.tgz",
|
||||
"integrity": "sha512-5KlxHJlbD7AMo+b9pMGkjxUOfMILtsqCtGgI7DMvZNfEkdohO8QgUY+hPqr540kmwArFS91ipQYWhqzGaOhM3Q==",
|
||||
"requires": {
|
||||
"@sentry/hub": "6.9.0",
|
||||
"@sentry/minimal": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/hub": "6.10.0",
|
||||
"@sentry/minimal": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -9753,12 +9753,12 @@
|
||||
}
|
||||
},
|
||||
"@sentry/hub": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.9.0.tgz",
|
||||
"integrity": "sha512-5mors7ojbo7G85ZmoVPQBgFBMONAJwyZfV0LNLy14GenoaVNuxTPyvAQiJb1FYq+x6YZ3CvqGX6r74KRKQU87w==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.10.0.tgz",
|
||||
"integrity": "sha512-MV8wjhWiFAXZAhmj7Ef5QdBr2IF93u8xXiIo2J+dRZ7eVa4/ZszoUiDbhUcl/TPxczaw4oW2a6tINBNFLzXiig==",
|
||||
"requires": {
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -9770,12 +9770,12 @@
|
||||
}
|
||||
},
|
||||
"@sentry/minimal": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.9.0.tgz",
|
||||
"integrity": "sha512-GBZ6wG2Rc1wInYEl2BZTZc/t57O1Da876ifLsSPpEQAEnGWbqZWb8RLjZskH09ZIL/K4XCIDDi5ySzN8kFUWJw==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.10.0.tgz",
|
||||
"integrity": "sha512-yarm046UgUFIBoxqnBan2+BEgaO9KZCrLzsIsmALiQvpfW92K1lHurSawl5W6SR7wCYBnNn7CPvPE/BHFdy4YA==",
|
||||
"requires": {
|
||||
"@sentry/hub": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/hub": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -9787,14 +9787,14 @@
|
||||
}
|
||||
},
|
||||
"@sentry/tracing": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.9.0.tgz",
|
||||
"integrity": "sha512-gogVTypolhPazXr3Lue8HgzBg5Sy1cQpEp5Iq9LtECs+TlOlxJ+S+P+EIjEZ0f1AHVu706jr5cY2G2Shluli9g==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.10.0.tgz",
|
||||
"integrity": "sha512-jZj6Aaf8kU5wgyNXbAJHosHn8OOFdK14lgwYPb/AIDsY35g9a9ncTOqIOBp8X3KkmSR8lcBzAEyiUzCxAis2jA==",
|
||||
"requires": {
|
||||
"@sentry/hub": "6.9.0",
|
||||
"@sentry/minimal": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/hub": "6.10.0",
|
||||
"@sentry/minimal": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -9806,16 +9806,16 @@
|
||||
}
|
||||
},
|
||||
"@sentry/types": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.9.0.tgz",
|
||||
"integrity": "sha512-v52HJqLoLapEnqS2NdVtUXPvT+aezQgNXQkp8hiQ3RUdTm5cffwBVG7wlbpE6OsOOIZxd6p1zKylFkwCypiIIA=="
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.10.0.tgz",
|
||||
"integrity": "sha512-M7s0JFgG7/6/yNVYoPUbxzaXDhnzyIQYRRJJKRaTD77YO4MHvi4Ke8alBWqD5fer0cPIfcSkBqa9BLdqRqcMWw=="
|
||||
},
|
||||
"@sentry/utils": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.9.0.tgz",
|
||||
"integrity": "sha512-PimDr6KAi4cCp5hQZ8Az2/pDcdfhTu7WAU30Dd9MZwknpHSTmD4G6QvkdrB5er6kMMnNQOC7rMo6w/Do3m6X3w==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.10.0.tgz",
|
||||
"integrity": "sha512-F9OczOcZMFtazYVZ6LfRIe65/eOfQbiAedIKS0li4npuMz0jKYRbxrjd/U7oLiNQkPAp4/BujU4m1ZIwq6a+tg==",
|
||||
"requires": {
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -9972,12 +9972,12 @@
|
||||
"integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA=="
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.4.tgz",
|
||||
"integrity": "sha512-s1oY4RmYDlWMlcV0kKPBaADn46JirZzvvH7c2CtAqxCY96S538JRBAzt83RrfkDheV/+G/vWNK0zek+8TB3Gmw==",
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.5.tgz",
|
||||
"integrity": "sha512-m31cPEnbuCqXtEZQJOXAHsHvtoDi9OVaeL5wZnO2KZTnkvELk+u6J6jHg+NzvWQxk+87Zjbc4lJS4NHmgImz6Q==",
|
||||
"requires": {
|
||||
"@typescript-eslint/experimental-utils": "4.28.4",
|
||||
"@typescript-eslint/scope-manager": "4.28.4",
|
||||
"@typescript-eslint/experimental-utils": "4.28.5",
|
||||
"@typescript-eslint/scope-manager": "4.28.5",
|
||||
"debug": "^4.3.1",
|
||||
"functional-red-black-tree": "^1.0.1",
|
||||
"regexpp": "^3.1.0",
|
||||
@ -9986,14 +9986,14 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/experimental-utils": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.4.tgz",
|
||||
"integrity": "sha512-OglKWOQRWTCoqMSy6pm/kpinEIgdcXYceIcH3EKWUl4S8xhFtN34GQRaAvTIZB9DD94rW7d/U7tUg3SYeDFNHA==",
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.5.tgz",
|
||||
"integrity": "sha512-bGPLCOJAa+j49hsynTaAtQIWg6uZd8VLiPcyDe4QPULsvQwLHGLSGKKcBN8/lBxIX14F74UEMK2zNDI8r0okwA==",
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.7",
|
||||
"@typescript-eslint/scope-manager": "4.28.4",
|
||||
"@typescript-eslint/types": "4.28.4",
|
||||
"@typescript-eslint/typescript-estree": "4.28.4",
|
||||
"@typescript-eslint/scope-manager": "4.28.5",
|
||||
"@typescript-eslint/types": "4.28.5",
|
||||
"@typescript-eslint/typescript-estree": "4.28.5",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0"
|
||||
},
|
||||
@ -10009,37 +10009,37 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.4.tgz",
|
||||
"integrity": "sha512-4i0jq3C6n+og7/uCHiE6q5ssw87zVdpUj1k6VlVYMonE3ILdFApEzTWgppSRG4kVNB/5jxnH+gTeKLMNfUelQA==",
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.5.tgz",
|
||||
"integrity": "sha512-NPCOGhTnkXGMqTznqgVbA5LqVsnw+i3+XA1UKLnAb+MG1Y1rP4ZSK9GX0kJBmAZTMIktf+dTwXToT6kFwyimbw==",
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "4.28.4",
|
||||
"@typescript-eslint/types": "4.28.4",
|
||||
"@typescript-eslint/typescript-estree": "4.28.4",
|
||||
"@typescript-eslint/scope-manager": "4.28.5",
|
||||
"@typescript-eslint/types": "4.28.5",
|
||||
"@typescript-eslint/typescript-estree": "4.28.5",
|
||||
"debug": "^4.3.1"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.4.tgz",
|
||||
"integrity": "sha512-ZJBNs4usViOmlyFMt9X9l+X0WAFcDH7EdSArGqpldXu7aeZxDAuAzHiMAeI+JpSefY2INHrXeqnha39FVqXb8w==",
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.5.tgz",
|
||||
"integrity": "sha512-PHLq6n9nTMrLYcVcIZ7v0VY1X7dK309NM8ya9oL/yG8syFINIMHxyr2GzGoBYUdv3NUfCOqtuqps0ZmcgnZTfQ==",
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "4.28.4",
|
||||
"@typescript-eslint/visitor-keys": "4.28.4"
|
||||
"@typescript-eslint/types": "4.28.5",
|
||||
"@typescript-eslint/visitor-keys": "4.28.5"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.4.tgz",
|
||||
"integrity": "sha512-3eap4QWxGqkYuEmVebUGULMskR6Cuoc/Wii0oSOddleP4EGx1tjLnZQ0ZP33YRoMDCs5O3j56RBV4g14T4jvww=="
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.5.tgz",
|
||||
"integrity": "sha512-MruOu4ZaDOLOhw4f/6iudyks/obuvvZUAHBDSW80Trnc5+ovmViLT2ZMDXhUV66ozcl6z0LJfKs1Usldgi/WCA=="
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.4.tgz",
|
||||
"integrity": "sha512-z7d8HK8XvCRyN2SNp+OXC2iZaF+O2BTquGhEYLKLx5k6p0r05ureUtgEfo5f6anLkhCxdHtCf6rPM1p4efHYDQ==",
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.5.tgz",
|
||||
"integrity": "sha512-FzJUKsBX8poCCdve7iV7ShirP8V+ys2t1fvamVeD1rWpiAnIm550a+BX/fmTHrjEpQJ7ZAn+Z7ZZwJjytk9rZw==",
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "4.28.4",
|
||||
"@typescript-eslint/visitor-keys": "4.28.4",
|
||||
"@typescript-eslint/types": "4.28.5",
|
||||
"@typescript-eslint/visitor-keys": "4.28.5",
|
||||
"debug": "^4.3.1",
|
||||
"globby": "^11.0.3",
|
||||
"is-glob": "^4.0.1",
|
||||
@ -10063,11 +10063,11 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.4.tgz",
|
||||
"integrity": "sha512-NIAXAdbz1XdOuzqkJHjNKXKj8QQ4cv5cxR/g0uQhCYf/6//XrmfpaYsM7PnBcNbfvTDLUkqQ5TPNm1sozDdTWg==",
|
||||
"version": "4.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.5.tgz",
|
||||
"integrity": "sha512-dva/7Rr+EkxNWdJWau26xU/0slnFlkh88v3TsyTgRS/IIYFi5iIfpCFM4ikw0vQTFUR9FYSSyqgK4w64gsgxhg==",
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "4.28.4",
|
||||
"@typescript-eslint/types": "4.28.5",
|
||||
"eslint-visitor-keys": "^2.0.0"
|
||||
}
|
||||
},
|
||||
@ -10508,9 +10508,9 @@
|
||||
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
|
||||
},
|
||||
"chart.js": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.4.1.tgz",
|
||||
"integrity": "sha512-0R4mL7WiBcYoazIhrzSYnWcOw6RmrRn7Q4nKZNsBQZCBrlkZKodQbfeojCCo8eETPRCs1ZNTsAcZhIfyhyP61g=="
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.5.0.tgz",
|
||||
"integrity": "sha512-J1a4EAb1Gi/KbhwDRmoovHTRuqT8qdF0kZ4XgwxpGethJHUdDrkqyPYwke0a+BuvSeUxPf8Cos6AX2AB8H8GLA=="
|
||||
},
|
||||
"chartjs-adapter-moment": {
|
||||
"version": "1.0.0",
|
||||
@ -10636,9 +10636,9 @@
|
||||
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4="
|
||||
},
|
||||
"codemirror": {
|
||||
"version": "5.62.1",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.1.tgz",
|
||||
"integrity": "sha512-39ce8tHh/M9J+Epa90R5zMGg06pxVXc1+Y0SRR6eKaUjjzuj5iYkk7rHc2uU+FzvfsWYGEYKPFf0pBVBLmYXNQ=="
|
||||
"version": "5.62.2",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.2.tgz",
|
||||
"integrity": "sha512-tVFMUa4J3Q8JUd1KL9yQzQB0/BJt7ZYZujZmTPgo/54Lpuq3ez4C8x/ATUY/wv7b7X3AUq8o3Xd+2C5ZrCGWHw=="
|
||||
},
|
||||
"collection-visit": {
|
||||
"version": "1.0.0",
|
||||
@ -13241,9 +13241,9 @@
|
||||
}
|
||||
},
|
||||
"rollup": {
|
||||
"version": "2.53.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.53.2.tgz",
|
||||
"integrity": "sha512-1CtEYuS5CRCzFZ7SNW5528SlDlk4VDXIRGwbm/2POQxA/G4+7/crIqJwkmnj8Q/74hGx4oVlNvh4E1CJQ5hZ6w==",
|
||||
"version": "2.54.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.54.0.tgz",
|
||||
"integrity": "sha512-RHzvstAVwm9A751NxWIbGPFXs3zL4qe/eYg+N7WwGtIXVLy1cK64MiU37+hXeFm1jqipK6DGgMi6Z2hhPuCC3A==",
|
||||
"requires": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
|
@ -53,20 +53,20 @@
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-replace": "^3.0.0",
|
||||
"@rollup/plugin-typescript": "^8.2.3",
|
||||
"@sentry/browser": "^6.9.0",
|
||||
"@sentry/tracing": "^6.9.0",
|
||||
"@sentry/browser": "^6.10.0",
|
||||
"@sentry/tracing": "^6.10.0",
|
||||
"@types/chart.js": "^2.9.34",
|
||||
"@types/codemirror": "5.60.2",
|
||||
"@types/grecaptcha": "^3.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.28.4",
|
||||
"@typescript-eslint/parser": "^4.28.4",
|
||||
"@typescript-eslint/eslint-plugin": "^4.28.5",
|
||||
"@typescript-eslint/parser": "^4.28.5",
|
||||
"@webcomponents/webcomponentsjs": "^2.5.0",
|
||||
"authentik-api": "file:api",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"chart.js": "^3.4.1",
|
||||
"chart.js": "^3.5.0",
|
||||
"chartjs-adapter-moment": "^1.0.0",
|
||||
"codemirror": "^5.62.1",
|
||||
"codemirror": "^5.62.2",
|
||||
"construct-style-sheets-polyfill": "^2.4.16",
|
||||
"eslint": "^7.31.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
@ -77,7 +77,7 @@
|
||||
"lit-html": "^1.4.1",
|
||||
"moment": "^2.29.1",
|
||||
"rapidoc": "^9.0.0",
|
||||
"rollup": "^2.53.2",
|
||||
"rollup": "^2.54.0",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"rollup-plugin-cssimport": "^1.0.2",
|
||||
|
@ -7,6 +7,7 @@
|
||||
--ak-dark-background: #18191a;
|
||||
--ak-dark-background-darker: #000000;
|
||||
--ak-dark-background-light: #1c1e21;
|
||||
--ak-dark-background-light-ish: #212427;
|
||||
--ak-dark-background-lighter: #2b2e33;
|
||||
}
|
||||
|
||||
@ -127,7 +128,8 @@ body {
|
||||
.pf-c-page__main-section {
|
||||
--pf-c-page__main-section--BackgroundColor: var(--ak-dark-background);
|
||||
}
|
||||
.sidebar-trigger {
|
||||
.sidebar-trigger,
|
||||
.notification-trigger {
|
||||
background-color: var(--ak-dark-background-light) !important;
|
||||
}
|
||||
.pf-c-page__main-section.pf-m-light {
|
||||
@ -302,6 +304,9 @@ body {
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
/* notifications */
|
||||
.pf-c-drawer__panel {
|
||||
background-color: var(--ak-dark-background);
|
||||
}
|
||||
.pf-c-notification-drawer {
|
||||
--pf-c-notification-drawer--BackgroundColor: var(--ak-dark-background);
|
||||
}
|
||||
@ -309,6 +314,10 @@ body {
|
||||
background-color: var(--ak-dark-background-lighter);
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
.pf-c-notification-drawer__list-item {
|
||||
background-color: var(--ak-dark-background-light-ish);
|
||||
color: var(--ak-dark-foreground);
|
||||
}
|
||||
/* data list */
|
||||
.pf-c-data-list__item {
|
||||
--pf-c-data-list__item--BackgroundColor: var(--ak-dark-background-light);
|
||||
|
61
web/src/common/ws.ts
Normal file
61
web/src/common/ws.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { EVENT_WS_MESSAGE } from "../constants";
|
||||
import { MessageLevel } from "../elements/messages/Message";
|
||||
import { showMessage } from "../elements/messages/MessageContainer";
|
||||
|
||||
export interface WSMessage {
|
||||
message_type: string;
|
||||
}
|
||||
|
||||
export class WebsocketClient {
|
||||
|
||||
messageSocket?: WebSocket;
|
||||
retryDelay = 200;
|
||||
|
||||
constructor() {
|
||||
try {
|
||||
this.connect();
|
||||
} catch (error) {
|
||||
console.warn(`authentik/ws: failed to connect to ws ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
connect(): void {
|
||||
if (navigator.webdriver) return;
|
||||
const wsUrl = `${window.location.protocol.replace("http", "ws")}//${window.location.host
|
||||
}/ws/client/`;
|
||||
this.messageSocket = new WebSocket(wsUrl);
|
||||
this.messageSocket.addEventListener("open", () => {
|
||||
console.debug(`authentik/ws: connected to ${wsUrl}`);
|
||||
this.retryDelay = 200;
|
||||
});
|
||||
this.messageSocket.addEventListener("close", (e) => {
|
||||
console.debug(`authentik/ws: closed ws connection: ${e}`);
|
||||
if (this.retryDelay > 3000) {
|
||||
showMessage({
|
||||
level: MessageLevel.error,
|
||||
message: t`Connection error, reconnecting...`
|
||||
});
|
||||
}
|
||||
setTimeout(() => {
|
||||
console.debug(`authentik/ws: reconnecting ws in ${this.retryDelay}ms`);
|
||||
this.connect();
|
||||
}, this.retryDelay);
|
||||
this.retryDelay = this.retryDelay * 2;
|
||||
});
|
||||
this.messageSocket.addEventListener("message", (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(EVENT_WS_MESSAGE, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: data as WSMessage,
|
||||
})
|
||||
);
|
||||
});
|
||||
this.messageSocket.addEventListener("error", () => {
|
||||
this.retryDelay = this.retryDelay * 2;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -3,11 +3,16 @@ export const SUCCESS_CLASS = "pf-m-success";
|
||||
export const ERROR_CLASS = "pf-m-danger";
|
||||
export const PROGRESS_CLASS = "pf-m-in-progress";
|
||||
export const CURRENT_CLASS = "pf-m-current";
|
||||
export const VERSION = "2021.7.1-rc1";
|
||||
export const VERSION = "2021.7.1";
|
||||
export const PAGE_SIZE = 20;
|
||||
export const TITLE_DEFAULT = "authentik";
|
||||
export const ROUTE_SEPARATOR = ";";
|
||||
|
||||
export const EVENT_REFRESH = "ak-refresh";
|
||||
export const EVENT_NOTIFICATION_TOGGLE = "ak-notification-toggle";
|
||||
export const EVENT_SIDEBAR_TOGGLE = "ak-sidebar-toggle";
|
||||
export const EVENT_API_DRAWER_REFRESH = "ak-api-drawer-refresh";
|
||||
export const TITLE_DEFAULT = "authentik";
|
||||
export const ROUTE_SEPARATOR = ";";
|
||||
export const EVENT_WS_MESSAGE = "ak-ws-message";
|
||||
|
||||
export const WS_MSG_TYPE_MESSAGE = "message";
|
||||
export const WS_MSG_TYPE_REFRESH = "refresh";
|
||||
|
@ -4,8 +4,9 @@ import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import AKGlobal from "../authentik.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import { EVENT_SIDEBAR_TOGGLE, TITLE_DEFAULT } from "../constants";
|
||||
import { tenant } from "../api/Config";
|
||||
import { EVENT_NOTIFICATION_TOGGLE, EVENT_SIDEBAR_TOGGLE, TITLE_DEFAULT } from "../constants";
|
||||
import { DEFAULT_CONFIG, tenant } from "../api/Config";
|
||||
import { EventsApi } from "../../api/dist";
|
||||
|
||||
@customElement("ak-page-header")
|
||||
export class PageHeader extends LitElement {
|
||||
@ -16,6 +17,9 @@ export class PageHeader extends LitElement {
|
||||
@property({type: Boolean})
|
||||
iconImage = false
|
||||
|
||||
@property({type: Boolean})
|
||||
hasNotifications = false;
|
||||
|
||||
@property()
|
||||
set header(value: string) {
|
||||
tenant().then(tenant => {
|
||||
@ -44,12 +48,12 @@ export class PageHeader extends LitElement {
|
||||
flex-direction: row;
|
||||
min-height: 114px;
|
||||
}
|
||||
button.pf-c-button.pf-m-plain.sidebar-trigger {
|
||||
.pf-c-button.pf-m-plain {
|
||||
background-color: var(--pf-c-page__main-section--m-light--BackgroundColor);
|
||||
border-radius: 0px;
|
||||
}
|
||||
.pf-c-page__main-section {
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
@ -57,6 +61,13 @@ export class PageHeader extends LitElement {
|
||||
img.pf-icon {
|
||||
max-height: 24px;
|
||||
}
|
||||
.sidebar-trigger,
|
||||
.notification-trigger {
|
||||
font-size: 24px;
|
||||
}
|
||||
.notification-trigger.has-notifications {
|
||||
color: #2B9AF3;
|
||||
}
|
||||
`];
|
||||
}
|
||||
|
||||
@ -70,6 +81,16 @@ export class PageHeader extends LitElement {
|
||||
return html``;
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
new EventsApi(DEFAULT_CONFIG).eventsNotificationsList({
|
||||
seen: false,
|
||||
ordering: "-created",
|
||||
pageSize: 1,
|
||||
}).then(r => {
|
||||
this.hasNotifications = r.pagination.count > 0;
|
||||
});
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<button
|
||||
class="sidebar-trigger pf-c-button pf-m-plain"
|
||||
@ -92,7 +113,19 @@ export class PageHeader extends LitElement {
|
||||
${this.description ?
|
||||
html`<p>${this.description}</p>` : html``}
|
||||
</div>
|
||||
</section>`;
|
||||
</section>
|
||||
<button
|
||||
class="notification-trigger pf-c-button pf-m-plain ${this.hasNotifications ? "has-notifications" : ""}"
|
||||
@click=${() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_NOTIFICATION_TOGGLE, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
})
|
||||
);
|
||||
}}>
|
||||
<i class="fas fa-bell"></i>
|
||||
</button>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { LitElement, html, customElement, TemplateResult, property, CSSResult, css } from "lit-element";
|
||||
import "./Message";
|
||||
import { APIMessage, MessageLevel } from "./Message";
|
||||
import { APIMessage } from "./Message";
|
||||
import PFAlertGroup from "@patternfly/patternfly/components/AlertGroup/alert-group.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import { EVENT_WS_MESSAGE, WS_MSG_TYPE_MESSAGE } from "../../constants";
|
||||
import { WSMessage } from "../../common/ws";
|
||||
|
||||
export function showMessage(message: APIMessage): void {
|
||||
const container = document.querySelector<MessageContainer>("ak-message-container");
|
||||
@ -20,9 +21,6 @@ export class MessageContainer extends LitElement {
|
||||
@property({attribute: false})
|
||||
messages: APIMessage[] = [];
|
||||
|
||||
messageSocket?: WebSocket;
|
||||
retryDelay = 200;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFAlertGroup, css`
|
||||
/* Fix spacing between messages */
|
||||
@ -34,11 +32,10 @@ export class MessageContainer extends LitElement {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
try {
|
||||
this.connect();
|
||||
} catch (error) {
|
||||
console.warn(`authentik/messages: failed to connect to ws ${error}`);
|
||||
}
|
||||
this.addEventListener(EVENT_WS_MESSAGE, ((e: CustomEvent<WSMessage>) => {
|
||||
if (e.detail.message_type !== WS_MSG_TYPE_MESSAGE) return;
|
||||
this.addMessage(e.detail as unknown as APIMessage);
|
||||
}) as EventListener);
|
||||
}
|
||||
|
||||
// add a new message, but only if the message isn't currently shown.
|
||||
@ -49,40 +46,6 @@ export class MessageContainer extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
connect(): void {
|
||||
if (navigator.webdriver) return;
|
||||
const wsUrl = `${window.location.protocol.replace("http", "ws")}//${
|
||||
window.location.host
|
||||
}/ws/client/`;
|
||||
this.messageSocket = new WebSocket(wsUrl);
|
||||
this.messageSocket.addEventListener("open", () => {
|
||||
console.debug(`authentik/messages: connected to ${wsUrl}`);
|
||||
this.retryDelay = 200;
|
||||
});
|
||||
this.messageSocket.addEventListener("close", (e) => {
|
||||
console.debug(`authentik/messages: closed ws connection: ${e}`);
|
||||
if (this.retryDelay > 3000) {
|
||||
showMessage({
|
||||
level: MessageLevel.error,
|
||||
message: t`Connection error, reconnecting...`
|
||||
});
|
||||
}
|
||||
setTimeout(() => {
|
||||
console.debug(`authentik/messages: reconnecting ws in ${this.retryDelay}ms`);
|
||||
this.connect();
|
||||
}, this.retryDelay);
|
||||
this.retryDelay = this.retryDelay * 2;
|
||||
});
|
||||
this.messageSocket.addEventListener("message", (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
this.addMessage(data);
|
||||
this.requestUpdate();
|
||||
});
|
||||
this.messageSocket.addEventListener("error", () => {
|
||||
this.retryDelay = this.retryDelay * 2;
|
||||
});
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<ul class="pf-c-alert-group pf-m-toast">
|
||||
${this.messages.map((m) => {
|
||||
|
@ -10,6 +10,7 @@ import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import AKGlobal from "../../authentik.css";
|
||||
import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
import { EVENT_NOTIFICATION_TOGGLE } from "../../constants";
|
||||
import { ActionToLabel } from "../../pages/events/utils";
|
||||
|
||||
@customElement("ak-notification-drawer")
|
||||
export class NotificationDrawer extends LitElement {
|
||||
@ -23,6 +24,15 @@ export class NotificationDrawer extends LitElement {
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFButton, PFNotificationDrawer, PFContent, PFDropdown, AKGlobal].concat(
|
||||
css`
|
||||
.pf-c-notification-drawer__header {
|
||||
height: 114px;
|
||||
align-items: center;
|
||||
}
|
||||
.pf-c-notification-drawer__header-action,
|
||||
.pf-c-notification-drawer__header-action-close,
|
||||
.pf-c-notification-drawer__header-action-close > .pf-c-button.pf-m-plain {
|
||||
height: 100%;
|
||||
}
|
||||
.pf-c-notification-drawer__list-item-description {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
@ -55,13 +65,13 @@ export class NotificationDrawer extends LitElement {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return html`<li class="pf-c-notification-drawer__list-item pf-m-read">
|
||||
return html`<li class="pf-c-notification-drawer__list-item">
|
||||
<div class="pf-c-notification-drawer__list-item-header">
|
||||
<span class="pf-c-notification-drawer__list-item-header-icon ${level}">
|
||||
<i class="fas fa-info-circle" aria-hidden="true"></i>
|
||||
</span>
|
||||
<h2 class="pf-c-notification-drawer__list-item-header-title">
|
||||
${item.event?.action}
|
||||
${ActionToLabel(item.event?.action)}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="pf-c-notification-drawer__list-item-action">
|
||||
@ -95,12 +105,14 @@ export class NotificationDrawer extends LitElement {
|
||||
return html`<div class="pf-c-drawer__body pf-m-no-padding">
|
||||
<div class="pf-c-notification-drawer">
|
||||
<div class="pf-c-notification-drawer__header">
|
||||
<h1 class="pf-c-notification-drawer__header-title">
|
||||
${t`Notifications`}
|
||||
</h1>
|
||||
<span class="pf-c-notification-drawer__header-status">
|
||||
${t`${this.unread} unread`}
|
||||
</span>
|
||||
<div class="text">
|
||||
<h1 class="pf-c-notification-drawer__header-title">
|
||||
${t`Notifications`}
|
||||
</h1>
|
||||
<span>
|
||||
${t`${this.unread} unread`}
|
||||
</span>
|
||||
</div>
|
||||
<div class="pf-c-notification-drawer__header-action">
|
||||
<div class="pf-c-notification-drawer__header-action-close">
|
||||
<button
|
||||
|
@ -1,31 +0,0 @@
|
||||
import { CSSResult, customElement, html, LitElement, TemplateResult } from "lit-element";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css";
|
||||
import FA from "@fortawesome/fontawesome-free/css/fontawesome.css";
|
||||
import { EVENT_NOTIFICATION_TOGGLE } from "../../constants";
|
||||
|
||||
@customElement("ak-notification-trigger")
|
||||
export class NotificationRule extends LitElement {
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFDropdown, FA];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.addEventListener("click", () => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_NOTIFICATION_TOGGLE, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
// TODO: Show icon with red dot when unread notifications exist
|
||||
return html`<i class="fas fa-bell pf-c-dropdown__toggle-icon" aria-hidden="true"></i>`;
|
||||
}
|
||||
|
||||
}
|
@ -5,7 +5,6 @@ import { me } from "../../api/Users";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import "../notifications/NotificationTrigger";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
|
||||
@customElement("ak-sidebar-user")
|
||||
@ -39,8 +38,6 @@ export class SidebarUser extends LitElement {
|
||||
return html`<img class="pf-c-avatar" src="${ifDefined(u.user.avatar)}" alt="" />`;
|
||||
}), html``)}
|
||||
</a>
|
||||
<ak-notification-trigger class="pf-c-nav__link user-notifications">
|
||||
</ak-notification-trigger>
|
||||
<a href="/flows/-/default/invalidation/" class="pf-c-nav__link user-logout" id="logout">
|
||||
<i class="fas fa-sign-out-alt" aria-hidden="true"></i>
|
||||
</a>
|
||||
|
@ -33,6 +33,7 @@ import { until } from "lit-html/directives/until";
|
||||
import { PFSize } from "../elements/Spinner";
|
||||
import { TITLE_DEFAULT } from "../constants";
|
||||
import { configureSentry } from "../api/Sentry";
|
||||
import { WebsocketClient } from "../common/ws";
|
||||
|
||||
@customElement("ak-flow-executor")
|
||||
export class FlowExecutor extends LitElement implements StageHost {
|
||||
@ -48,6 +49,8 @@ export class FlowExecutor extends LitElement implements StageHost {
|
||||
@property({ attribute: false })
|
||||
tenant?: CurrentTenant;
|
||||
|
||||
ws: WebsocketClient;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFLogin, PFButton, PFTitle, PFList, PFBackgroundImage, AKGlobal].concat(css`
|
||||
.ak-loading {
|
||||
@ -75,6 +78,8 @@ export class FlowExecutor extends LitElement implements StageHost {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.ws = new WebsocketClient();
|
||||
this.ws.connect();
|
||||
this.flowSlug = window.location.pathname.split("/")[3];
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
|
||||
import AKGlobal from "../authentik.css";
|
||||
|
||||
import "../elements/router/RouterOutlet";
|
||||
import "../elements/messages/MessageContainer";
|
||||
@ -18,6 +19,7 @@ import { until } from "lit-html/directives/until";
|
||||
import { EVENT_NOTIFICATION_TOGGLE, EVENT_SIDEBAR_TOGGLE, VERSION } from "../constants";
|
||||
import { AdminApi } from "authentik-api";
|
||||
import { DEFAULT_CONFIG } from "../api/Config";
|
||||
import { WebsocketClient } from "../common/ws";
|
||||
|
||||
|
||||
@customElement("ak-interface-admin")
|
||||
@ -29,8 +31,10 @@ export class AdminInterface extends LitElement {
|
||||
@property({type: Boolean})
|
||||
notificationOpen = false;
|
||||
|
||||
ws: WebsocketClient;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFPage, PFButton, PFDrawer, css`
|
||||
return [PFBase, PFPage, PFButton, PFDrawer, AKGlobal, css`
|
||||
.pf-c-page__main, .pf-c-drawer__content, .pf-c-page__drawer {
|
||||
z-index: auto !important;
|
||||
}
|
||||
@ -39,6 +43,8 @@ export class AdminInterface extends LitElement {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.ws = new WebsocketClient();
|
||||
this.ws.connect();
|
||||
this.sidebarOpen = window.innerWidth >= 1280;
|
||||
window.addEventListener("resize", () => {
|
||||
this.sidebarOpen = window.innerWidth >= 1280;
|
||||
|
@ -208,6 +208,10 @@ msgstr "Application"
|
||||
msgid "Application Icon"
|
||||
msgstr "Application Icon"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Application authorized"
|
||||
msgstr "Application authorized"
|
||||
|
||||
#: src/flows/stages/consent/ConsentStage.ts
|
||||
msgid "Application requires following permissions:"
|
||||
msgstr "Application requires following permissions:"
|
||||
@ -676,6 +680,10 @@ msgstr "Confidential clients are capable of maintaining the confidentiality of t
|
||||
msgid "Configuration"
|
||||
msgstr "Configuration"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Configuration error"
|
||||
msgstr "Configuration error"
|
||||
|
||||
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
|
||||
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
@ -735,7 +743,7 @@ msgstr "Connect"
|
||||
msgid "Connected."
|
||||
msgstr "Connected."
|
||||
|
||||
#: src/elements/messages/MessageContainer.ts
|
||||
#: src/common/ws.ts
|
||||
msgid "Connection error, reconnecting..."
|
||||
msgstr "Connection error, reconnecting..."
|
||||
|
||||
@ -1222,6 +1230,10 @@ msgstr "Duo activation"
|
||||
msgid "Duo push-notifications"
|
||||
msgstr "Duo push-notifications"
|
||||
|
||||
#: src/pages/tenants/TenantForm.ts
|
||||
msgid "Duration after which events will be deleted from the database."
|
||||
msgstr "Duration after which events will be deleted from the database."
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
msgid "Each provider has a different issuer, based on the application slug."
|
||||
msgstr "Each provider has a different issuer, based on the application slug."
|
||||
@ -1299,6 +1311,10 @@ msgstr "Email address"
|
||||
msgid "Email info:"
|
||||
msgstr "Email info:"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Email sent"
|
||||
msgstr "Email sent"
|
||||
|
||||
#: src/pages/stages/prompt/PromptForm.ts
|
||||
msgid "Email: Text field with Email type."
|
||||
msgstr "Email: Text field with Email type."
|
||||
@ -1396,6 +1412,10 @@ msgstr "Event Log"
|
||||
msgid "Event info"
|
||||
msgstr "Event info"
|
||||
|
||||
#: src/pages/tenants/TenantForm.ts
|
||||
msgid "Event retention"
|
||||
msgstr "Event retention"
|
||||
|
||||
#: src/pages/events/EventInfoPage.ts
|
||||
msgid "Event {0}"
|
||||
msgstr "Event {0}"
|
||||
@ -1404,6 +1424,10 @@ msgstr "Event {0}"
|
||||
msgid "Events"
|
||||
msgstr "Events"
|
||||
|
||||
#: src/pages/admin-overview/cards/SystemStatusCard.ts
|
||||
msgid "Everything is ok."
|
||||
msgstr "Everything is ok."
|
||||
|
||||
#: src/pages/events/EventInfo.ts
|
||||
#: src/pages/events/EventInfo.ts
|
||||
#: src/pages/events/EventInfo.ts
|
||||
@ -1505,6 +1529,10 @@ msgstr "External host"
|
||||
msgid "Failed attempts before cancel"
|
||||
msgstr "Failed attempts before cancel"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Failed login"
|
||||
msgstr "Failed login"
|
||||
|
||||
#: src/pages/admin-overview/charts/LDAPSyncStatusChart.ts
|
||||
msgid "Failed sources"
|
||||
msgstr "Failed sources"
|
||||
@ -1650,6 +1678,10 @@ msgstr "Forgot username or password?"
|
||||
msgid "Form didn't return a promise for submitting"
|
||||
msgstr "Form didn't return a promise for submitting"
|
||||
|
||||
#: src/pages/tenants/TenantForm.ts
|
||||
msgid "Format: \"weeks=3;days=2;hours=3,seconds=2\"."
|
||||
msgstr "Format: \"weeks=3;days=2;hours=3,seconds=2\"."
|
||||
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
msgid "Forward auth (domain level)"
|
||||
msgstr "Forward auth (domain level)"
|
||||
@ -1674,6 +1706,10 @@ msgstr "From address"
|
||||
msgid "GID start number"
|
||||
msgstr "GID start number"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "General system exception"
|
||||
msgstr "General system exception"
|
||||
|
||||
#: src/pages/admin-overview/AdminOverviewPage.ts
|
||||
msgid "General system status"
|
||||
msgstr "General system status"
|
||||
@ -1741,6 +1777,10 @@ msgstr "HTTP-Basic Password Key"
|
||||
msgid "HTTP-Basic Username Key"
|
||||
msgstr "HTTP-Basic Username Key"
|
||||
|
||||
#: src/pages/admin-overview/cards/SystemStatusCard.ts
|
||||
msgid "HTTPS is not detected correctly"
|
||||
msgstr "HTTPS is not detected correctly"
|
||||
|
||||
#: src/pages/outposts/OutpostListPage.ts
|
||||
msgid "Health and Version"
|
||||
msgstr "Health and Version"
|
||||
@ -1857,6 +1897,14 @@ msgstr "If your authentik Instance is using a self-signed certificate, set this
|
||||
msgid "Impersonate"
|
||||
msgstr "Impersonate"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Impersonation ended"
|
||||
msgstr "Impersonation ended"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Impersonation started"
|
||||
msgstr "Impersonation started"
|
||||
|
||||
#: src/pages/flows/FlowListPage.ts
|
||||
#: src/pages/flows/FlowListPage.ts
|
||||
msgid "Import"
|
||||
@ -1918,6 +1966,10 @@ msgstr "Invalidation flow"
|
||||
msgid "Invitation"
|
||||
msgstr "Invitation"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Invitation used"
|
||||
msgstr "Invitation used"
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
#: src/pages/stages/invitation/InvitationListPage.ts
|
||||
msgid "Invitations"
|
||||
@ -2136,6 +2188,10 @@ msgstr "Local"
|
||||
msgid "Log the currently pending user in."
|
||||
msgstr "Log the currently pending user in."
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Login"
|
||||
msgstr "Login"
|
||||
|
||||
#: src/pages/sources/ldap/LDAPSourceForm.ts
|
||||
msgid "Login password is synced from LDAP into authentik automatically. Enable this option only to write password changes in authentik back to LDAP."
|
||||
msgstr "Login password is synced from LDAP into authentik automatically. Enable this option only to write password changes in authentik back to LDAP."
|
||||
@ -2157,6 +2213,10 @@ msgstr "Logins over the last 24 hours"
|
||||
msgid "Logo"
|
||||
msgstr "Logo"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Logout"
|
||||
msgstr "Logout"
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderViewPage.ts
|
||||
msgid "Logout URL"
|
||||
msgstr "Logout URL"
|
||||
@ -2247,6 +2307,18 @@ msgstr "Mode"
|
||||
msgid "Model Name"
|
||||
msgstr "Model Name"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Model created"
|
||||
msgstr "Model created"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Model deleted"
|
||||
msgstr "Model deleted"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Model updated"
|
||||
msgstr "Model updated"
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
msgid "Monitor"
|
||||
msgstr "Monitor"
|
||||
@ -2591,6 +2663,10 @@ msgstr "Optionally set this to your parent domain, if you want authentication an
|
||||
msgid "Order"
|
||||
msgstr "Order"
|
||||
|
||||
#: src/pages/tenants/TenantForm.ts
|
||||
msgid "Other global settings"
|
||||
msgstr "Other global settings"
|
||||
|
||||
#: src/pages/admin-overview/charts/OutpostStatusChart.ts
|
||||
msgid "Outdated outposts"
|
||||
msgstr "Outdated outposts"
|
||||
@ -2665,6 +2741,10 @@ msgstr "Password"
|
||||
msgid "Password field"
|
||||
msgstr "Password field"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Password set"
|
||||
msgstr "Password set"
|
||||
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
msgid "Password stage"
|
||||
msgstr "Password stage"
|
||||
@ -2730,6 +2810,14 @@ msgstr "Policy binding"
|
||||
msgid "Policy engine mode"
|
||||
msgstr "Policy engine mode"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Policy exception"
|
||||
msgstr "Policy exception"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Policy execution"
|
||||
msgstr "Policy execution"
|
||||
|
||||
#: src/pages/policies/BoundPoliciesList.ts
|
||||
msgid "Policy {0}"
|
||||
msgstr "Policy {0}"
|
||||
@ -2798,6 +2886,10 @@ msgstr "Prompts"
|
||||
msgid "Property Mapping"
|
||||
msgstr "Property Mapping"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Property Mapping exception"
|
||||
msgstr "Property Mapping exception"
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
#: src/pages/property-mappings/PropertyMappingListPage.ts
|
||||
msgid "Property Mappings"
|
||||
@ -3144,6 +3236,14 @@ msgstr "Search..."
|
||||
msgid "Secret key"
|
||||
msgstr "Secret key"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Secret was rotation"
|
||||
msgstr "Secret was rotation"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Secret was viewed"
|
||||
msgstr "Secret was viewed"
|
||||
|
||||
#: src/pages/events/EventInfo.ts
|
||||
msgid "Secret:"
|
||||
msgstr "Secret:"
|
||||
@ -3228,6 +3328,10 @@ msgstr "Separator: Static Separator Line"
|
||||
msgid "Server URI"
|
||||
msgstr "Server URI"
|
||||
|
||||
#: src/pages/admin-overview/cards/SystemStatusCard.ts
|
||||
msgid "Server and client are further than 5 seconds apart."
|
||||
msgstr "Server and client are further than 5 seconds apart."
|
||||
|
||||
#: src/pages/providers/ldap/LDAPProviderForm.ts
|
||||
msgid "Server name for which this provider's certificate is valid for."
|
||||
msgstr "Server name for which this provider's certificate is valid for."
|
||||
@ -3349,6 +3453,10 @@ msgstr "Something went wrong! Please try again later."
|
||||
msgid "Source"
|
||||
msgstr "Source"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Source linked"
|
||||
msgstr "Source linked"
|
||||
|
||||
#: src/pages/user-settings/settings/SourceSettingsOAuth.ts
|
||||
msgid "Source {0}"
|
||||
msgstr "Source {0}"
|
||||
@ -3768,6 +3876,10 @@ msgstr "Superuser-groups"
|
||||
msgid "Superusers"
|
||||
msgstr "Superusers"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Suspicious request"
|
||||
msgstr "Suspicious request"
|
||||
|
||||
#: src/pages/policies/password/PasswordPolicyForm.ts
|
||||
msgid "Symbol charset"
|
||||
msgstr "Symbol charset"
|
||||
@ -3797,6 +3909,18 @@ msgstr "System Overview"
|
||||
msgid "System Tasks"
|
||||
msgstr "System Tasks"
|
||||
|
||||
#: src/pages/admin-overview/AdminOverviewPage.ts
|
||||
msgid "System status"
|
||||
msgstr "System status"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "System task exception"
|
||||
msgstr "System task exception"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "System task execution"
|
||||
msgstr "System task execution"
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionDockerForm.ts
|
||||
msgid "TLS Authentication Certificate"
|
||||
msgstr "TLS Authentication Certificate"
|
||||
@ -3933,6 +4057,10 @@ msgstr "These policies control which users can access this application."
|
||||
msgid "This provider will behave like a transparent reverse-proxy, except requests must be authenticated. If your upstream application uses HTTPS, make sure to connect to the outpost using HTTPS as well."
|
||||
msgstr "This provider will behave like a transparent reverse-proxy, except requests must be authenticated. If your upstream application uses HTTPS, make sure to connect to the outpost using HTTPS as well."
|
||||
|
||||
#: src/pages/tenants/TenantForm.ts
|
||||
msgid "This setting only affects new Events, as the expiration is saved per-event."
|
||||
msgstr "This setting only affects new Events, as the expiration is saved per-event."
|
||||
|
||||
#: src/pages/stages/invitation/InvitationStageForm.ts
|
||||
msgid "This stage can be included in enrollment flows to accept invitations."
|
||||
msgstr "This stage can be included in enrollment flows to accept invitations."
|
||||
@ -4255,6 +4383,10 @@ msgstr "Update Token"
|
||||
msgid "Update User"
|
||||
msgstr "Update User"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Update available"
|
||||
msgstr "Update available"
|
||||
|
||||
#: src/pages/user-settings/UserDetailsPage.ts
|
||||
msgid "Update details"
|
||||
msgstr "Update details"
|
||||
@ -4375,6 +4507,10 @@ msgstr "User object filter"
|
||||
msgid "User password writeback"
|
||||
msgstr "User password writeback"
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "User was written to"
|
||||
msgstr "User was written to"
|
||||
|
||||
#: src/pages/policies/BoundPoliciesList.ts
|
||||
#: src/pages/users/UserViewPage.ts
|
||||
msgid "User {0}"
|
||||
@ -4492,6 +4628,8 @@ msgstr "Wait (max)"
|
||||
msgid "Wait (min)"
|
||||
msgstr "Wait (min)"
|
||||
|
||||
#: src/pages/admin-overview/cards/SystemStatusCard.ts
|
||||
#: src/pages/admin-overview/cards/SystemStatusCard.ts
|
||||
#: src/pages/events/RuleForm.ts
|
||||
#: src/pages/system-tasks/SystemTaskListPage.ts
|
||||
msgid "Warning"
|
||||
@ -4559,6 +4697,10 @@ msgstr "When selected, incoming assertion's Signatures will be validated against
|
||||
msgid "When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged."
|
||||
msgstr "When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged."
|
||||
|
||||
#: src/pages/tenants/TenantForm.ts
|
||||
msgid "When using an external logging solution for archiving, this can be set to \"minutes=5\"."
|
||||
msgstr "When using an external logging solution for archiving, this can be set to \"minutes=5\"."
|
||||
|
||||
#: src/flows/FlowExecutor.ts
|
||||
msgid "Whoops!"
|
||||
msgstr "Whoops!"
|
||||
|
@ -208,6 +208,10 @@ msgstr ""
|
||||
msgid "Application Icon"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Application authorized"
|
||||
msgstr ""
|
||||
|
||||
#: src/flows/stages/consent/ConsentStage.ts
|
||||
msgid "Application requires following permissions:"
|
||||
msgstr ""
|
||||
@ -670,6 +674,10 @@ msgstr ""
|
||||
msgid "Configuration"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Configuration error"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
|
||||
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
@ -729,7 +737,7 @@ msgstr ""
|
||||
msgid "Connected."
|
||||
msgstr ""
|
||||
|
||||
#: src/elements/messages/MessageContainer.ts
|
||||
#: src/common/ws.ts
|
||||
msgid "Connection error, reconnecting..."
|
||||
msgstr ""
|
||||
|
||||
@ -1214,6 +1222,10 @@ msgstr ""
|
||||
msgid "Duo push-notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/tenants/TenantForm.ts
|
||||
msgid "Duration after which events will be deleted from the database."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
msgid "Each provider has a different issuer, based on the application slug."
|
||||
msgstr ""
|
||||
@ -1291,6 +1303,10 @@ msgstr ""
|
||||
msgid "Email info:"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Email sent"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/prompt/PromptForm.ts
|
||||
msgid "Email: Text field with Email type."
|
||||
msgstr ""
|
||||
@ -1388,6 +1404,10 @@ msgstr ""
|
||||
msgid "Event info"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/tenants/TenantForm.ts
|
||||
msgid "Event retention"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/EventInfoPage.ts
|
||||
msgid "Event {0}"
|
||||
msgstr ""
|
||||
@ -1396,6 +1416,10 @@ msgstr ""
|
||||
msgid "Events"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/admin-overview/cards/SystemStatusCard.ts
|
||||
msgid "Everything is ok."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/EventInfo.ts
|
||||
#: src/pages/events/EventInfo.ts
|
||||
#: src/pages/events/EventInfo.ts
|
||||
@ -1497,6 +1521,10 @@ msgstr ""
|
||||
msgid "Failed attempts before cancel"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Failed login"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/admin-overview/charts/LDAPSyncStatusChart.ts
|
||||
msgid "Failed sources"
|
||||
msgstr ""
|
||||
@ -1642,6 +1670,10 @@ msgstr ""
|
||||
msgid "Form didn't return a promise for submitting"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/tenants/TenantForm.ts
|
||||
msgid "Format: \"weeks=3;days=2;hours=3,seconds=2\"."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/proxy/ProxyProviderForm.ts
|
||||
msgid "Forward auth (domain level)"
|
||||
msgstr ""
|
||||
@ -1666,6 +1698,10 @@ msgstr ""
|
||||
msgid "GID start number"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "General system exception"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/admin-overview/AdminOverviewPage.ts
|
||||
msgid "General system status"
|
||||
msgstr ""
|
||||
@ -1733,6 +1769,10 @@ msgstr ""
|
||||
msgid "HTTP-Basic Username Key"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/admin-overview/cards/SystemStatusCard.ts
|
||||
msgid "HTTPS is not detected correctly"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/OutpostListPage.ts
|
||||
msgid "Health and Version"
|
||||
msgstr ""
|
||||
@ -1849,6 +1889,14 @@ msgstr ""
|
||||
msgid "Impersonate"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Impersonation ended"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Impersonation started"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/flows/FlowListPage.ts
|
||||
#: src/pages/flows/FlowListPage.ts
|
||||
msgid "Import"
|
||||
@ -1910,6 +1958,10 @@ msgstr ""
|
||||
msgid "Invitation"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Invitation used"
|
||||
msgstr ""
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
#: src/pages/stages/invitation/InvitationListPage.ts
|
||||
msgid "Invitations"
|
||||
@ -2128,6 +2180,10 @@ msgstr ""
|
||||
msgid "Log the currently pending user in."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/sources/ldap/LDAPSourceForm.ts
|
||||
msgid "Login password is synced from LDAP into authentik automatically. Enable this option only to write password changes in authentik back to LDAP."
|
||||
msgstr ""
|
||||
@ -2149,6 +2205,10 @@ msgstr ""
|
||||
msgid "Logo"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Logout"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderViewPage.ts
|
||||
msgid "Logout URL"
|
||||
msgstr ""
|
||||
@ -2239,6 +2299,18 @@ msgstr ""
|
||||
msgid "Model Name"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Model created"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Model deleted"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Model updated"
|
||||
msgstr ""
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
msgid "Monitor"
|
||||
msgstr ""
|
||||
@ -2583,6 +2655,10 @@ msgstr ""
|
||||
msgid "Order"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/tenants/TenantForm.ts
|
||||
msgid "Other global settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/admin-overview/charts/OutpostStatusChart.ts
|
||||
msgid "Outdated outposts"
|
||||
msgstr ""
|
||||
@ -2657,6 +2733,10 @@ msgstr ""
|
||||
msgid "Password field"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Password set"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
msgid "Password stage"
|
||||
msgstr ""
|
||||
@ -2722,6 +2802,14 @@ msgstr ""
|
||||
msgid "Policy engine mode"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Policy exception"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Policy execution"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/policies/BoundPoliciesList.ts
|
||||
msgid "Policy {0}"
|
||||
msgstr ""
|
||||
@ -2790,6 +2878,10 @@ msgstr ""
|
||||
msgid "Property Mapping"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Property Mapping exception"
|
||||
msgstr ""
|
||||
|
||||
#: src/interfaces/AdminInterface.ts
|
||||
#: src/pages/property-mappings/PropertyMappingListPage.ts
|
||||
msgid "Property Mappings"
|
||||
@ -3136,6 +3228,14 @@ msgstr ""
|
||||
msgid "Secret key"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Secret was rotation"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Secret was viewed"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/EventInfo.ts
|
||||
msgid "Secret:"
|
||||
msgstr ""
|
||||
@ -3220,6 +3320,10 @@ msgstr ""
|
||||
msgid "Server URI"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/admin-overview/cards/SystemStatusCard.ts
|
||||
msgid "Server and client are further than 5 seconds apart."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/providers/ldap/LDAPProviderForm.ts
|
||||
msgid "Server name for which this provider's certificate is valid for."
|
||||
msgstr ""
|
||||
@ -3341,6 +3445,10 @@ msgstr ""
|
||||
msgid "Source"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Source linked"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/settings/SourceSettingsOAuth.ts
|
||||
msgid "Source {0}"
|
||||
msgstr ""
|
||||
@ -3760,6 +3868,10 @@ msgstr ""
|
||||
msgid "Superusers"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Suspicious request"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/policies/password/PasswordPolicyForm.ts
|
||||
msgid "Symbol charset"
|
||||
msgstr ""
|
||||
@ -3789,6 +3901,18 @@ msgstr ""
|
||||
msgid "System Tasks"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/admin-overview/AdminOverviewPage.ts
|
||||
msgid "System status"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "System task exception"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "System task execution"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/outposts/ServiceConnectionDockerForm.ts
|
||||
msgid "TLS Authentication Certificate"
|
||||
msgstr ""
|
||||
@ -3918,6 +4042,10 @@ msgstr ""
|
||||
msgid "This provider will behave like a transparent reverse-proxy, except requests must be authenticated. If your upstream application uses HTTPS, make sure to connect to the outpost using HTTPS as well."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/tenants/TenantForm.ts
|
||||
msgid "This setting only affects new Events, as the expiration is saved per-event."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/stages/invitation/InvitationStageForm.ts
|
||||
msgid "This stage can be included in enrollment flows to accept invitations."
|
||||
msgstr ""
|
||||
@ -4240,6 +4368,10 @@ msgstr ""
|
||||
msgid "Update User"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "Update available"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/user-settings/UserDetailsPage.ts
|
||||
msgid "Update details"
|
||||
msgstr ""
|
||||
@ -4360,6 +4492,10 @@ msgstr ""
|
||||
msgid "User password writeback"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/events/utils.ts
|
||||
msgid "User was written to"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/policies/BoundPoliciesList.ts
|
||||
#: src/pages/users/UserViewPage.ts
|
||||
msgid "User {0}"
|
||||
@ -4477,6 +4613,8 @@ msgstr ""
|
||||
msgid "Wait (min)"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/admin-overview/cards/SystemStatusCard.ts
|
||||
#: src/pages/admin-overview/cards/SystemStatusCard.ts
|
||||
#: src/pages/events/RuleForm.ts
|
||||
#: src/pages/system-tasks/SystemTaskListPage.ts
|
||||
msgid "Warning"
|
||||
@ -4544,6 +4682,10 @@ msgstr ""
|
||||
msgid "When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged."
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/tenants/TenantForm.ts
|
||||
msgid "When using an external logging solution for archiving, this can be set to \"minutes=5\"."
|
||||
msgstr ""
|
||||
|
||||
#: src/flows/FlowExecutor.ts
|
||||
msgid "Whoops!"
|
||||
msgstr ""
|
||||
|
@ -9,6 +9,7 @@ import "./cards/AdminStatusCard";
|
||||
import "./cards/BackupStatusCard";
|
||||
import "./cards/VersionStatusCard";
|
||||
import "./cards/WorkerStatusCard";
|
||||
import "./cards/SystemStatusCard";
|
||||
|
||||
import "./charts/FlowStatusChart";
|
||||
import "./charts/LDAPSyncStatusChart";
|
||||
@ -92,14 +93,18 @@ export class AdminOverviewPage extends LitElement {
|
||||
<ak-admin-status-version icon="pf-icon pf-icon-bundle" header=${t`Version`} headerLink="https://github.com/goauthentik/authentik/releases">
|
||||
</ak-admin-status-version>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-md pf-m-4-col-on-xl card-container">
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-2-col-on-md pf-m-2-col-on-xl card-container">
|
||||
<ak-admin-status-card-backup icon="fa fa-database" header=${t`Backup status`} headerLink="#/administration/system-tasks">
|
||||
</ak-admin-status-card-backup>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-md pf-m-4-col-on-xl card-container">
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-3-col-on-md pf-m-3-col-on-xl card-container">
|
||||
<ak-admin-status-card-workers icon="pf-icon pf-icon-server" header=${t`Workers`}>
|
||||
</ak-admin-status-card-workers>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-6-col pf-m-3-col-on-md pf-m-3-col-on-xl card-container">
|
||||
<ak-admin-status-system icon="pf-icon pf-icon-server" header=${t`System status`}>
|
||||
</ak-admin-status-system>
|
||||
</div>
|
||||
<div class="pf-l-grid__item pf-m-12-col row-divider">
|
||||
<hr>
|
||||
</div>
|
||||
|
46
web/src/pages/admin-overview/cards/SystemStatusCard.ts
Normal file
46
web/src/pages/admin-overview/cards/SystemStatusCard.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement, html, TemplateResult } from "lit-element";
|
||||
import { AdminApi, System } from "authentik-api";
|
||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||
import { AdminStatusCard, AdminStatus } from "./AdminStatusCard";
|
||||
|
||||
@customElement("ak-admin-status-system")
|
||||
export class SystemStatusCard extends AdminStatusCard<System> {
|
||||
|
||||
now?: Date;
|
||||
|
||||
header = "OK";
|
||||
|
||||
getPrimaryValue(): Promise<System> {
|
||||
this.now = new Date();
|
||||
return new AdminApi(DEFAULT_CONFIG).adminSystemRetrieve();
|
||||
}
|
||||
|
||||
getStatus(value: System): Promise<AdminStatus> {
|
||||
if (!value.httpIsSecure && document.location.protocol === "https:") {
|
||||
this.header = t`Warning`;
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-exclamation-triangle pf-m-warning",
|
||||
message: t`HTTPS is not detected correctly`,
|
||||
});
|
||||
}
|
||||
const timeDiff = value.serverTime.getTime() - (this.now || new Date()).getTime();
|
||||
console.log(`authentik/: timediff ${timeDiff}`);
|
||||
if (timeDiff > 5000 || timeDiff < -5000) {
|
||||
this.header = t`Warning`;
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-exclamation-triangle pf-m-warning",
|
||||
message: t`Server and client are further than 5 seconds apart.`,
|
||||
});
|
||||
}
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle pf-m-success",
|
||||
message: t`Everything is ok.`
|
||||
});
|
||||
}
|
||||
|
||||
renderValue(): TemplateResult {
|
||||
return html`${this.header}`;
|
||||
}
|
||||
|
||||
}
|
@ -42,7 +42,9 @@ export class PolicyStatusChart extends AKChart<PolicyMetrics> {
|
||||
})).pagination.count;
|
||||
this.centerText = count.toString();
|
||||
return {
|
||||
count: count - cached - unbound,
|
||||
// If we have more cache than total policies, only show that
|
||||
// otherwise show count without unbound
|
||||
count: cached >= count ? cached : count - unbound,
|
||||
cached: cached,
|
||||
unbound: unbound,
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ import { until } from "lit-html/directives/until";
|
||||
import "../../elements/forms/HorizontalFormElement";
|
||||
|
||||
@customElement("ak-application-check-access-form")
|
||||
export class ApplicationCheckAccessForm extends Form<number> {
|
||||
export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
|
||||
|
||||
@property({attribute: false})
|
||||
application!: Application;
|
||||
@ -23,14 +23,19 @@ export class ApplicationCheckAccessForm extends Form<number> {
|
||||
return t`Successfully sent test-request.`;
|
||||
}
|
||||
|
||||
send = (data: number): Promise<PolicyTestResult> => {
|
||||
this.request = data;
|
||||
send = (data: { forUser: number }): Promise<PolicyTestResult> => {
|
||||
this.request = data.forUser;
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationsCheckAccessRetrieve({
|
||||
slug: this.application?.slug,
|
||||
forUser: data,
|
||||
forUser: data.forUser,
|
||||
}).then(result => this.result = result);
|
||||
};
|
||||
|
||||
resetForm(): void {
|
||||
super.resetForm();
|
||||
this.result = undefined;
|
||||
}
|
||||
|
||||
renderResult(): TemplateResult {
|
||||
return html`
|
||||
<ak-form-element-horizontal
|
||||
|
@ -8,6 +8,7 @@ import { PAGE_SIZE } from "../../constants";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { TablePage } from "../../elements/table/TablePage";
|
||||
import "./EventInfo";
|
||||
import { ActionToLabel } from "./utils";
|
||||
|
||||
@customElement("ak-event-list")
|
||||
export class EventListPage extends TablePage<Event> {
|
||||
@ -51,7 +52,7 @@ export class EventListPage extends TablePage<Event> {
|
||||
|
||||
row(item: EventWithContext): TemplateResult[] {
|
||||
return [
|
||||
html`<div>${item.action}</div>
|
||||
html`<div>${ActionToLabel(item.action)}</div>
|
||||
<small>${item.app}</small>`,
|
||||
item.user?.username ?
|
||||
html`<a href="#/identity/users/${item.user.pk}">
|
||||
|
60
web/src/pages/events/utils.ts
Normal file
60
web/src/pages/events/utils.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { EventActions } from "authentik-api";
|
||||
|
||||
export function ActionToLabel(action?: EventActions): string {
|
||||
if (!action) return "";
|
||||
switch (action) {
|
||||
case EventActions.Login:
|
||||
return t`Login`;
|
||||
case EventActions.LoginFailed:
|
||||
return t`Failed login`;
|
||||
case EventActions.Logout:
|
||||
return t`Logout`;
|
||||
case EventActions.UserWrite:
|
||||
return t`User was written to`;
|
||||
case EventActions.SuspiciousRequest:
|
||||
return t`Suspicious request`;
|
||||
case EventActions.PasswordSet:
|
||||
return t`Password set`;
|
||||
case EventActions.SecretView:
|
||||
return t`Secret was viewed`;
|
||||
case EventActions.SecretRotate:
|
||||
return t`Secret was rotation`;
|
||||
case EventActions.InvitationUsed:
|
||||
return t`Invitation used`;
|
||||
case EventActions.AuthorizeApplication:
|
||||
return t`Application authorized`;
|
||||
case EventActions.SourceLinked:
|
||||
return t`Source linked`;
|
||||
case EventActions.ImpersonationStarted:
|
||||
return t`Impersonation started`;
|
||||
case EventActions.ImpersonationEnded:
|
||||
return t`Impersonation ended`;
|
||||
case EventActions.PolicyExecution:
|
||||
return t`Policy execution`;
|
||||
case EventActions.PolicyException:
|
||||
return t`Policy exception`;
|
||||
case EventActions.PropertyMappingException:
|
||||
return t`Property Mapping exception`;
|
||||
case EventActions.SystemTaskExecution:
|
||||
return t`System task execution`;
|
||||
case EventActions.SystemTaskException:
|
||||
return t`System task exception`;
|
||||
case EventActions.SystemException:
|
||||
return t`General system exception`;
|
||||
case EventActions.ConfigurationError:
|
||||
return t`Configuration error`;
|
||||
case EventActions.ModelCreated:
|
||||
return t`Model created`;
|
||||
case EventActions.ModelUpdated:
|
||||
return t`Model updated`;
|
||||
case EventActions.ModelDeleted:
|
||||
return t`Model deleted`;
|
||||
case EventActions.EmailSent:
|
||||
return t`Email sent`;
|
||||
case EventActions.UpdateAvailable:
|
||||
return t`Update available`;
|
||||
default:
|
||||
return action;
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ import { FlowsApi, ProvidersApi, LDAPProvider, CoreApi, FlowsInstancesListDesign
|
||||
import { t } from "@lingui/macro";
|
||||
import { customElement } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||
import { DEFAULT_CONFIG, tenant } from "../../../api/Config";
|
||||
import { ModelForm } from "../../../elements/forms/ModelForm";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
@ -53,12 +53,18 @@ export class LDAPProviderFormPage extends ModelForm<LDAPProvider, number> {
|
||||
?required=${true}
|
||||
name="authorizationFlow">
|
||||
<select class="pf-c-form-control">
|
||||
${until(new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authentication,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${this.instance?.authorizationFlow === flow.pk}>${flow.name} (${flow.slug})</option>`;
|
||||
${until(tenant().then(t => {
|
||||
new FlowsApi(DEFAULT_CONFIG).flowsInstancesList({
|
||||
ordering: "pk",
|
||||
designation: FlowsInstancesListDesignationEnum.Authentication,
|
||||
}).then(flows => {
|
||||
return flows.results.map(flow => {
|
||||
let selected = flow.pk === t.flowAuthentication;
|
||||
if (this.instance?.authorizationFlow === flow.pk) {
|
||||
selected = true;
|
||||
}
|
||||
return html`<option value=${ifDefined(flow.pk)} ?selected=${selected}>${flow.name} (${flow.slug})</option>`;
|
||||
});
|
||||
});
|
||||
}), html`<option>${t`Loading...`}</option>`)}
|
||||
</select>
|
||||
|
@ -162,6 +162,29 @@ export class TenantForm extends ModelForm<Tenant, string> {
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
<ak-form-group>
|
||||
<span slot="header">
|
||||
${t`Other global settings`}
|
||||
</span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${t`Event retention`}
|
||||
?required=${true}
|
||||
name="eventRetention">
|
||||
<input type="text" value="${first(this.instance?.eventRetention, "days=365")}" class="pf-c-form-control" required>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`Duration after which events will be deleted from the database.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`When using an external logging solution for archiving, this can be set to "minutes=5".`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${t`This setting only affects new Events, as the expiration is saved per-event.`}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">${t`Format: "weeks=3;days=2;hours=3,seconds=2".`}</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>
|
||||
</form>`;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ To create a local development setup for authentik, you need the following:
|
||||
|
||||
- Python 3.9
|
||||
- pipenv, which is used to manage dependencies, and can be installed with `pip install pipenv`
|
||||
- Go 1.16
|
||||
- PostgreSQL (any recent version will do)
|
||||
- Redis (any recent version will do)
|
||||
|
||||
@ -24,11 +25,13 @@ log_level: debug
|
||||
secret_key: "A long key you can generate with `pwgen 40 1` for example"
|
||||
```
|
||||
|
||||
Afterwards, you can start authentik by running `./manage.py runserver`. Generally speaking, authentik is a Django application.
|
||||
Afterwards, you can start authentik by running `make run`.
|
||||
|
||||
Generally speaking, authentik is a Django application, ran by gunicorn, proxied by a Go application. The Go application serves static files.
|
||||
|
||||
Most functions and classes have type-hints and docstrings, so it is recommended to install a Python Type-checking Extension in your IDE to navigate around the code.
|
||||
|
||||
Before committing code, run `make lint` to ensure your code is formatted well. This also requires `pyright`, which can be installed with npm.
|
||||
Before committing code, run `make lint` to ensure your code is formatted well. This also requires `pyright@1.1.136`, which can be installed with npm.
|
||||
|
||||
Run `make gen` to generate an updated OpenAPI document for any changes you made.
|
||||
|
||||
|
@ -12,11 +12,11 @@ This installation method is for test-setups and small-scale productive setups.
|
||||
|
||||
## Preparation
|
||||
|
||||
Download the latest `docker-compose.yml` from [here](https://raw.githubusercontent.com/goauthentik/authentik/version/2021.7.1-rc1/docker-compose.yml). Place it in a directory of your choice.
|
||||
Download the latest `docker-compose.yml` from [here](https://raw.githubusercontent.com/goauthentik/authentik/version/2021.7.1/docker-compose.yml). Place it in a directory of your choice.
|
||||
|
||||
To optionally enable error-reporting, run `echo AUTHENTIK_ERROR_REPORTING__ENABLED=true >> .env`
|
||||
|
||||
To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.7.1-rc1 >> .env`
|
||||
To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.7.1 >> .env`
|
||||
|
||||
If this is a fresh authentik install run the following commands to generate a password:
|
||||
|
||||
|
@ -11,7 +11,7 @@ version: "3.5"
|
||||
|
||||
services:
|
||||
authentik_proxy:
|
||||
image: ghcr.io/goauthentik/proxy:2021.7.1-rc1
|
||||
image: ghcr.io/goauthentik/proxy:2021.7.1
|
||||
ports:
|
||||
- 4180:4180
|
||||
- 4443:4443
|
||||
@ -21,7 +21,7 @@ services:
|
||||
AUTHENTIK_TOKEN: token-generated-by-authentik
|
||||
# Or, for the LDAP Outpost
|
||||
authentik_proxy:
|
||||
image: ghcr.io/goauthentik/ldap:2021.7.1-rc1
|
||||
image: ghcr.io/goauthentik/ldap:2021.7.1
|
||||
ports:
|
||||
- 389:3389
|
||||
environment:
|
||||
|
@ -14,7 +14,7 @@ metadata:
|
||||
app.kubernetes.io/instance: __OUTPOST_NAME__
|
||||
app.kubernetes.io/managed-by: goauthentik.io
|
||||
app.kubernetes.io/name: authentik-proxy
|
||||
app.kubernetes.io/version: 2021.7.1-rc1
|
||||
app.kubernetes.io/version: 2021.7.1
|
||||
name: authentik-outpost-api
|
||||
stringData:
|
||||
authentik_host: "__AUTHENTIK_URL__"
|
||||
@ -29,7 +29,7 @@ metadata:
|
||||
app.kubernetes.io/instance: __OUTPOST_NAME__
|
||||
app.kubernetes.io/managed-by: goauthentik.io
|
||||
app.kubernetes.io/name: authentik-proxy
|
||||
app.kubernetes.io/version: 2021.7.1-rc1
|
||||
app.kubernetes.io/version: 2021.7.1
|
||||
name: authentik-outpost
|
||||
spec:
|
||||
ports:
|
||||
@ -54,7 +54,7 @@ metadata:
|
||||
app.kubernetes.io/instance: __OUTPOST_NAME__
|
||||
app.kubernetes.io/managed-by: goauthentik.io
|
||||
app.kubernetes.io/name: authentik-proxy
|
||||
app.kubernetes.io/version: 2021.7.1-rc1
|
||||
app.kubernetes.io/version: 2021.7.1
|
||||
name: authentik-outpost
|
||||
spec:
|
||||
selector:
|
||||
@ -62,14 +62,14 @@ spec:
|
||||
app.kubernetes.io/instance: __OUTPOST_NAME__
|
||||
app.kubernetes.io/managed-by: goauthentik.io
|
||||
app.kubernetes.io/name: authentik-proxy
|
||||
app.kubernetes.io/version: 2021.7.1-rc1
|
||||
app.kubernetes.io/version: 2021.7.1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: __OUTPOST_NAME__
|
||||
app.kubernetes.io/managed-by: goauthentik.io
|
||||
app.kubernetes.io/name: authentik-proxy
|
||||
app.kubernetes.io/version: 2021.7.1-rc1
|
||||
app.kubernetes.io/version: 2021.7.1
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
@ -88,7 +88,7 @@ spec:
|
||||
secretKeyRef:
|
||||
key: authentik_host_insecure
|
||||
name: authentik-outpost-api
|
||||
image: ghcr.io/goauthentik/proxy:2021.7.1-rc1
|
||||
image: ghcr.io/goauthentik/proxy:2021.7.1
|
||||
name: proxy
|
||||
ports:
|
||||
- containerPort: 4180
|
||||
@ -110,7 +110,7 @@ metadata:
|
||||
app.kubernetes.io/instance: __OUTPOST_NAME__
|
||||
app.kubernetes.io/managed-by: goauthentik.io
|
||||
app.kubernetes.io/name: authentik-proxy
|
||||
app.kubernetes.io/version: 2021.7.1-rc1
|
||||
app.kubernetes.io/version: 2021.7.1
|
||||
name: authentik-outpost
|
||||
spec:
|
||||
rules:
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: LDAP Outpost
|
||||
title: LDAP Provider
|
||||
---
|
||||
|
||||
:::info
|
||||
@ -55,7 +55,7 @@ The following fields are current set for groups:
|
||||
- "group"
|
||||
- "goauthentik.io/ldap/group"
|
||||
|
||||
A virtual group is also created for each user, they have the same fields as groups but have an additional objectClass: `goauthentik.io/ldap/virtual-group`.
|
||||
A virtual group is also created for each user, they have the same fields as groups but have an additional objectClass: `goauthentik.io/ldap/virtual-group`.
|
||||
The virtual groups gidNumber is equal to the uidNumber of the user.
|
||||
|
||||
**Additionally**, for both users and (non-virtual) groups, any attributes you set are also present as LDAP Attributes.
|
@ -1,27 +0,0 @@
|
||||
---
|
||||
title: Proxy Provider
|
||||
---
|
||||
|
||||
:::info
|
||||
This provider is to be used in conjunction with [Outposts](../outposts/outposts.md)
|
||||
:::
|
||||
|
||||
This provider protects applications, which have no built-in support for OAuth2 or SAML. This is done by running a lightweight Reverse Proxy in front of the application, which authenticates the requests.
|
||||
|
||||
authentik Proxy is based on [oauth2_proxy](https://github.com/oauth2-proxy/oauth2-proxy), but has been integrated more tightly with authentik.
|
||||
|
||||
The Proxy these extra headers to the application:
|
||||
|
||||
| Header Name | Value |
|
||||
| ------------------------------ | --------------------------------------------------- |
|
||||
| X-Forwarded-User | The user's unique identifier (**not the username**) |
|
||||
| X-Forwarded-Email | The user's email address |
|
||||
| X-Forwarded-Preferred-Username | The user's username |
|
||||
| X-Auth-Username | The user's username |
|
||||
|
||||
Additionally, you can add more custom headers using `additionalHeaders` in the User or Group Properties, for example
|
||||
|
||||
```yaml
|
||||
additionalHeaders:
|
||||
X-additional-header: bar
|
||||
```
|
@ -32,6 +32,10 @@ is redirect to the outpost.
|
||||
|
||||
For domain level, you'd use the same domain as authentik.
|
||||
|
||||
:::info
|
||||
*example-outpost* is used as a placeholder for the outpost name.
|
||||
:::
|
||||
|
||||
## Nginx
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
@ -103,7 +107,7 @@ spec:
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: authentik-outpost-*uuid of the service generated by authentik*
|
||||
serviceName: authentik-outpost-example-outpost
|
||||
servicePort: 4180
|
||||
path: /akprox
|
||||
```
|
||||
@ -117,7 +121,7 @@ metadata:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/auth-url: https://*external host that you configured in authentik*/akprox/auth?nginx
|
||||
nginx.ingress.kubernetes.io/auth-signin: https://*external host that you configured in authentik*/akprox/start?rd=$escaped_request_uri
|
||||
nginx.ingress.kubernetes.io/auth-response-headers: X-Auth-Username,X-Forwarded-Email,X-Forwarded-Preferred-Username,X-Forwarded-User
|
||||
nginx.ingress.kubernetes.io/auth-response-headers: X-Auth-Username,X-Forwarded-Email,X-Forwarded-Preferred-Username,X-Forwarded-User,X-Auth-Groups
|
||||
nginx.ingress.kubernetes.io/auth-snippet: |
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
```
|
||||
@ -140,11 +144,12 @@ http:
|
||||
middlewares:
|
||||
authentik:
|
||||
forwardAuth:
|
||||
address: http://authentik-outpost-*uuid of the service generated by authentik*:4180/akprox/auth?traefik
|
||||
address: http://authentik-outpost-example-outpost:4180/akprox/auth?traefik
|
||||
trustForwardHeader: true
|
||||
authResponseHeaders:
|
||||
- Set-Cookie
|
||||
- X-Auth-Username
|
||||
- X-Auth-Groups
|
||||
- X-Forwarded-Email
|
||||
- X-Forwarded-Preferred-Username
|
||||
- X-Forwarded-User
|
||||
@ -211,7 +216,7 @@ services:
|
||||
traefik.http.routers.authentik.tls: true
|
||||
traefik.http.middlewares.authentik.forwardauth.address: http://authentik_proxy:4180/akprox/auth?traefik
|
||||
traefik.http.middlewares.authentik.forwardauth.trustForwardHeader: true
|
||||
traefik.http.middlewares.authentik.forwardauth.authResponseHeaders: Set-Cookie,X-Auth-Username,X-Forwarded-Email,X-Forwarded-Preferred-Username,X-Forwarded-User
|
||||
traefik.http.middlewares.authentik.forwardauth.authResponseHeaders: Set-Cookie,X-Auth-Username,X-Auth-Groups,X-Forwarded-Email,X-Forwarded-Preferred-Username,X-Forwarded-User
|
||||
restart: unless-stopped
|
||||
|
||||
whoami:
|
||||
@ -236,11 +241,12 @@ metadata:
|
||||
name: authentik
|
||||
spec:
|
||||
forwardAuth:
|
||||
address: http://authentik-outpost-*uuid of the service generated by authentik*:4180/akprox/auth?traefik
|
||||
address: http://authentik-outpost-example-outpost:4180/akprox/auth?traefik
|
||||
trustForwardHeader: true
|
||||
authResponseHeaders:
|
||||
- Set-Cookie
|
||||
- X-Auth-Username
|
||||
- X-Auth-Groups
|
||||
- X-Forwarded-Email
|
||||
- X-Forwarded-Preferred-Username
|
||||
- X-Forwarded-User
|
||||
@ -262,7 +268,7 @@ spec:
|
||||
priority: 15
|
||||
services:
|
||||
- kind: Service
|
||||
name: authentik-outpost-*uuid of the service generated by authentik*
|
||||
name: authentik-outpost-example-outpost
|
||||
port: 4180
|
||||
```
|
||||
</TabItem>
|
@ -1,11 +1,12 @@
|
||||
---
|
||||
title: Proxy Outpost
|
||||
title: Proxy provider
|
||||
---
|
||||
|
||||
The proxy outpost sets the following headers:
|
||||
|
||||
```
|
||||
X-Auth-Username: akadmin # The username of the currently logged in user
|
||||
X-Auth-Groups: foo|bar|baz # The groups the user is member of, separated by a pipe
|
||||
X-Forwarded-Email: root@localhost # The email address of the currently logged in user
|
||||
X-Forwarded-Preferred-Username: akadmin # The username of the currently logged in user
|
||||
X-Forwarded-User: 900347b8a29876b45ca6f75722635ecfedf0e931c6022e3a29a8aa13fb5516fb # The hashed identifier of the currently logged in user.
|
@ -13,7 +13,7 @@ This update brings a lot of big features, such as:
|
||||
|
||||
Due to this new OAuth2 Provider, the Application Gateway Provider, now simply called "Proxy Provider" has been revamped as well. The new authentik Proxy integrates more tightly with authentik via the new Outposts system. The new proxy also supports multiple applications per proxy instance, can configure TLS based on authentik Keypairs, and more.
|
||||
|
||||
See [Proxy](../providers/proxy.md)
|
||||
See [Proxy](../providers/proxy/proxy.md)
|
||||
|
||||
- Outpost System
|
||||
|
||||
|
@ -20,7 +20,7 @@ This feature is still in technical preview, so please report any Bugs you run in
|
||||
- Compatibility with forwardAuth/auth_request
|
||||
|
||||
The authentik proxy is now compatible with forwardAuth (traefik) / auth_request (nginx). All that is required is the latest version of the outpost,
|
||||
and the correct config from [here](../outposts/proxy/forward_auth.mdx).
|
||||
and the correct config from [here](../providers/proxy/forward_auth.mdx).
|
||||
|
||||
- Docker images for ARM
|
||||
|
||||
|
@ -32,6 +32,31 @@ slug: "2021.7"
|
||||
- root: subclass SessionMiddleware to set Secure and SameSite flag depending on context
|
||||
- web: fix error when showing error message of request
|
||||
|
||||
## Fixed in 2021.7.1-rc2
|
||||
|
||||
- core: add email filter for user
|
||||
- core: add group filter by member username and pk
|
||||
- core: broaden error catching for propertymappings
|
||||
- lib: fix outpost fake-ip not working, add tests
|
||||
- outpost: fix 100% CPU Usage when not connected to websocket
|
||||
- outposts: ensure outpost SAs always have permissions to fake IP
|
||||
- outposts: fix git hash not being set in outposts
|
||||
- outposts: save certificate fingerprint and check before re-fetching to cleanup logs
|
||||
- outposts/ldap: add tracing for LDAP bind and search
|
||||
- outposts/ldap: improve parsing of LDAP filters
|
||||
- outposts/ldap: optimise backend Search API requests
|
||||
- outposts/proxy: add X-Auth-Groups header to pass groups
|
||||
- providers/oauth2: handler PropertyMapping exceptions and create event
|
||||
- providers/saml: improve error handling for property mappings
|
||||
- sources/ldap: improve error handling for property mappings
|
||||
- web: fix icon flashing in header, fix notification header icon in dark mode
|
||||
- web: separate websocket connection from messages
|
||||
- web/admin: fix missing dark theme for notifications
|
||||
- web/admin: fix negative count for policies when more cached than total policies
|
||||
- web/admin: improve UI for notification toggle
|
||||
- website/docs: clear up outpost uuids
|
||||
- website/docs: remove duplicate proxy docs
|
||||
|
||||
## Upgrading
|
||||
|
||||
This release does not introduce any new requirements.
|
||||
|
194
website/package-lock.json
generated
194
website/package-lock.json
generated
@ -11,10 +11,10 @@
|
||||
"@docusaurus/plugin-client-redirects": "2.0.0-beta.3",
|
||||
"@docusaurus/preset-classic": "2.0.0-beta.3",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"@sentry/react": "^6.9.0",
|
||||
"@sentry/tracing": "^6.9.0",
|
||||
"@sentry/react": "^6.10.0",
|
||||
"@sentry/tracing": "^6.10.0",
|
||||
"clsx": "^1.1.1",
|
||||
"postcss": "^8.3.5",
|
||||
"postcss": "^8.3.6",
|
||||
"rapidoc": "^9.0.0",
|
||||
"react": "^17.0.2",
|
||||
"react-before-after-slider-component": "^0.7.4",
|
||||
@ -2606,13 +2606,13 @@
|
||||
"integrity": "sha512-15spi3V28QdevleWBNXE4pIls3nFZmBbUGrW9IVPwiQczuSb9n76TCB4bsk8TSel+I1OkHEdPhu5QKMfY6rQHA=="
|
||||
},
|
||||
"node_modules/@sentry/browser": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.9.0.tgz",
|
||||
"integrity": "sha512-4JnEPcwoNs6JqeEd4wscBq+hxpotEJ0DJ4eOIsaNZIMyqEHXBHTXCk/gfrSsiZFrkHM4PgvUHOxaC0HcZ92oBA==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.10.0.tgz",
|
||||
"integrity": "sha512-H0Blgp8f8bomebkkGWIgxHVjabtQAlsKJDiFXBg7gIc75YcarRxwH0R3hMog1/h8mmv4CGGUsy5ljYW6jsNnvA==",
|
||||
"dependencies": {
|
||||
"@sentry/core": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/core": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -2625,14 +2625,14 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/core": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.9.0.tgz",
|
||||
"integrity": "sha512-oFX2qQcMLujCeIuCQGlhpTUIOXiU5n6V2lqDnvMXUV8gKpplBPalwdlR9bgbSi+VO8u7LjHR1IKM0RAPWgNHWw==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.10.0.tgz",
|
||||
"integrity": "sha512-5KlxHJlbD7AMo+b9pMGkjxUOfMILtsqCtGgI7DMvZNfEkdohO8QgUY+hPqr540kmwArFS91ipQYWhqzGaOhM3Q==",
|
||||
"dependencies": {
|
||||
"@sentry/hub": "6.9.0",
|
||||
"@sentry/minimal": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/hub": "6.10.0",
|
||||
"@sentry/minimal": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -2645,12 +2645,12 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/hub": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.9.0.tgz",
|
||||
"integrity": "sha512-5mors7ojbo7G85ZmoVPQBgFBMONAJwyZfV0LNLy14GenoaVNuxTPyvAQiJb1FYq+x6YZ3CvqGX6r74KRKQU87w==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.10.0.tgz",
|
||||
"integrity": "sha512-MV8wjhWiFAXZAhmj7Ef5QdBr2IF93u8xXiIo2J+dRZ7eVa4/ZszoUiDbhUcl/TPxczaw4oW2a6tINBNFLzXiig==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -2663,12 +2663,12 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/minimal": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.9.0.tgz",
|
||||
"integrity": "sha512-GBZ6wG2Rc1wInYEl2BZTZc/t57O1Da876ifLsSPpEQAEnGWbqZWb8RLjZskH09ZIL/K4XCIDDi5ySzN8kFUWJw==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.10.0.tgz",
|
||||
"integrity": "sha512-yarm046UgUFIBoxqnBan2+BEgaO9KZCrLzsIsmALiQvpfW92K1lHurSawl5W6SR7wCYBnNn7CPvPE/BHFdy4YA==",
|
||||
"dependencies": {
|
||||
"@sentry/hub": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/hub": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -2681,14 +2681,14 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/react": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-6.9.0.tgz",
|
||||
"integrity": "sha512-ccMhpL+YHcq171EhSHU02IYh476mBjPfK1zq+vW2fJkaigg+mEqbOHnQV0Uu3zFYHGqVg4CZKZc6v92cvbBwEg==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-6.10.0.tgz",
|
||||
"integrity": "sha512-ICHAxMKaQ+3MimzsKQWKivjqJWCbc9ZJ071XoTkRgaOIBLFk8VAVWOldaxrLaWLQdPNT2OwVWnsZI7IvzQNW6w==",
|
||||
"dependencies": {
|
||||
"@sentry/browser": "6.9.0",
|
||||
"@sentry/minimal": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/browser": "6.10.0",
|
||||
"@sentry/minimal": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
@ -2705,14 +2705,14 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/tracing": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.9.0.tgz",
|
||||
"integrity": "sha512-gogVTypolhPazXr3Lue8HgzBg5Sy1cQpEp5Iq9LtECs+TlOlxJ+S+P+EIjEZ0f1AHVu706jr5cY2G2Shluli9g==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.10.0.tgz",
|
||||
"integrity": "sha512-jZj6Aaf8kU5wgyNXbAJHosHn8OOFdK14lgwYPb/AIDsY35g9a9ncTOqIOBp8X3KkmSR8lcBzAEyiUzCxAis2jA==",
|
||||
"dependencies": {
|
||||
"@sentry/hub": "6.9.0",
|
||||
"@sentry/minimal": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/hub": "6.10.0",
|
||||
"@sentry/minimal": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -2725,19 +2725,19 @@
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||
},
|
||||
"node_modules/@sentry/types": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.9.0.tgz",
|
||||
"integrity": "sha512-v52HJqLoLapEnqS2NdVtUXPvT+aezQgNXQkp8hiQ3RUdTm5cffwBVG7wlbpE6OsOOIZxd6p1zKylFkwCypiIIA==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.10.0.tgz",
|
||||
"integrity": "sha512-M7s0JFgG7/6/yNVYoPUbxzaXDhnzyIQYRRJJKRaTD77YO4MHvi4Ke8alBWqD5fer0cPIfcSkBqa9BLdqRqcMWw==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/utils": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.9.0.tgz",
|
||||
"integrity": "sha512-PimDr6KAi4cCp5hQZ8Az2/pDcdfhTu7WAU30Dd9MZwknpHSTmD4G6QvkdrB5er6kMMnNQOC7rMo6w/Do3m6X3w==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.10.0.tgz",
|
||||
"integrity": "sha512-F9OczOcZMFtazYVZ6LfRIe65/eOfQbiAedIKS0li4npuMz0jKYRbxrjd/U7oLiNQkPAp4/BujU4m1ZIwq6a+tg==",
|
||||
"dependencies": {
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"engines": {
|
||||
@ -10143,9 +10143,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.3.5",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.5.tgz",
|
||||
"integrity": "sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA==",
|
||||
"version": "8.3.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz",
|
||||
"integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==",
|
||||
"dependencies": {
|
||||
"colorette": "^1.2.2",
|
||||
"nanoid": "^3.1.23",
|
||||
@ -17460,13 +17460,13 @@
|
||||
"integrity": "sha512-15spi3V28QdevleWBNXE4pIls3nFZmBbUGrW9IVPwiQczuSb9n76TCB4bsk8TSel+I1OkHEdPhu5QKMfY6rQHA=="
|
||||
},
|
||||
"@sentry/browser": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.9.0.tgz",
|
||||
"integrity": "sha512-4JnEPcwoNs6JqeEd4wscBq+hxpotEJ0DJ4eOIsaNZIMyqEHXBHTXCk/gfrSsiZFrkHM4PgvUHOxaC0HcZ92oBA==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-6.10.0.tgz",
|
||||
"integrity": "sha512-H0Blgp8f8bomebkkGWIgxHVjabtQAlsKJDiFXBg7gIc75YcarRxwH0R3hMog1/h8mmv4CGGUsy5ljYW6jsNnvA==",
|
||||
"requires": {
|
||||
"@sentry/core": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/core": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -17478,14 +17478,14 @@
|
||||
}
|
||||
},
|
||||
"@sentry/core": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.9.0.tgz",
|
||||
"integrity": "sha512-oFX2qQcMLujCeIuCQGlhpTUIOXiU5n6V2lqDnvMXUV8gKpplBPalwdlR9bgbSi+VO8u7LjHR1IKM0RAPWgNHWw==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.10.0.tgz",
|
||||
"integrity": "sha512-5KlxHJlbD7AMo+b9pMGkjxUOfMILtsqCtGgI7DMvZNfEkdohO8QgUY+hPqr540kmwArFS91ipQYWhqzGaOhM3Q==",
|
||||
"requires": {
|
||||
"@sentry/hub": "6.9.0",
|
||||
"@sentry/minimal": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/hub": "6.10.0",
|
||||
"@sentry/minimal": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -17497,12 +17497,12 @@
|
||||
}
|
||||
},
|
||||
"@sentry/hub": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.9.0.tgz",
|
||||
"integrity": "sha512-5mors7ojbo7G85ZmoVPQBgFBMONAJwyZfV0LNLy14GenoaVNuxTPyvAQiJb1FYq+x6YZ3CvqGX6r74KRKQU87w==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.10.0.tgz",
|
||||
"integrity": "sha512-MV8wjhWiFAXZAhmj7Ef5QdBr2IF93u8xXiIo2J+dRZ7eVa4/ZszoUiDbhUcl/TPxczaw4oW2a6tINBNFLzXiig==",
|
||||
"requires": {
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -17514,12 +17514,12 @@
|
||||
}
|
||||
},
|
||||
"@sentry/minimal": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.9.0.tgz",
|
||||
"integrity": "sha512-GBZ6wG2Rc1wInYEl2BZTZc/t57O1Da876ifLsSPpEQAEnGWbqZWb8RLjZskH09ZIL/K4XCIDDi5ySzN8kFUWJw==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.10.0.tgz",
|
||||
"integrity": "sha512-yarm046UgUFIBoxqnBan2+BEgaO9KZCrLzsIsmALiQvpfW92K1lHurSawl5W6SR7wCYBnNn7CPvPE/BHFdy4YA==",
|
||||
"requires": {
|
||||
"@sentry/hub": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/hub": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -17531,14 +17531,14 @@
|
||||
}
|
||||
},
|
||||
"@sentry/react": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-6.9.0.tgz",
|
||||
"integrity": "sha512-ccMhpL+YHcq171EhSHU02IYh476mBjPfK1zq+vW2fJkaigg+mEqbOHnQV0Uu3zFYHGqVg4CZKZc6v92cvbBwEg==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/react/-/react-6.10.0.tgz",
|
||||
"integrity": "sha512-ICHAxMKaQ+3MimzsKQWKivjqJWCbc9ZJ071XoTkRgaOIBLFk8VAVWOldaxrLaWLQdPNT2OwVWnsZI7IvzQNW6w==",
|
||||
"requires": {
|
||||
"@sentry/browser": "6.9.0",
|
||||
"@sentry/minimal": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/browser": "6.10.0",
|
||||
"@sentry/minimal": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
@ -17551,14 +17551,14 @@
|
||||
}
|
||||
},
|
||||
"@sentry/tracing": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.9.0.tgz",
|
||||
"integrity": "sha512-gogVTypolhPazXr3Lue8HgzBg5Sy1cQpEp5Iq9LtECs+TlOlxJ+S+P+EIjEZ0f1AHVu706jr5cY2G2Shluli9g==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.10.0.tgz",
|
||||
"integrity": "sha512-jZj6Aaf8kU5wgyNXbAJHosHn8OOFdK14lgwYPb/AIDsY35g9a9ncTOqIOBp8X3KkmSR8lcBzAEyiUzCxAis2jA==",
|
||||
"requires": {
|
||||
"@sentry/hub": "6.9.0",
|
||||
"@sentry/minimal": "6.9.0",
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/utils": "6.9.0",
|
||||
"@sentry/hub": "6.10.0",
|
||||
"@sentry/minimal": "6.10.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"@sentry/utils": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -17570,16 +17570,16 @@
|
||||
}
|
||||
},
|
||||
"@sentry/types": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.9.0.tgz",
|
||||
"integrity": "sha512-v52HJqLoLapEnqS2NdVtUXPvT+aezQgNXQkp8hiQ3RUdTm5cffwBVG7wlbpE6OsOOIZxd6p1zKylFkwCypiIIA=="
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.10.0.tgz",
|
||||
"integrity": "sha512-M7s0JFgG7/6/yNVYoPUbxzaXDhnzyIQYRRJJKRaTD77YO4MHvi4Ke8alBWqD5fer0cPIfcSkBqa9BLdqRqcMWw=="
|
||||
},
|
||||
"@sentry/utils": {
|
||||
"version": "6.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.9.0.tgz",
|
||||
"integrity": "sha512-PimDr6KAi4cCp5hQZ8Az2/pDcdfhTu7WAU30Dd9MZwknpHSTmD4G6QvkdrB5er6kMMnNQOC7rMo6w/Do3m6X3w==",
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.10.0.tgz",
|
||||
"integrity": "sha512-F9OczOcZMFtazYVZ6LfRIe65/eOfQbiAedIKS0li4npuMz0jKYRbxrjd/U7oLiNQkPAp4/BujU4m1ZIwq6a+tg==",
|
||||
"requires": {
|
||||
"@sentry/types": "6.9.0",
|
||||
"@sentry/types": "6.10.0",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -23150,9 +23150,9 @@
|
||||
"integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.3.5",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.5.tgz",
|
||||
"integrity": "sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA==",
|
||||
"version": "8.3.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.3.6.tgz",
|
||||
"integrity": "sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==",
|
||||
"requires": {
|
||||
"colorette": "^1.2.2",
|
||||
"nanoid": "^3.1.23",
|
||||
|
@ -15,10 +15,10 @@
|
||||
"@docusaurus/plugin-client-redirects": "2.0.0-beta.3",
|
||||
"@docusaurus/preset-classic": "2.0.0-beta.3",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"@sentry/react": "^6.9.0",
|
||||
"@sentry/tracing": "^6.9.0",
|
||||
"@sentry/react": "^6.10.0",
|
||||
"@sentry/tracing": "^6.10.0",
|
||||
"clsx": "^1.1.1",
|
||||
"postcss": "^8.3.5",
|
||||
"postcss": "^8.3.6",
|
||||
"rapidoc": "^9.0.0",
|
||||
"react": "^17.0.2",
|
||||
"react-before-after-slider-component": "^0.7.4",
|
||||
|
@ -23,7 +23,19 @@ module.exports = {
|
||||
{
|
||||
type: "category",
|
||||
label: "Providers",
|
||||
items: ["providers/oauth2", "providers/saml", "providers/proxy"],
|
||||
items: [
|
||||
"providers/oauth2",
|
||||
"providers/saml",
|
||||
{
|
||||
type: "category",
|
||||
label: "Proxy",
|
||||
items: [
|
||||
"providers/proxy/proxy",
|
||||
"providers/proxy/forward_auth",
|
||||
],
|
||||
},
|
||||
"providers/ldap",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
@ -39,21 +51,6 @@ module.exports = {
|
||||
"outposts/manual-deploy-kubernetes",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Proxy",
|
||||
items: [
|
||||
"outposts/proxy/proxy",
|
||||
"outposts/proxy/forward_auth",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "LDAP",
|
||||
items: [
|
||||
"outposts/ldap/ldap",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Reference in New Issue
Block a user