Compare commits

...

193 Commits

Author SHA1 Message Date
9b9c0fe663 release: 2022.4.1 2022-04-12 22:07:34 +02:00
5a58f6ee64 providers/oauth2: remove test for non sa user
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-12 20:35:13 +02:00
da83c3af53 ci: disable translation schedule 2022-04-12 16:27:34 +00:00
e84b17d550 providers/oauth2: don't force service accounts for client_credentials flow
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-12 10:23:25 +02:00
b4fb0190a3 build(deps): bump @typescript-eslint/parser in /web (#2711) 2022-04-12 09:12:02 +02:00
bb52b95e5b build(deps): bump @typescript-eslint/eslint-plugin in /web (#2712) 2022-04-12 09:09:21 +02:00
a2b5d667af build(deps): bump @rollup/plugin-node-resolve in /web (#2710) 2022-04-12 09:09:10 +02:00
2df9c0479d build(deps): bump goauthentik.io/api/v3 (#2716) 2022-04-12 09:08:59 +02:00
5c673dc7bb build(deps): bump actions/setup-node from 3.1.0 to 3.1.1 (#2715) 2022-04-12 09:07:09 +02:00
da2dd7daf4 build(deps): bump twisted from 22.2.0 to 22.4.0 (#2713) 2022-04-12 09:06:54 +02:00
f2a80030d7 build(deps): bump sentry-sdk from 1.5.8 to 1.5.9 (#2714) 2022-04-12 09:06:40 +02:00
918183f472 core: compile backend translations (#2706)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-04-11 22:27:32 +02:00
9da439623b stages/authenticator_duo: fix bad request being sent to duo when calling enrollment_status outside a flow
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2666
2022-04-11 21:02:32 +02:00
957bb1c5ef core: make generated token length configurable
closes #2574

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-11 20:57:16 +02:00
677d46d7fd website/docs: prepare 2022.4
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-11 20:54:16 +02:00
5af7baf36c web: Update Web API Client version (#2705)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-04-11 20:46:22 +02:00
8b2ca822f5 core: compile backend translations (#2704)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-04-11 20:46:10 +02:00
2303a97bb9 core: add method to set key of token
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2574
2022-04-11 20:43:39 +02:00
8be04cc013 providers/oauth2: fix elliptic curve keys attempting to use EC256 instead of ES256
closes #2703

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-11 20:05:58 +02:00
9b6e47e6b8 outposts/ldap: fix panic in type conversion when value is nil
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-11 15:52:58 +02:00
677621989a build(deps): bump django from 4.0.3 to 4.0.4 (#2702)
Bumps [django](https://github.com/django/django) from 4.0.3 to 4.0.4.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/4.0.3...4.0.4)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-11 10:39:51 +02:00
0d5125db76 build(deps): bump @patternfly/patternfly from 4.183.1 to 4.185.1 in /web (#2699)
Bumps [@patternfly/patternfly](https://github.com/patternfly/patternfly) from 4.183.1 to 4.185.1.
- [Release notes](https://github.com/patternfly/patternfly/releases)
- [Changelog](https://github.com/patternfly/patternfly/blob/main/RELEASE-NOTES.md)
- [Commits](https://github.com/patternfly/patternfly/compare/prerelease-v4.183.1...prerelease-v4.185.1)

---
updated-dependencies:
- dependency-name: "@patternfly/patternfly"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-11 09:51:41 +02:00
ed88f6594c build(deps): bump actions/upload-artifact from 2 to 3 (#2700)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-11 09:51:28 +02:00
b1816f2101 build(deps): bump eslint from 8.12.0 to 8.13.0 in /web (#2698)
Bumps [eslint](https://github.com/eslint/eslint) from 8.12.0 to 8.13.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.12.0...v8.13.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-11 09:51:12 +02:00
fe60c26e11 build(deps): bump actions/setup-go from 2 to 3 (#2701)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 3.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-11 09:49:51 +02:00
cca33a74b6 core: fix error when checking generated users with no expiry
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-10 17:53:46 +02:00
f977bf61eb providers/oauth2: make exp optional on jwt client_credentials flow
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-10 17:25:35 +02:00
f8f8a9bbb9 providers/oauth2: give keypairs private key preference over certificate in client_credentials jwt flow
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-10 16:27:53 +02:00
7a44d5768a web/flows: fix focus being continuously forced to the input
closes #2692

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-08 18:26:39 +02:00
d9e4219d70 Revert "build(deps): bump @sentry/tracing from 6.19.4 to 6.19.6 in /web (#2690)"
This reverts commit dfe3394d4e.
2022-04-08 10:38:58 +02:00
6db5df1b31 Revert "build(deps): bump @sentry/browser from 6.19.4 to 6.19.6 in /web (#2689)"
This reverts commit 0e59ed62f5.
2022-04-08 10:38:56 +02:00
e64ca4ab04 core: fix lint error
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-08 10:10:30 +02:00
0e59ed62f5 build(deps): bump @sentry/browser from 6.19.4 to 6.19.6 in /web (#2689)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 6.19.4 to 6.19.6.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.19.4...6.19.6)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-08 10:08:34 +02:00
dfe3394d4e build(deps): bump @sentry/tracing from 6.19.4 to 6.19.6 in /web (#2690)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.19.4 to 6.19.6.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.19.4...6.19.6)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-08 10:07:16 +02:00
9d4fb8048c build(deps): bump github.com/go-ldap/ldap/v3 from 3.4.2 to 3.4.3 (#2691)
Bumps [github.com/go-ldap/ldap/v3](https://github.com/go-ldap/ldap) from 3.4.2 to 3.4.3.
- [Release notes](https://github.com/go-ldap/ldap/releases)
- [Commits](https://github.com/go-ldap/ldap/compare/v3.4.2...v3.4.3)

---
updated-dependencies:
- dependency-name: github.com/go-ldap/ldap/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-08 10:07:06 +02:00
a7a517733e web: enable rollup cache
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-07 21:06:18 +02:00
e2f0a76309 outposts: check if docker ports should be mapped before comparing ports
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-07 17:30:33 +02:00
07267ac425 build(deps): bump yaml from 1.10.2 to 2.0.0 in /web (#2682)
Bumps [yaml](https://github.com/eemeli/yaml) from 1.10.2 to 2.0.0.
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v1.10.2...v2.0.0)

---
updated-dependencies:
- dependency-name: yaml
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-07 10:02:52 +02:00
8fb7620004 build(deps): bump @babel/core from 7.17.8 to 7.17.9 in /web (#2681)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.17.8 to 7.17.9.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.17.9/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-07 10:02:35 +02:00
2ef85c4447 build(deps): bump lit from 2.2.1 to 2.2.2 in /web (#2683)
Bumps [lit](https://github.com/lit/lit/tree/HEAD/packages/lit) from 2.2.1 to 2.2.2.
- [Release notes](https://github.com/lit/lit/releases)
- [Changelog](https://github.com/lit/lit/blob/main/packages/lit/CHANGELOG.md)
- [Commits](https://github.com/lit/lit/commits/lit@2.2.2/packages/lit)

---
updated-dependencies:
- dependency-name: lit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-07 10:02:22 +02:00
c3174ac044 build(deps): bump @babel/plugin-proposal-decorators in /web (#2684)
Bumps [@babel/plugin-proposal-decorators](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-proposal-decorators) from 7.17.8 to 7.17.9.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.17.9/packages/babel-plugin-proposal-decorators)

---
updated-dependencies:
- dependency-name: "@babel/plugin-proposal-decorators"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-07 10:01:06 +02:00
952b48541c build(deps): bump goauthentik.io/api/v3 from 3.2022033.9 to 3.2022033.10 (#2687) 2022-04-07 09:54:55 +02:00
a97ffce5f9 build(deps): bump webauthn from 1.4.0 to 1.5.0 (#2685) 2022-04-07 09:54:38 +02:00
5d514bd8c4 build(deps-dev): bump pylint from 2.13.4 to 2.13.5 (#2686) 2022-04-07 09:53:42 +02:00
128234324d Translate /web/src/locales/en.po in zh-Hans (#2680)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh-Hans' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-04-06 17:38:54 +02:00
2d1bc2efcc Translate /web/src/locales/en.po in zh_CN (#2679)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh_CN' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-04-06 17:38:48 +02:00
2a1af96838 web/admin: fix linting
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-06 10:46:08 +02:00
a6674440e6 web: Update Web API Client version (#2678)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-04-06 10:45:34 +02:00
5861d41ad3 tenants: add tenant-level attributes, applied to users based on request
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-06 10:41:35 +02:00
fcd9c58a73 web/user: fix minor style issues
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-06 10:22:36 +02:00
4bf2878cf7 build(deps): bump @sentry/browser from 6.19.3 to 6.19.4 in /web (#2674) 2022-04-06 10:06:42 +02:00
79d508a020 build(deps): bump @sentry/tracing from 6.19.3 to 6.19.4 in /web (#2675) 2022-04-06 09:51:08 +02:00
03916b0b25 build(deps): bump codecov/codecov-action from 2 to 3 (#2676) 2022-04-06 09:50:54 +02:00
263964865c build(deps): bump celery from 5.2.5 to 5.2.6 (#2677) 2022-04-06 09:50:41 +02:00
21f92b4a65 website/docs: add docs for customisation
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-06 00:06:28 +02:00
e38d03b304 web/user: make more themable
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-05 23:47:15 +02:00
f2b540ed8a web/user: add column layouts
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-05 23:28:00 +02:00
79ad356d90 web/elements: insert spaces instead of tabs in codemirror
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-05 23:10:51 +02:00
e70490481d build(deps): bump minimist from 1.2.5 to 1.2.6 in /website (#2670)
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-05 10:28:52 +02:00
66ab9504e9 build(deps): bump minimist from 1.2.5 to 1.2.6 in /web (#2671)
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-05 10:28:31 +02:00
009173fe23 build(deps): bump @typescript-eslint/parser in /web (#2667)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.17.0 to 5.18.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.18.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-05 10:15:29 +02:00
75a5335f0f build(deps): bump @typescript-eslint/eslint-plugin in /web (#2668)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.17.0 to 5.18.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.18.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-05 10:11:15 +02:00
7a9452c66a build(deps): bump goauthentik.io/api/v3 from 3.2022033.8 to 3.2022033.9 (#2669)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2022033.8 to 3.2022033.9.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2022033.8...v3.2022033.9)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-05 10:11:04 +02:00
82a999f95d internal: disable HTML encoding in logrus
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-04 22:57:55 +02:00
0c2e9234bf ci: use native caching (#2665)
* ci: use native caching

* migrate to actions

* cleanup

* migrate more
2022-04-04 14:02:10 +02:00
964a3276a1 ci: bump cache 2022-04-04 09:58:05 +00:00
5185b027dc ci: remove unneeded tools 2022-04-04 09:28:57 +00:00
d690296120 ci: use go install 2022-04-04 09:21:33 +00:00
9252a1f9d3 ci: fix broken GHA cache again...
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-04 10:45:30 +02:00
fc6742a17e web/admin: trigger update when provider wizard finishes
closes #2599

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-04 10:38:30 +02:00
31546da796 web: Update Web API Client version (#2664)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-04-04 10:25:41 +02:00
4a6c46a5c9 root: upgrade to openapi generator v6
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-04 10:20:05 +02:00
20262f3f4b core: mark provider_obj as read_only
closes #2637

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-04 10:17:59 +02:00
dea61ef35e build(deps): bump moment from 2.29.1 to 2.29.2 in /web (#2657) 2022-04-04 09:49:11 +02:00
edda644e28 build(deps): bump react-before-after-slider-component in /website (#2656) 2022-04-04 09:48:44 +02:00
ee13ec1dca build(deps-dev): bump prettier from 2.6.1 to 2.6.2 in /website (#2658) 2022-04-04 09:48:18 +02:00
39bea1d5d0 build(deps): bump prettier from 2.6.1 to 2.6.2 in /web (#2659) 2022-04-04 09:48:07 +02:00
453dcd790f build(deps): bump actions/setup-node from 3.0.0 to 3.1.0 (#2661) 2022-04-04 09:47:44 +02:00
bb70e6c81d build(deps): bump celery from 5.2.3 to 5.2.5 (#2662) 2022-04-04 09:47:19 +02:00
4ff9db9d7e build(deps): bump goauthentik.io/api/v3 from 3.2022033.6 to 3.2022033.8 (#2663) 2022-04-04 09:47:04 +02:00
8b2e70d15d web/user: fix column size
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-03 18:19:14 +02:00
8e2f929933 Translate /web/src/locales/en.po in zh_CN (#2652)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh_CN' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-04-03 13:58:44 +02:00
ae2d86096b Translate /web/src/locales/en.po in zh-Hans (#2653)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh-Hans' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-04-03 13:58:33 +02:00
849c347e8c Translate /web/src/locales/en.po in zh_TW (#2654)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh_TW' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-04-03 13:58:24 +02:00
c974298836 Translate /web/src/locales/en.po in zh-Hant (#2655)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh-Hant' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-04-03 13:58:14 +02:00
b46eb7198b internal: handle log level not being set in config
closes #2650

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-03 13:23:55 +02:00
37db6764ab web: Update Web API Client version (#2649)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-04-02 23:12:00 +02:00
633296503d core: add grouping to applications (#2648)
* core: add grouping to applications

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* core: add new field to tests

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-02 23:08:58 +02:00
508cec2fd5 web: migrate dropdowns to wizards (#2633)
* web/admin: add basic wizards for providers

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web: add dark mode for wizard

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: migrate policies to wizard

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* start source

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* policies: sanitze_dict when returning log messages during tests

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* Revert "web/admin: migrate policies to wizard"

This reverts commit d8b7f62d3e.

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

# Conflicts:
#	web/src/locales/zh-Hans.po
#	web/src/locales/zh-Hant.po
#	web/src/locales/zh_TW.po

* web: rewrite wizard to be element based

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* further cleanup

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* update sources

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web: migrate property mappings

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* migrate stages

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* migrate misc dropdowns

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* migrate outpost integrations

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-02 19:48:17 +02:00
7a93614e4b policies: fix tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-02 18:31:02 +02:00
4f319eaa4f policies/dummy: bump to info to always get message
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-02 17:28:51 +02:00
86a8d00b3f policies: sanitze_dict when returning log messages during tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-02 17:15:44 +02:00
5fe8c1f3d7 policies: fix missing default for log_messages
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-02 16:44:49 +02:00
be91d893fb Translate /web/src/locales/en.po in zh_CN (#2644)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh_CN' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-04-02 16:39:38 +02:00
1fc6aa5a02 Translate /web/src/locales/en.po in zh-Hans (#2645)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh-Hans' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-04-02 16:39:30 +02:00
2256baced5 Translate /web/src/locales/en.po in zh-Hant (#2646)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh-Hant' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-04-02 16:39:22 +02:00
f2af904aeb Translate /web/src/locales/en.po in zh_TW (#2647)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh_TW' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-04-02 16:39:15 +02:00
030f612c38 web: Update Web API Client version (#2642)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-04-01 22:10:45 +02:00
d84ff2bbca policies: add policy log messages to test endpoints
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-04-01 22:07:35 +02:00
4be238018b providers/oauth2: pass scope and other parameters to access policy request context
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2641
2022-04-01 21:39:05 +02:00
71c6313c46 Revert "build(deps): bump @mdx-js/react from 1.6.22 to 2.1.1 in /website (#2634)" (#2639)
This reverts commit f7daa7723d.
2022-04-01 10:45:26 +02:00
f7daa7723d build(deps): bump @mdx-js/react from 1.6.22 to 2.1.1 in /website (#2634)
Bumps [@mdx-js/react](https://github.com/mdx-js/mdx/tree/HEAD/packages/react) from 1.6.22 to 2.1.1.
- [Release notes](https://github.com/mdx-js/mdx/releases)
- [Changelog](https://github.com/mdx-js/mdx/blob/main/changelog.md)
- [Commits](https://github.com/mdx-js/mdx/commits/2.1.1/packages/react)

---
updated-dependencies:
- dependency-name: "@mdx-js/react"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-01 10:06:30 +02:00
1ff35eef4c build(deps-dev): bump pylint from 2.13.3 to 2.13.4 (#2635)
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.13.3 to 2.13.4.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/main/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.13.3...v2.13.4)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-01 10:06:10 +02:00
743bb3e98f build(deps): bump goauthentik.io/api/v3 from 3.2022033.5 to 3.2022033.6 (#2636) 2022-04-01 10:04:25 +02:00
83c4d5393c web: Update Web API Client version (#2632)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-03-31 20:24:11 +02:00
99008252f8 providers/oauth2: fix verification_keys being required
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-31 20:19:13 +02:00
4cf00ed5cf lifecycle: fix password and hostname not properly quoted
#2623
2022-03-31 16:37:54 +00:00
8689444954 providers/oauth2: add password grant support (treated as client_credentials)
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-31 18:02:17 +02:00
4210f692ff Translate /web/src/locales/en.po in zh_CN (#2626)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh_CN' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-03-31 09:33:18 +02:00
85a3578092 Translate /web/src/locales/en.po in zh-Hant (#2627)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh-Hant' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-03-31 09:33:09 +02:00
6b05d44d1f Translate /web/src/locales/en.po in zh-Hans (#2628)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh-Hans' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-03-31 09:33:00 +02:00
49b221ed68 Translate /web/src/locales/en.po in zh_TW (#2629)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh_TW' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-03-31 09:32:48 +02:00
67b43c223c build(deps): bump goauthentik.io/api/v3 from 3.2022033.4 to 3.2022033.5 (#2630) 2022-03-31 08:59:31 +02:00
5f9dc4395a web: Update Web API Client version (#2625) 2022-03-31 01:03:08 +02:00
bb8af2f19b providers/oauth2: add client_assertion_type jwt bearer support (#2618) 2022-03-31 00:30:55 +02:00
996bd05ba6 api: fix API header auth not passing to next auth method
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-31 00:06:01 +02:00
ac03f5a97d website/docs: prepare 2022.4
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-30 23:43:36 +02:00
a1a64e25ee api: remove legacy http basic auth
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-30 23:39:08 +02:00
53851efacb Revert "web/elements: fix chart not rendering if update events happens before initial render"
This reverts commit f53343141e.
2022-03-30 10:36:50 +02:00
afea262e14 build(deps): bump @sentry/tracing from 6.19.2 to 6.19.3 in /web (#2619)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.19.2 to 6.19.3.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.19.2...6.19.3)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-30 10:36:05 +02:00
53f92f01da build(deps): bump @sentry/browser from 6.19.2 to 6.19.3 in /web (#2620)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 6.19.2 to 6.19.3.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.19.2...6.19.3)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-30 10:34:57 +02:00
a267686098 build(deps): bump @types/chart.js from 2.9.35 to 2.9.36 in /web (#2621)
Bumps [@types/chart.js](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/chart.js) from 2.9.35 to 2.9.36.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/chart.js)

---
updated-dependencies:
- dependency-name: "@types/chart.js"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-30 10:34:44 +02:00
9ee06b7d1f build(deps-dev): bump pylint from 2.13.2 to 2.13.3 (#2622)
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.13.2 to 2.13.3.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/main/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.13.2...v2.13.3)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-30 10:34:35 +02:00
f53343141e web/elements: fix chart not rendering if update events happens before initial render
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-29 11:33:40 +02:00
62250f4ec6 build(deps): bump @typescript-eslint/parser in /web (#2615)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.16.0 to 5.17.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.17.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-29 10:29:27 +02:00
485329130b build(deps): bump @typescript-eslint/eslint-plugin in /web (#2616)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.16.0 to 5.17.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.17.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-29 10:10:58 +02:00
6891c239e2 build(deps-dev): bump black from 22.1.0 to 22.3.0 (#2617)
Bumps [black](https://github.com/psf/black) from 22.1.0 to 22.3.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/22.1.0...22.3.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-29 10:10:34 +02:00
993c6472db crypto: only count discovered when cert was loaded successfully 2022-03-28 08:58:23 +00:00
123b0b2f05 core: fix pylint renamed variable 2022-03-28 08:58:13 +00:00
487b1e4f34 build(deps): bump @docusaurus/plugin-client-redirects in /website (#2606)
Bumps [@docusaurus/plugin-client-redirects](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-plugin-client-redirects) from 2.0.0-beta.17 to 2.0.0-beta.18.
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v2.0.0-beta.18/packages/docusaurus-plugin-client-redirects)

---
updated-dependencies:
- dependency-name: "@docusaurus/plugin-client-redirects"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-28 09:47:27 +02:00
b308cfa8d7 build(deps): bump @formatjs/intl-listformat from 6.5.2 to 6.5.3 in /web (#2603)
Bumps [@formatjs/intl-listformat](https://github.com/formatjs/formatjs) from 6.5.2 to 6.5.3.
- [Release notes](https://github.com/formatjs/formatjs/releases)
- [Commits](https://github.com/formatjs/formatjs/compare/@formatjs/intl-listformat@6.5.2...@formatjs/intl-listformat@6.5.3)

---
updated-dependencies:
- dependency-name: "@formatjs/intl-listformat"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-28 09:46:17 +02:00
839884c65c build(deps-dev): bump pylint from 2.12.2 to 2.13.2 (#2612)
Bumps [pylint](https://github.com/PyCQA/pylint) from 2.12.2 to 2.13.2.
- [Release notes](https://github.com/PyCQA/pylint/releases)
- [Changelog](https://github.com/PyCQA/pylint/blob/main/ChangeLog)
- [Commits](https://github.com/PyCQA/pylint/compare/v2.12.2...v2.13.2)

---
updated-dependencies:
- dependency-name: pylint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-28 09:45:21 +02:00
dc93f5d4c9 build(deps): bump @docusaurus/preset-classic in /website (#2602)
Bumps [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic) from 2.0.0-beta.17 to 2.0.0-beta.18.
- [Release notes](https://github.com/facebook/docusaurus/releases)
- [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/docusaurus/commits/v2.0.0-beta.18/packages/docusaurus-preset-classic)

---
updated-dependencies:
- dependency-name: "@docusaurus/preset-classic"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-28 09:45:02 +02:00
735af9aaad build(deps-dev): bump prettier from 2.6.0 to 2.6.1 in /website (#2604) 2022-03-28 09:43:03 +02:00
9c52ee585f build(deps): bump @rollup/plugin-commonjs from 21.0.2 to 21.0.3 in /web (#2605) 2022-03-28 09:42:40 +02:00
4c5f01020e build(deps): bump eslint from 8.11.0 to 8.12.0 in /web (#2607) 2022-03-28 09:42:28 +02:00
fc315eb8da build(deps): bump eslint-plugin-custom-elements in /web (#2608) 2022-03-28 09:41:52 +02:00
b90d8b14d6 build(deps): bump prettier from 2.6.0 to 2.6.1 in /web (#2609) 2022-03-28 09:41:39 +02:00
1af49c930c build(deps): bump goauthentik.io/api/v3 from 3.2022033.3 to 3.2022033.4 (#2610) 2022-03-28 09:41:17 +02:00
624ae67b50 build(deps-dev): bump pylint-django from 2.5.2 to 2.5.3 (#2611) 2022-03-28 09:41:04 +02:00
cd2fb49f9b web: update api
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-27 19:06:12 +02:00
3da531ede3 Translate /web/src/locales/en.po in zh_CN (#2595)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh_CN' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-03-27 15:52:24 +02:00
e3e4b2f818 Translate /web/src/locales/en.po in zh_TW (#2596)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh_TW' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-03-27 15:52:14 +02:00
98391da0d0 Translate /web/src/locales/en.po in zh-Hans (#2598)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh-Hans' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-03-27 15:52:00 +02:00
1555aed02f Translate /web/src/locales/en.po in zh-Hant (#2597)
translation completed for the source file '/web/src/locales/en.po'
on the 'zh-Hant' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2022-03-27 15:51:49 +02:00
7a01529511 root: lock openapi image
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-26 23:25:04 +01:00
bc3e6b3962 web/admin: fix missing protocols on generated nginx config
closes #2585

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-26 19:18:52 +01:00
7cbd5174f0 stages/invitation: fix tests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-26 19:12:22 +01:00
788cd401f6 build(deps): bump ansi-regex from 4.1.0 to 4.1.1 in /web (#2593)
Bumps [ansi-regex](https://github.com/chalk/ansi-regex) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/chalk/ansi-regex/releases)
- [Commits](https://github.com/chalk/ansi-regex/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: ansi-regex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-26 19:09:01 +01:00
bec8c8fe0a web: Update Web API Client version (#2592)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-03-26 18:51:29 +01:00
3184a64482 web: live-convert to slug in fields where only slugs are allowed
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-26 18:49:08 +01:00
c7a83e6182 stages/invitation: add invitation name
closes #2583

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-26 18:32:59 +01:00
933919c647 web/admin: allow editing of invitations
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2583
2022-03-26 18:21:14 +01:00
7d3841e85f build(deps): bump python (#2586)
Bumps python from 3.10.3-slim-bullseye to 3.10.4-slim-bullseye.

---
updated-dependencies:
- dependency-name: python
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-25 09:44:53 +01:00
21e54d803f build(deps): bump typescript from 4.6.2 to 4.6.3 in /web (#2587)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.6.2 to 4.6.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.6.2...v4.6.3)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-25 09:44:42 +01:00
883af97148 build(deps): bump @sentry/tracing from 6.19.1 to 6.19.2 in /web (#2578)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.19.1 to 6.19.2.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.19.1...6.19.2)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-24 10:15:07 +01:00
3184019996 build(deps): bump @sentry/browser from 6.19.1 to 6.19.2 in /web (#2579)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 6.19.1 to 6.19.2.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.19.1...6.19.2)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-24 10:14:08 +01:00
c0edaaf821 build(deps): bump peter-evans/create-pull-request from 3 to 4 (#2580)
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 3 to 4.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/v3...v4)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-24 10:13:58 +01:00
74ff9d04dd stages/prompt: set field default based on placeholder, fix duplicate fields
closes #2572

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-23 22:26:06 +01:00
969902f503 stages/prompt: filter rest_framework.fields.empty when field is not required
closes #2572

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-23 20:21:12 +01:00
04372e21dd events: handle types in event contexts
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2572
2022-03-23 19:49:55 +01:00
0c53650216 website/docs: terminology clear unneeded sentence. (#2577)
certificates: minor improvements
2022-03-23 17:38:36 +01:00
8e028c2feb website/docs: added missing client_id in client_credential grant type (#2576) 2022-03-23 15:07:09 +01:00
d75a864f0e providers/oauth2: map internal groups to GitHub teams in GHE OAuth emulation (#2497)
* providers/oauth2: impl `/user/teams` endpoint for Github OAuth2

This commit adds a functional `/user/teams` endpoint for the emulated Github OAuth2 service.
The teams a user is part of are based on the user's groups in Authentik.

* providers/oauth2: Move org template inside loop; Change slug to use Django slugify

* providers/oauth2: Remove placeholder replacement

* Possibly fix complaints from the linters

* Update github.py

* Change organization name

* Update github.py
2022-03-23 12:05:20 +01:00
81f3b133f6 web/user: dont show flow title in user settings
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2572
2022-03-23 10:18:53 +01:00
b887916f5b web/flows: fix styling for access denied stage
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2572
2022-03-23 10:16:43 +01:00
2a354aa64f website/integrations: Fix Kimai Group mapping attribute (#2565)
* Fix: Group mapping attribute

Missed it before, sorry.
Now it works properly

* Fix: branding

Co-authored-by: hexx.one <dominics.pc@gmail.com>
2022-03-23 10:08:31 +01:00
d9724e6885 build(deps): bump @fortawesome/fontawesome-free in /web (#2568) 2022-03-23 09:27:07 +01:00
d092e8e4bc build(deps): bump eslint-plugin-custom-elements in /web (#2569) 2022-03-23 09:26:51 +01:00
e5b8975459 build(deps): bump country-flag-icons from 1.4.24 to 1.4.25 in /web (#2570) 2022-03-23 09:26:30 +01:00
4f4784f4d8 build(deps): bump peter-evans/enable-pull-request-automerge from 1 to 2 (#2571) 2022-03-23 09:26:16 +01:00
51194cbf42 outposts/ldap: use backend group num_pk
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-22 23:54:50 +01:00
4d5a619cc0 build(deps): bump goauthentik.io/api/v3 from 3.2022033.2 to 3.2022033.3 (#2566)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2022033.2 to 3.2022033.3.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2022033.2...v3.2022033.3)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-22 23:47:43 +01:00
2314340823 build(deps): bump goauthentik.io/api/v3 from 3.2022033.1 to 3.2022033.2 (#2562)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2022033.1 to 3.2022033.2.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2022033.1...v3.2022033.2)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-22 21:41:54 +01:00
7c6b2c843b web: Update Web API Client version (#2563)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-03-22 21:41:44 +01:00
0c2b32da31 core: add num_pk to group for applications that need a numerical group id
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2497
2022-03-22 21:37:11 +01:00
9ad4c736f1 stages/email: allow overriding of destination email in plan context
closes #2445

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-22 21:19:34 +01:00
0c0b9ca84a build(deps): bump node-forge from 1.2.1 to 1.3.0 in /website (#2560)
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 1.2.1 to 1.3.0.
- [Release notes](https://github.com/digitalbazaar/forge/releases)
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/v1.2.1...v1.3.0)

---
updated-dependencies:
- dependency-name: node-forge
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-22 20:49:35 +01:00
4154b62565 stages/prompt: fix non-required fields not allowing blank values, add more tests
closes #2544

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-22 20:38:04 +01:00
5a07d4ec66 web/admin: fix typo in flow launch URL
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-22 20:20:38 +01:00
64b758c8fa web: Update Web API Client version (#2558)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-03-22 20:19:53 +01:00
a0e29d42a6 web/user: fix success popup showing after each flow stage
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

closes #2547

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-22 20:17:16 +01:00
0bbea79c64 root: update schema with latest drf-spectacular
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-22 20:15:15 +01:00
467ad29656 website/integrations: Add service documentation for "Kimai" timetracker (#2548)
* Add documentation for Kimai

* website: fix kimai application slug

* Add kimai sidebar integration

Co-authored-by: hexx.one <dominics.pc@gmail.com>
2022-03-22 18:20:04 +01:00
d2fc1226f8 website/integrations: Add service documentation for pgAdmin (#2556) 2022-03-22 18:19:27 +01:00
5c50a18b6f build(deps): bump @typescript-eslint/parser in /web (#2549)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 5.15.0 to 5.16.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.16.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-22 10:07:02 +01:00
75505a2077 build(deps): bump @sentry/tracing from 6.18.2 to 6.19.1 in /web (#2552)
Bumps [@sentry/tracing](https://github.com/getsentry/sentry-javascript) from 6.18.2 to 6.19.1.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.18.2...6.19.1)

---
updated-dependencies:
- dependency-name: "@sentry/tracing"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-22 10:05:45 +01:00
6d7525b5a1 build(deps): bump @typescript-eslint/eslint-plugin in /web (#2550)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.15.0 to 5.16.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.16.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-22 10:04:53 +01:00
4ca7ba427a build(deps): bump @sentry/browser from 6.18.2 to 6.19.1 in /web (#2551)
Bumps [@sentry/browser](https://github.com/getsentry/sentry-javascript) from 6.18.2 to 6.19.1.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.18.2...6.19.1)

---
updated-dependencies:
- dependency-name: "@sentry/browser"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-22 10:04:43 +01:00
740fafa86d build(deps): bump actions/cache from 2.1.7 to 3 (#2553)
Bumps [actions/cache](https://github.com/actions/cache) from 2.1.7 to 3.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](https://github.com/actions/cache/compare/v2.1.7...v3)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-22 10:04:35 +01:00
4b80f52e11 build(deps): bump drf-spectacular from 0.21.2 to 0.22.0 (#2554)
Bumps [drf-spectacular](https://github.com/tfranzel/drf-spectacular) from 0.21.2 to 0.22.0.
- [Release notes](https://github.com/tfranzel/drf-spectacular/releases)
- [Changelog](https://github.com/tfranzel/drf-spectacular/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/tfranzel/drf-spectacular/compare/0.21.2...0.22.0)

---
updated-dependencies:
- dependency-name: drf-spectacular
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-22 10:04:25 +01:00
7ae2bdc35f build(deps): bump goauthentik.io/api/v3 from 3.2022032.1 to 3.2022033.1 (#2555)
Bumps [goauthentik.io/api/v3](https://github.com/goauthentik/client-go) from 3.2022032.1 to 3.2022033.1.
- [Release notes](https://github.com/goauthentik/client-go/releases)
- [Commits](https://github.com/goauthentik/client-go/compare/v3.2022032.1...v3.2022033.1)

---
updated-dependencies:
- dependency-name: goauthentik.io/api/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-22 10:03:43 +01:00
34473903dd web: Update Web API Client version (#2545)
Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: BeryJu <BeryJu@users.noreply.github.com>
2022-03-22 00:20:25 +01:00
185 changed files with 9285 additions and 5082 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2022.3.3
current_version = 2022.4.1
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*)

49
.github/actions/docker-setup/action.yml vendored Normal file
View File

@ -0,0 +1,49 @@
name: 'Prepare docker environment variables'
description: 'Prepare docker environment variables'
outputs:
shouldBuild:
description: "Whether to build image or not"
value: ${{ steps.ev.outputs.shouldBuild }}
branchName:
description: "Branch name"
value: ${{ steps.ev.outputs.branchName }}
branchNameContainer:
description: "Branch name (for containers)"
value: ${{ steps.ev.outputs.branchNameContainer }}
timestamp:
description: "Timestamp"
value: ${{ steps.ev.outputs.timestamp }}
sha:
description: "sha"
value: ${{ steps.ev.outputs.sha }}
runs:
using: "composite"
steps:
- name: Generate config
id: ev
shell: python
run: |
"""Helper script to get the actual branch name, docker safe"""
import os
from time import time
env_pr_branch = "GITHUB_HEAD_REF"
default_branch = "GITHUB_REF"
sha = "GITHUB_SHA"
branch_name = os.environ[default_branch]
if os.environ.get(env_pr_branch, "") != "":
branch_name = os.environ[env_pr_branch]
should_build = str(os.environ.get("DOCKER_USERNAME", "") != "").lower()
print("##[set-output name=branchName]%s" % branch_name)
print(
"##[set-output name=branchNameContainer]%s"
% branch_name.replace("refs/heads/", "").replace("/", "-")
)
print("##[set-output name=timestamp]%s" % int(time()))
print("##[set-output name=sha]%s" % os.environ[sha])
print("##[set-output name=shouldBuild]%s" % should_build)

45
.github/actions/setup/action.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: 'Setup authentik testing environemnt'
description: 'Setup authentik testing environemnt'
runs:
using: "composite"
steps:
- name: Install poetry
shell: bash
run: |
pipx install poetry || true
sudo apt update
sudo apt install -y libxmlsec1-dev pkg-config gettext
- name: Setup python and restore poetry
uses: actions/setup-python@v3
with:
python-version: '3.10'
cache: 'poetry'
- name: Setup node
uses: actions/setup-node@v3.1.0
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Setup dependencies
shell: bash
run: |
docker-compose -f .github/actions/setup/docker-compose.yml up -d
poetry env use python3.10
poetry install
npm install -g pyright@1.1.136
- name: Generate config
shell: poetry run python {0}
run: |
from authentik.lib.generators import generate_id
from yaml import safe_dump
with open("local.env.yml", "w") as _config:
safe_dump(
{
"log_level": "debug",
"secret_key": generate_id(),
},
_config,
default_flow_style=False,
)

View File

@ -32,35 +32,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- uses: actions/setup-node@v3.0.0
with:
node-version: '16'
- id: cache-poetry
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}
run: scripts/ci_prepare.sh
- name: Setup authentik env
uses: ./.github/actions/setup
- name: run job
run: poetry run make ci-${{ matrix.job }}
test-migrations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- id: cache-poetry
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}
run: scripts/ci_prepare.sh
- name: Setup authentik env
uses: ./.github/actions/setup
- name: run migrations
run: poetry run python -m lifecycle.migrate
test-migrations-from-stable:
@ -69,17 +50,8 @@ jobs:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-python@v3
- name: prepare variables
id: ev
run: |
python ./scripts/gh_env.py
sudo pip install -U pipenv
- id: cache-poetry
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: Setup authentik env
uses: ./.github/actions/setup
- name: checkout stable
run: |
# Copy current, latest config to local
@ -89,13 +61,8 @@ jobs:
git checkout $(git describe --abbrev=0 --match 'version/*')
rm -rf .github/ scripts/
mv ../.github ../scripts .
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}
run: |
scripts/ci_prepare.sh
# install anyways since stable will have different dependencies
poetry install
- name: Setup authentik env (ensure stable deps are installed)
uses: ./.github/actions/setup
- name: run migrations to stable
run: poetry run python -m lifecycle.migrate
- name: checkout current code
@ -103,28 +70,19 @@ jobs:
set -x
git fetch
git reset --hard HEAD
git clean -d -fx .
git checkout $GITHUB_SHA
poetry install
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}
run: scripts/ci_prepare.sh
- name: Setup authentik env (ensure latest deps are installed)
uses: ./.github/actions/setup
- name: migrate to latest
run: poetry run python -m lifecycle.migrate
test-unittest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- id: cache-poetry
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}
run: scripts/ci_prepare.sh
- name: Setup authentik env
uses: ./.github/actions/setup
- uses: testspace-com/setup-testspace@v1
with:
domain: ${{github.repository_owner}}
@ -137,21 +95,13 @@ jobs:
run: |
testspace [unittest]unittest.xml --link=codecov
- if: ${{ always() }}
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v3
test-integration:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- id: cache-poetry
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}
run: scripts/ci_prepare.sh
- name: Setup authentik env
uses: ./.github/actions/setup
- uses: testspace-com/setup-testspace@v1
with:
domain: ${{github.repository_owner}}
@ -166,33 +116,21 @@ jobs:
run: |
testspace [integration]unittest.xml --link=codecov
- if: ${{ always() }}
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v3
test-e2e-provider:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- uses: actions/setup-node@v3.0.0
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Setup authentik env
uses: ./.github/actions/setup
- uses: testspace-com/setup-testspace@v1
with:
domain: ${{github.repository_owner}}
- id: cache-poetry
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}
- name: Setup authentik env
run: |
scripts/ci_prepare.sh
docker-compose -f tests/e2e/docker-compose.yml up -d
- id: cache-web
uses: actions/cache@v2.1.7
uses: actions/cache@v3
with:
path: web/dist
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/**') }}
@ -211,33 +149,21 @@ jobs:
run: |
testspace [e2e-provider]unittest.xml --link=codecov
- if: ${{ always() }}
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v3
test-e2e-rest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- uses: actions/setup-node@v3.0.0
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: web/package-lock.json
- name: Setup authentik env
uses: ./.github/actions/setup
- uses: testspace-com/setup-testspace@v1
with:
domain: ${{github.repository_owner}}
- id: cache-poetry
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}
- name: Setup authentik env
run: |
scripts/ci_prepare.sh
docker-compose -f tests/e2e/docker-compose.yml up -d
- id: cache-web
uses: actions/cache@v2.1.7
uses: actions/cache@v3
with:
path: web/dist
key: ${{ runner.os }}-web-${{ hashFiles('web/package-lock.json', 'web/**') }}
@ -256,7 +182,7 @@ jobs:
run: |
testspace [e2e-rest]unittest.xml --link=codecov
- if: ${{ always() }}
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v3
ci-core-mark:
needs:
- lint
@ -288,8 +214,7 @@ jobs:
id: ev
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
run: |
python ./scripts/gh_env.py
uses: ./.github/actions/docker-setup
- name: Login to Container Registry
uses: docker/login-action@v1
if: ${{ steps.ev.outputs.shouldBuild == 'true' }}

View File

@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v2
- uses: actions/setup-go@v3
with:
go-version: "^1.17"
- name: Run linter
@ -34,17 +34,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v2
- uses: actions/setup-go@v3
with:
go-version: "^1.17"
- name: Get dependencies
run: |
go get github.com/axw/gocov/gocov
go get github.com/AlekSi/gocov-xml
go get github.com/jstemmer/go-junit-report
- name: Go unittests
run: |
go test -timeout 0 -v -race -coverprofile=coverage.out -covermode=atomic -cover ./... | go-junit-report > junit.xml
go test -timeout 0 -v -race -coverprofile=coverage.out -covermode=atomic -cover ./...
ci-outpost-mark:
needs:
- lint-golint
@ -73,10 +68,9 @@ jobs:
uses: docker/setup-buildx-action@v1
- name: prepare variables
id: ev
uses: ./.github/actions/docker-setup
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
run: |
python ./scripts/gh_env.py
- name: Login to Container Registry
uses: docker/login-action@v1
if: ${{ steps.ev.outputs.shouldBuild == 'true' }}
@ -111,10 +105,10 @@ jobs:
goarch: [amd64, arm64]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v2
- uses: actions/setup-go@v3
with:
go-version: "^1.17"
- uses: actions/setup-node@v3.0.0
- uses: actions/setup-node@v3.1.1
with:
node-version: '16'
cache: 'npm'
@ -130,7 +124,7 @@ jobs:
export GOOS=${{ matrix.goos }}
export GOARCH=${{ matrix.goarch }}
go build -tags=outpost_static_embed -v -o ./authentik-outpost-${{ matrix.type }}_${{ matrix.goos }}_${{ matrix.goarch }} ./cmd/${{ matrix.type }}
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: authentik-outpost-${{ matrix.type }}_${{ matrix.goos }}_${{ matrix.goarch }}
path: ./authentik-outpost-${{ matrix.type }}_${{ matrix.goos }}_${{ matrix.goarch }}

View File

@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3.0.0
- uses: actions/setup-node@v3.1.1
with:
node-version: '16'
cache: 'npm'
@ -33,7 +33,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3.0.0
- uses: actions/setup-node@v3.1.1
with:
node-version: '16'
cache: 'npm'
@ -51,7 +51,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3.0.0
- uses: actions/setup-node@v3.1.1
with:
node-version: '16'
cache: 'npm'
@ -79,7 +79,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3.0.0
- uses: actions/setup-node@v3.1.1
with:
node-version: '16'
cache: 'npm'

View File

@ -30,9 +30,9 @@ jobs:
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik:2022.3.3,
beryju/authentik:2022.4.1,
beryju/authentik:latest,
ghcr.io/goauthentik/server:2022.3.3,
ghcr.io/goauthentik/server:2022.4.1,
ghcr.io/goauthentik/server:latest
platforms: linux/amd64,linux/arm64
context: .
@ -46,7 +46,7 @@ jobs:
- ldap
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v2
- uses: actions/setup-go@v3
with:
go-version: "^1.17"
- name: Set up QEMU
@ -69,9 +69,9 @@ jobs:
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik-${{ matrix.type }}:2022.3.3,
beryju/authentik-${{ matrix.type }}:2022.4.1,
beryju/authentik-${{ matrix.type }}:latest,
ghcr.io/goauthentik/${{ matrix.type }}:2022.3.3,
ghcr.io/goauthentik/${{ matrix.type }}:2022.4.1,
ghcr.io/goauthentik/${{ matrix.type }}:latest
file: ${{ matrix.type }}.Dockerfile
platforms: linux/amd64,linux/arm64
@ -88,10 +88,10 @@ jobs:
goarch: [amd64, arm64]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v2
- uses: actions/setup-go@v3
with:
go-version: "^1.17"
- uses: actions/setup-node@v3.0.0
- uses: actions/setup-node@v3.1.1
with:
node-version: '16'
cache: 'npm'
@ -152,7 +152,7 @@ jobs:
SENTRY_PROJECT: authentik
SENTRY_URL: https://sentry.beryju.org
with:
version: authentik@2022.3.3
version: authentik@2022.4.1
environment: beryjuorg-prod
sourcemaps: './web/dist'
url_prefix: '~/static/dist'

View File

@ -7,8 +7,6 @@ on:
pull_request:
paths:
- '/locale/'
schedule:
- cron: "0 */2 * * *"
workflow_dispatch:
env:
@ -21,23 +19,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- id: cache-poetry
uses: actions/cache@v2.1.7
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-cache-v2-${{ hashFiles('**/poetry.lock') }}
- name: prepare
env:
INSTALL: ${{ steps.cache-poetry.outputs.cache-hit }}
run: |
sudo apt-get update
sudo apt-get install -y gettext
scripts/ci_prepare.sh
- name: Setup authentik env
uses: ./.github/actions/setup
- name: run compile
run: poetry run ./manage.py compilemessages
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
uses: peter-evans/create-pull-request@v4
id: cpr
with:
token: ${{ secrets.GITHUB_TOKEN }}
@ -47,10 +34,3 @@ jobs:
body: "core: compile backend translations"
delete-branch: true
signoff: true
- name: Enable Pull Request Automerge
if: steps.cpr.outputs.pull-request-operation == 'created'
uses: peter-evans/enable-pull-request-automerge@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
merge-method: squash

View File

@ -10,7 +10,7 @@ jobs:
steps:
- uses: actions/checkout@v3
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v3.0.0
- uses: actions/setup-node@v3.1.1
with:
node-version: '16'
registry-url: 'https://registry.npmjs.org'
@ -29,7 +29,7 @@ jobs:
export VERSION=`node -e 'console.log(require("../web-api/package.json").version)'`
npm i @goauthentik/api@$VERSION
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
uses: peter-evans/create-pull-request@v4
id: cpr
with:
token: ${{ secrets.GITHUB_TOKEN }}
@ -39,10 +39,3 @@ jobs:
body: "web: Update Web API Client version"
delete-branch: true
signoff: true
- name: Enable Pull Request Automerge
if: steps.cpr.outputs.pull-request-operation == 'created'
uses: peter-evans/enable-pull-request-automerge@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
merge-method: squash

View File

@ -32,7 +32,7 @@ COPY ./go.sum /work/go.sum
RUN go build -o /work/authentik ./cmd/server/main.go
# Stage 4: Run
FROM docker.io/python:3.10.3-slim-bullseye
FROM docker.io/python:3.10.4-slim-bullseye
LABEL org.opencontainers.image.url https://goauthentik.io
LABEL org.opencontainers.image.description goauthentik.io Main server image, see https://goauthentik.io for more info.

View File

@ -56,13 +56,12 @@ gen-web:
docker run \
--rm -v ${PWD}:/local \
--user ${UID}:${GID} \
openapitools/openapi-generator-cli generate \
openapitools/openapi-generator-cli:v6.0.0-beta generate \
-i /local/schema.yml \
-g typescript-fetch \
-o /local/web-api \
--additional-properties=typescriptThreePlus=true,supportsES6=true,npmName=@goauthentik/api,npmVersion=${NPM_VERSION}
mkdir -p web/node_modules/@goauthentik/api
python -m scripts.web_api_esm
\cp -fv scripts/web_api_readme.md web-api/README.md
cd web-api && npm i
\cp -rfv web-api/* web/node_modules/@goauthentik/api
@ -75,7 +74,7 @@ gen-outpost:
docker run \
--rm -v ${PWD}:/local \
--user ${UID}:${GID} \
openapitools/openapi-generator-cli:v5.2.1 generate \
openapitools/openapi-generator-cli:v6.0.0-beta generate \
-i /local/schema.yml \
-g go \
-o /local/api \

View File

@ -6,8 +6,8 @@
| Version | Supported |
| ---------- | ------------------ |
| 2022.2.x | :white_check_mark: |
| 2022.3.x | :white_check_mark: |
| 2022.4.x | :white_check_mark: |
## Reporting a Vulnerability

View File

@ -2,7 +2,7 @@
from os import environ
from typing import Optional
__version__ = "2022.3.3"
__version__ = "2022.4.1"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -1,6 +1,4 @@
"""API Authentication"""
from base64 import b64decode
from binascii import Error
from typing import Any, Optional
from django.conf import settings
@ -16,38 +14,36 @@ from authentik.outposts.models import Outpost
LOGGER = get_logger()
# pylint: disable=too-many-return-statements
def bearer_auth(raw_header: bytes) -> Optional[User]:
"""raw_header in the Format of `Bearer dGVzdDp0ZXN0`"""
auth_credentials = raw_header.decode()
def validate_auth(header: bytes) -> str:
"""Validate that the header is in a correct format,
returns type and credentials"""
auth_credentials = header.decode().strip()
if auth_credentials == "" or " " not in auth_credentials:
return None
auth_type, _, auth_credentials = auth_credentials.partition(" ")
if auth_type.lower() not in ["basic", "bearer"]:
if auth_type.lower() != "bearer":
LOGGER.debug("Unsupported authentication type, denying", type=auth_type.lower())
raise AuthenticationFailed("Unsupported authentication type")
password = auth_credentials
if auth_type.lower() == "basic":
try:
auth_credentials = b64decode(auth_credentials.encode()).decode()
except (UnicodeDecodeError, Error):
raise AuthenticationFailed("Malformed header")
# Accept credentials with username and without
if ":" in auth_credentials:
_, _, password = auth_credentials.partition(":")
else:
password = auth_credentials
if password == "": # nosec
if auth_credentials == "": # nosec
raise AuthenticationFailed("Malformed header")
tokens = Token.filter_not_expired(key=password, intent=TokenIntents.INTENT_API)
if not tokens.exists():
user = token_secret_key(password)
if not user:
raise AuthenticationFailed("Token invalid/expired")
return user
return auth_credentials
def bearer_auth(raw_header: bytes) -> Optional[User]:
"""raw_header in the Format of `Bearer ....`"""
auth_credentials = validate_auth(raw_header)
if not auth_credentials:
return None
# first, check traditional tokens
token = Token.filter_not_expired(key=auth_credentials, intent=TokenIntents.INTENT_API).first()
if hasattr(LOCAL, "authentik"):
LOCAL.authentik[KEY_AUTH_VIA] = "api_token"
return tokens.first().user
if token:
return token.user
user = token_secret_key(auth_credentials)
if user:
return user
raise AuthenticationFailed("Token invalid/expired")
def token_secret_key(value: str) -> Optional[User]:

View File

@ -14,12 +14,6 @@ from authentik.outposts.managed import OutpostManager
class TestAPIAuth(TestCase):
"""Test API Authentication"""
def test_valid_basic(self):
"""Test valid token"""
token = Token.objects.create(intent=TokenIntents.INTENT_API, user=get_anonymous_user())
auth = b64encode(f":{token.key}".encode()).decode()
self.assertEqual(bearer_auth(f"Basic {auth}".encode()), token.user)
def test_valid_bearer(self):
"""Test valid token"""
token = Token.objects.create(intent=TokenIntents.INTENT_API, user=get_anonymous_user())
@ -30,16 +24,6 @@ class TestAPIAuth(TestCase):
with self.assertRaises(AuthenticationFailed):
bearer_auth("foo bar".encode())
def test_invalid_decode(self):
"""Test invalid bas64"""
with self.assertRaises(AuthenticationFailed):
bearer_auth("Basic bar".encode())
def test_invalid_empty_password(self):
"""Test invalid with empty password"""
with self.assertRaises(AuthenticationFailed):
bearer_auth("Basic :".encode())
def test_invalid_no_token(self):
"""Test invalid with no token"""
with self.assertRaises(AuthenticationFailed):

View File

@ -17,6 +17,7 @@ from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from rest_framework_guardian.filters import ObjectPermissionsFilter
from structlog.stdlib import get_logger
from structlog.testing import capture_logs
from authentik.admin.api.metrics import CoordinateSerializer
from authentik.api.decorators import permission_required
@ -25,6 +26,7 @@ from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import FilePathSerializer, FileUploadSerializer
from authentik.core.models import Application, User
from authentik.events.models import EventAction
from authentik.events.utils import sanitize_dict
from authentik.policies.api.exec import PolicyTestResultSerializer
from authentik.policies.engine import PolicyEngine
from authentik.policies.types import PolicyResult
@ -42,7 +44,7 @@ class ApplicationSerializer(ModelSerializer):
"""Application Serializer"""
launch_url = SerializerMethodField()
provider_obj = ProviderSerializer(source="get_provider", required=False)
provider_obj = ProviderSerializer(source="get_provider", required=False, read_only=True)
meta_icon = ReadOnlyField(source="get_meta_icon")
@ -66,6 +68,7 @@ class ApplicationSerializer(ModelSerializer):
"meta_description",
"meta_publisher",
"policy_engine_mode",
"group",
]
extra_kwargs = {
"meta_icon": {"read_only": True},
@ -83,6 +86,7 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
"meta_launch_url",
"meta_description",
"meta_publisher",
"group",
]
lookup_field = "slug"
ordering = ["name"]
@ -132,12 +136,19 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
return HttpResponseBadRequest("for_user must be numerical")
engine = PolicyEngine(application, for_user, request)
engine.use_cache = False
engine.build()
result = engine.result
with capture_logs() as logs:
engine.build()
result = engine.result
response = PolicyTestResultSerializer(PolicyResult(False))
if result.passing:
response = PolicyTestResultSerializer(PolicyResult(True))
if request.user.is_superuser:
log_messages = []
for log in logs:
if log.get("process", "") == "PolicyProcess":
continue
log_messages.append(sanitize_dict(log))
result.log_messages = log_messages
response = PolicyTestResultSerializer(result)
return Response(response.data)

View File

@ -4,7 +4,7 @@ from json import loads
from django.db.models.query import QuerySet
from django_filters.filters import CharFilter, ModelMultipleChoiceFilter
from django_filters.filterset import FilterSet
from rest_framework.fields import CharField, JSONField
from rest_framework.fields import CharField, IntegerField, JSONField
from rest_framework.serializers import ListSerializer, ModelSerializer, ValidationError
from rest_framework.viewsets import ModelViewSet
from rest_framework_guardian.filters import ObjectPermissionsFilter
@ -46,11 +46,14 @@ class GroupSerializer(ModelSerializer):
)
parent_name = CharField(source="parent.name", read_only=True)
num_pk = IntegerField(read_only=True)
class Meta:
model = Group
fields = [
"pk",
"num_pk",
"name",
"is_superuser",
"parent",

View File

@ -2,7 +2,7 @@
from typing import Any
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import OpenApiResponse, extend_schema
from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer
from guardian.shortcuts import assign_perm, get_anonymous_user
from rest_framework.decorators import action
from rest_framework.exceptions import ValidationError
@ -20,13 +20,14 @@ from authentik.core.api.users import UserSerializer
from authentik.core.api.utils import PassiveSerializer
from authentik.core.models import USER_ATTRIBUTE_TOKEN_EXPIRING, Token, TokenIntents
from authentik.events.models import Event, EventAction
from authentik.events.utils import model_to_dict
from authentik.managed.api import ManagedSerializer
class TokenSerializer(ManagedSerializer, ModelSerializer):
"""Token Serializer"""
user_obj = UserSerializer(required=False, source="user")
user_obj = UserSerializer(required=False, source="user", read_only=True)
def validate(self, attrs: dict[Any, str]) -> dict[Any, str]:
"""Ensure only API or App password tokens are created."""
@ -110,10 +111,39 @@ class TokenViewSet(UsedByMixin, ModelViewSet):
404: OpenApiResponse(description="Token not found or expired"),
}
)
@action(detail=True, pagination_class=None, filter_backends=[])
@action(detail=True, pagination_class=None, filter_backends=[], methods=["GET"])
# pylint: disable=unused-argument
def view_key(self, request: Request, identifier: str) -> Response:
"""Return token key and log access"""
token: Token = self.get_object()
Event.new(EventAction.SECRET_VIEW, secret=token).from_http(request) # noqa # nosec
return Response(TokenViewSerializer({"key": token.key}).data)
@permission_required("authentik_core.set_token_key")
@extend_schema(
request=inline_serializer(
"TokenSetKey",
{
"key": CharField(),
},
),
responses={
204: OpenApiResponse(description="Successfully changed key"),
400: OpenApiResponse(description="Missing key"),
404: OpenApiResponse(description="Token not found or expired"),
},
)
@action(detail=True, pagination_class=None, filter_backends=[], methods=["POST"])
# pylint: disable=unused-argument
def set_key(self, request: Request, identifier: str) -> Response:
"""Return token key and log access"""
token: Token = self.get_object()
key = request.POST.get("key")
if not key:
return Response(status=400)
token.key = key
token.save()
Event.new(EventAction.MODEL_UPDATED, model=model_to_dict(token)).from_http(
request
) # noqa # nosec
return Response(status=204)

View File

@ -1,7 +1,7 @@
"""User API Views"""
from datetime import timedelta
from json import loads
from typing import Optional
from typing import Any, Optional
from django.contrib.auth import update_session_auth_hash
from django.db.models.query import QuerySet
@ -23,7 +23,7 @@ from drf_spectacular.utils import (
)
from guardian.shortcuts import get_anonymous_user, get_objects_for_user
from rest_framework.decorators import action
from rest_framework.fields import CharField, DictField, JSONField, SerializerMethodField
from rest_framework.fields import CharField, JSONField, SerializerMethodField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import (
@ -96,14 +96,13 @@ class UserSerializer(ModelSerializer):
class UserSelfSerializer(ModelSerializer):
"""User Serializer for information a user can retrieve about themselves and
update about themselves"""
"""User Serializer for information a user can retrieve about themselves"""
is_superuser = BooleanField(read_only=True)
avatar = CharField(read_only=True)
groups = SerializerMethodField()
uid = CharField(read_only=True)
settings = DictField(source="attributes.settings", default=dict)
settings = SerializerMethodField()
@extend_schema_field(
ListSerializer(
@ -121,6 +120,10 @@ class UserSelfSerializer(ModelSerializer):
"pk": group.pk,
}
def get_settings(self, user: User) -> dict[str, Any]:
"""Get user settings with tenant and group settings applied"""
return user.group_attributes(self._context["request"]).get("settings", {})
class Meta:
model = User
@ -328,12 +331,14 @@ class UserViewSet(UsedByMixin, ModelViewSet):
# pylint: disable=invalid-name
def me(self, request: Request) -> Response:
"""Get information about current user"""
context = {"request": request}
serializer = SessionUserSerializer(
data={"user": UserSelfSerializer(instance=request.user).data}
data={"user": UserSelfSerializer(instance=request.user, context=context).data}
)
if SESSION_IMPERSONATE_USER in request._request.session:
serializer.initial_data["original"] = UserSelfSerializer(
instance=request._request.session[SESSION_IMPERSONATE_ORIGINAL_USER]
instance=request._request.session[SESSION_IMPERSONATE_ORIGINAL_USER],
context=context,
).data
return Response(serializer.initial_data)

View File

@ -49,6 +49,7 @@ class TokenBackend(InbuiltBackend):
# difference between an existing and a nonexistent user (#20760).
User().set_password(password)
return None
# pylint: disable=no-member
tokens = Token.filter_not_expired(
user=user, key=password, intent=TokenIntents.INTENT_APP_PASSWORD
)

View File

@ -0,0 +1,18 @@
# Generated by Django 4.0.3 on 2022-04-02 19:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_core", "0018_auto_20210330_1345_squashed_0028_alter_token_intent"),
]
operations = [
migrations.AddField(
model_name="application",
name="group",
field=models.TextField(blank=True, default=""),
),
]

View File

@ -36,6 +36,9 @@ from authentik.policies.models import PolicyBindingModel
LOGGER = get_logger()
USER_ATTRIBUTE_DEBUG = "goauthentik.io/user/debug"
USER_ATTRIBUTE_SA = "goauthentik.io/user/service-account"
USER_ATTRIBUTE_GENERATED = "goauthentik.io/user/generated"
USER_ATTRIBUTE_EXPIRES = "goauthentik.io/user/expires"
USER_ATTRIBUTE_DELETE_ON_LOGOUT = "goauthentik.io/user/delete-on-logout"
USER_ATTRIBUTE_SOURCES = "goauthentik.io/user/sources"
USER_ATTRIBUTE_TOKEN_EXPIRING = "goauthentik.io/user/token-expires" # nosec
USER_ATTRIBUTE_CHANGE_USERNAME = "goauthentik.io/user/can-change-username"
@ -59,7 +62,7 @@ def default_token_key():
"""Default token key"""
# We use generate_id since the chars in the key should be easy
# to use in Emails (for verification) and URLs (for recovery)
return generate_id(128)
return generate_id(int(CONFIG.y("default_token_length")))
class Group(models.Model):
@ -81,6 +84,13 @@ class Group(models.Model):
)
attributes = models.JSONField(default=dict, blank=True)
@property
def num_pk(self) -> int:
"""Get a numerical, int32 ID for the group"""
# int max is 2147483647 (10 digits) so 9 is the max usable
# in the LDAP Outpost we use the last 5 chars so match here
return int(str(self.pk.int)[:5])
def is_member(self, user: "User") -> bool:
"""Recursively check if `user` is member of us, or any parent."""
query = """
@ -137,10 +147,12 @@ class User(GuardianUserMixin, AbstractUser):
objects = UserManager()
def group_attributes(self) -> dict[str, Any]:
def group_attributes(self, request: Optional[HttpRequest] = None) -> dict[str, Any]:
"""Get a dictionary containing the attributes from all groups the user belongs to,
including the users attributes"""
final_attributes = {}
if request and hasattr(request, "tenant"):
always_merger.merge(final_attributes, request.tenant.attributes)
for group in self.ak_groups.all().order_by("name"):
always_merger.merge(final_attributes, group.attributes)
always_merger.merge(final_attributes, self.attributes)
@ -156,11 +168,11 @@ class User(GuardianUserMixin, AbstractUser):
"""superuser == staff user"""
return self.is_superuser # type: ignore
def set_password(self, password, signal=True):
def set_password(self, raw_password, signal=True):
if self.pk and signal:
password_changed.send(sender=self, user=self, password=password)
password_changed.send(sender=self, user=self, password=raw_password)
self.password_change_date = now()
return super().set_password(password)
return super().set_password(raw_password)
def check_password(self, raw_password: str) -> bool:
"""
@ -257,6 +269,8 @@ class Application(PolicyBindingModel):
name = models.TextField(help_text=_("Application's display Name."))
slug = models.SlugField(help_text=_("Internal application name, used in URLs."), unique=True)
group = models.TextField(blank=True, default="")
provider = models.OneToOneField(
"Provider", null=True, blank=True, default=None, on_delete=models.SET_DEFAULT
)

View File

@ -1,10 +1,18 @@
"""authentik core tasks"""
from datetime import datetime, timedelta
from django.contrib.sessions.backends.cache import KEY_PREFIX
from django.core.cache import cache
from django.utils.timezone import now
from structlog.stdlib import get_logger
from authentik.core.models import AuthenticatedSession, ExpiringModel
from authentik.core.models import (
USER_ATTRIBUTE_EXPIRES,
USER_ATTRIBUTE_GENERATED,
AuthenticatedSession,
ExpiringModel,
User,
)
from authentik.events.monitored_tasks import (
MonitoredTask,
TaskResult,
@ -42,3 +50,24 @@ def clean_expired_models(self: MonitoredTask):
LOGGER.debug("Expired sessions", model=AuthenticatedSession, amount=amount)
messages.append(f"Expired {amount} {AuthenticatedSession._meta.verbose_name_plural}")
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages))
@CELERY_APP.task(bind=True, base=MonitoredTask)
@prefill_task
def clean_temporary_users(self: MonitoredTask):
"""Remove temporary users created by SAML Sources"""
_now = datetime.now()
messages = []
deleted_users = 0
for user in User.objects.filter(**{f"attributes__{USER_ATTRIBUTE_GENERATED}": True}):
if not user.attributes.get(USER_ATTRIBUTE_EXPIRES):
continue
delta: timedelta = _now - datetime.fromtimestamp(
user.attributes.get(USER_ATTRIBUTE_EXPIRES)
)
if delta.total_seconds() > 0:
LOGGER.debug("User is expired and will be deleted.", user=user, delta=delta)
user.delete()
deleted_users += 1
messages.append(f"Successfully deleted {deleted_users} users.")
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages))

View File

@ -1,4 +1,6 @@
"""Test Applications API"""
from json import loads
from django.urls import reverse
from rest_framework.test import APITestCase
@ -46,7 +48,10 @@ class TestApplicationsAPI(APITestCase):
)
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(response.content.decode(), {"messages": [], "passing": True})
body = loads(response.content.decode())
self.assertEqual(body["passing"], True)
self.assertEqual(body["messages"], [])
self.assertEqual(len(body["log_messages"]), 0)
response = self.client.get(
reverse(
"authentik_api:application-check-access",
@ -54,7 +59,9 @@ class TestApplicationsAPI(APITestCase):
)
)
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(response.content.decode(), {"messages": ["dummy"], "passing": False})
body = loads(response.content.decode())
self.assertEqual(body["passing"], False)
self.assertEqual(body["messages"], ["dummy"])
def test_list(self):
"""Test list operation without superuser_full_list"""
@ -77,6 +84,7 @@ class TestApplicationsAPI(APITestCase):
"pk": str(self.allowed.pk),
"name": "allowed",
"slug": "allowed",
"group": "",
"provider": self.provider.pk,
"provider_obj": {
"assigned_application_name": "allowed",
@ -124,6 +132,7 @@ class TestApplicationsAPI(APITestCase):
"pk": str(self.allowed.pk),
"name": "allowed",
"slug": "allowed",
"group": "",
"provider": self.provider.pk,
"provider_obj": {
"assigned_application_name": "allowed",
@ -150,6 +159,7 @@ class TestApplicationsAPI(APITestCase):
"meta_icon": None,
"meta_launch_url": "",
"meta_publisher": "",
"group": "",
"name": "denied",
"pk": str(self.denied.pk),
"policy_engine_mode": "any",

View File

@ -0,0 +1,50 @@
"""Test tasks"""
from time import mktime
from django.utils.timezone import now
from guardian.shortcuts import get_anonymous_user
from rest_framework.test import APITestCase
from authentik.core.models import (
USER_ATTRIBUTE_EXPIRES,
USER_ATTRIBUTE_GENERATED,
Token,
TokenIntents,
User,
)
from authentik.core.tasks import clean_expired_models, clean_temporary_users
from authentik.core.tests.utils import create_test_admin_user
from authentik.lib.generators import generate_id
class TestTasks(APITestCase):
"""Test token API"""
def setUp(self) -> None:
super().setUp()
self.user = User.objects.create(username="testuser")
self.admin = create_test_admin_user()
self.client.force_login(self.user)
def test_token_expire(self):
"""Test Token expire task"""
token: Token = Token.objects.create(
expires=now(), user=get_anonymous_user(), intent=TokenIntents.INTENT_API
)
key = token.key
clean_expired_models.delay().get()
token.refresh_from_db()
self.assertNotEqual(key, token.key)
def test_clean_temporary_users(self):
"""Test clean_temporary_users task"""
username = generate_id
User.objects.create(
username=username,
attributes={
USER_ATTRIBUTE_GENERATED: True,
USER_ATTRIBUTE_EXPIRES: mktime(now().timetuple()),
},
)
clean_temporary_users.delay().get()
self.assertFalse(User.objects.filter(username=username))

View File

@ -2,12 +2,10 @@
from json import loads
from django.urls.base import reverse
from django.utils.timezone import now
from guardian.shortcuts import get_anonymous_user
from rest_framework.test import APITestCase
from authentik.core.models import USER_ATTRIBUTE_TOKEN_EXPIRING, Token, TokenIntents, User
from authentik.core.tasks import clean_expired_models
from authentik.core.tests.utils import create_test_admin_user
@ -53,16 +51,6 @@ class TestTokenAPI(APITestCase):
self.assertEqual(token.intent, TokenIntents.INTENT_API)
self.assertEqual(token.expiring, False)
def test_token_expire(self):
"""Test Token expire task"""
token: Token = Token.objects.create(
expires=now(), user=get_anonymous_user(), intent=TokenIntents.INTENT_API
)
key = token.key
clean_expired_models.delay().get()
token.refresh_from_db()
self.assertNotEqual(key, token.key)
def test_list(self):
"""Test Token List (Test normal authentication)"""
token_should: Token = Token.objects.create(

View File

@ -67,9 +67,9 @@ def certificate_discovery(self: MonitoredTask):
private_keys[cert_name] = ensure_private_key_valid(body)
else:
certs[cert_name] = ensure_certificate_valid(body)
discovered += 1
except (OSError, ValueError) as exc:
LOGGER.warning("Failed to open file or invalid format", exc=exc, file=path)
discovered += 1
for name, cert_data in certs.items():
cert = CertificateKeyPair.objects.filter(managed=MANAGED_DISCOVERED % name).first()
if not cert:

View File

@ -93,6 +93,11 @@ def sanitize_dict(source: dict[Any, Any]) -> dict[Any, Any]:
final_dict[key] = value.hex
elif isinstance(value, (HttpRequest, WSGIRequest)):
continue
elif isinstance(value, type):
final_dict[key] = {
"type": value.__name__,
"module": value.__module__,
}
else:
final_dict[key] = value
return final_dict

View File

@ -442,9 +442,9 @@ class FlowErrorResponse(TemplateResponse):
context = {}
context["error"] = self.error
if self._request.user and self._request.user.is_authenticated:
if self._request.user.is_superuser or self._request.user.group_attributes().get(
USER_ATTRIBUTE_DEBUG, False
):
if self._request.user.is_superuser or self._request.user.group_attributes(
self._request
).get(USER_ATTRIBUTE_DEBUG, False):
context["tb"] = "".join(format_tb(self.error.__traceback__))
return context

View File

@ -71,3 +71,4 @@ default_user_change_username: true
gdpr_compliance: true
cert_discovery_dir: /certs
default_token_length: 128

View File

@ -45,7 +45,7 @@ def _get_outpost_override_ip(request: HttpRequest) -> Optional[str]:
LOGGER.warning("Attempted remote-ip override without token", fake_ip=fake_ip)
return None
user = tokens.first().user
if not user.group_attributes().get(USER_ATTRIBUTE_CAN_OVERRIDE_IP, False):
if not user.group_attributes(request).get(USER_ATTRIBUTE_CAN_OVERRIDE_IP, False):
LOGGER.warning(
"Remote-IP override: user doesn't have permission",
user=user,

View File

@ -157,7 +157,7 @@ class DockerController(BaseController):
# {'HostIp': '::', 'HostPort': '389'}
# ]}
# If no ports are mapped (either mapping disabled, or host network)
if not container.ports:
if not container.ports or not self.outpost.config.docker_map_ports:
return False
for port in self.deployment_ports:
key = f"{port.inner_port or port.port}/{port.protocol.lower()}"

View File

@ -1,5 +1,5 @@
"""Serializer for policy execution"""
from rest_framework.fields import BooleanField, CharField, JSONField, ListField
from rest_framework.fields import BooleanField, CharField, DictField, JSONField, ListField
from rest_framework.relations import PrimaryKeyRelatedField
from authentik.core.api.utils import PassiveSerializer, is_dict
@ -18,3 +18,4 @@ class PolicyTestResultSerializer(PassiveSerializer):
passing = BooleanField()
messages = ListField(child=CharField(), read_only=True)
log_messages = ListField(child=DictField(), read_only=True)

View File

@ -11,11 +11,13 @@ from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer, SerializerMethodField
from rest_framework.viewsets import GenericViewSet
from structlog.stdlib import get_logger
from structlog.testing import capture_logs
from authentik.api.decorators import permission_required
from authentik.core.api.applications import user_app_cache_key
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import CacheSerializer, MetaNameSerializer, TypeCreateSerializer
from authentik.events.utils import sanitize_dict
from authentik.lib.utils.reflection import all_subclasses
from authentik.policies.api.exec import PolicyTestResultSerializer, PolicyTestSerializer
from authentik.policies.models import Policy, PolicyBinding
@ -166,6 +168,13 @@ class PolicyViewSet(
p_request.context = test_params.validated_data.get("context", {})
proc = PolicyProcess(PolicyBinding(policy=policy), p_request, None)
result = proc.execute()
with capture_logs() as logs:
result = proc.execute()
log_messages = []
for log in logs:
if log.get("process", "") == "PolicyProcess":
continue
log_messages.append(sanitize_dict(log))
result.log_messages = log_messages
response = PolicyTestResultSerializer(result)
return Response(response.data)

View File

@ -33,8 +33,8 @@ class AccessDeniedResponse(TemplateResponse):
# either superuser or has USER_ATTRIBUTE_DEBUG set
if self.policy_result:
if self._request.user and self._request.user.is_authenticated:
if self._request.user.is_superuser or self._request.user.group_attributes().get(
USER_ATTRIBUTE_DEBUG, False
):
if self._request.user.is_superuser or self._request.user.group_attributes(
self._request
).get(USER_ATTRIBUTE_DEBUG, False):
context["policy_result"] = self.policy_result
return context

View File

@ -36,7 +36,7 @@ class DummyPolicy(Policy):
def passes(self, request: PolicyRequest) -> PolicyResult:
"""Wait random time then return result"""
wait = SystemRandom().randrange(self.wait_min, self.wait_max)
LOGGER.debug("Policy waiting", policy=self, delay=wait)
LOGGER.info("Policy waiting", policy=self, delay=wait)
sleep(wait)
return PolicyResult(self.result, "dummy")

View File

@ -9,7 +9,7 @@ from authentik.policies.types import PolicyRequest, PolicyResult
class ExpressionPolicy(Policy):
"""Execute arbitrary Python code to implement custom checks and validation."""
"""Execute arbitrary Python code to implement custom checks and validation."""
expression = models.TextField()

View File

@ -90,6 +90,7 @@ class PolicyProcess(PROCESS_CLASS):
"P_ENG(proc): Running policy",
policy=self.binding.policy,
user=self.request.user,
# this is used for filtering in access checking where logs are sent to the admin
process="PolicyProcess",
)
try:
@ -121,6 +122,7 @@ class PolicyProcess(PROCESS_CLASS):
"P_ENG(proc): finished and cached ",
policy=self.binding.policy,
result=policy_result,
# this is used for filtering in access checking where logs are sent to the admin
process="PolicyProcess",
passing=policy_result.passing,
user=self.request.user,

View File

@ -1,4 +1,6 @@
"""Test policies API"""
from json import loads
from django.urls import reverse
from rest_framework.test import APITestCase
@ -23,7 +25,9 @@ class TestPoliciesAPI(APITestCase):
"user": self.user.pk,
},
)
self.assertJSONEqual(response.content.decode(), {"passing": True, "messages": ["dummy"]})
body = loads(response.content.decode())
self.assertEqual(body["passing"], True)
self.assertEqual(body["messages"], ["dummy"])
def test_types(self):
"""Test Policy's types endpoint"""

View File

@ -67,12 +67,15 @@ class PolicyResult:
source_binding: Optional["PolicyBinding"]
source_results: Optional[list["PolicyResult"]]
log_messages: Optional[list[dict]]
def __init__(self, passing: bool, *messages: str):
super().__init__()
self.passing = passing
self.messages = messages
self.source_binding = None
self.source_results = []
self.log_messages = []
def __repr__(self):
return self.__str__()

View File

@ -14,7 +14,7 @@ from authentik.flows.views.executor import SESSION_KEY_APPLICATION_PRE, SESSION_
from authentik.lib.sentry import SentryIgnoredException
from authentik.policies.denied import AccessDeniedResponse
from authentik.policies.engine import PolicyEngine
from authentik.policies.types import PolicyResult
from authentik.policies.types import PolicyRequest, PolicyResult
LOGGER = get_logger()
@ -103,11 +103,16 @@ class PolicyAccessView(AccessMixin, View):
response.policy_result = result
return response
def modify_policy_request(self, request: PolicyRequest) -> PolicyRequest:
"""optionally modify the policy request"""
return request
def user_has_access(self, user: Optional[User] = None) -> PolicyResult:
"""Check if user has access to application."""
user = user or self.request.user
policy_engine = PolicyEngine(self.application, user or self.request.user, self.request)
policy_engine.use_cache = False
policy_engine.request = self.modify_policy_request(policy_engine.request)
policy_engine.build()
result = policy_engine.result
LOGGER.debug(

View File

@ -34,6 +34,7 @@ class OAuth2ProviderSerializer(ProviderSerializer):
"sub_mode",
"property_mappings",
"issuer_mode",
"verification_keys",
]

View File

@ -1,8 +1,14 @@
"""OAuth/OpenID Constants"""
GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code"
GRANT_TYPE_IMPLICIT = "implicit"
GRANT_TYPE_REFRESH_TOKEN = "refresh_token" # nosec
GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials"
GRANT_TYPE_PASSWORD = "password" # nosec
CLIENT_ASSERTION_TYPE = "client_assertion_type"
CLIENT_ASSERTION = "client_assertion"
CLIENT_ASSERTION_TYPE_JWT = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
PROMPT_NONE = "none"
PROMPT_CONSNET = "consent"

View File

@ -0,0 +1,36 @@
# Generated by Django 4.0.3 on 2022-03-29 19:37
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_crypto", "0003_certificatekeypair_managed"),
("authentik_providers_oauth2", "0008_rename_rsa_key_oauth2provider_signing_key_and_more"),
]
operations = [
migrations.AddField(
model_name="oauth2provider",
name="verification_keys",
field=models.ManyToManyField(
help_text="JWTs created with the configured certificates can authenticate with this provider.",
related_name="+",
to="authentik_crypto.certificatekeypair",
verbose_name="Allowed certificates for JWT-based client_credentials",
),
),
migrations.AlterField(
model_name="oauth2provider",
name="signing_key",
field=models.ForeignKey(
help_text="Key used to sign the tokens. Only required when JWT Algorithm is set to RS256.",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="authentik_crypto.certificatekeypair",
verbose_name="Signing Key",
),
),
]

View File

@ -0,0 +1,26 @@
# Generated by Django 4.0.3 on 2022-03-31 18:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_crypto", "0003_certificatekeypair_managed"),
("authentik_providers_oauth2", "0009_oauth2provider_verification_keys_and_more"),
]
operations = [
migrations.AlterField(
model_name="oauth2provider",
name="verification_keys",
field=models.ManyToManyField(
blank=True,
default=None,
help_text="JWTs created with the configured certificates can authenticate with this provider.",
related_name="+",
to="authentik_crypto.certificatekeypair",
verbose_name="Allowed certificates for JWT-based client_credentials",
),
),
]

View File

@ -97,7 +97,7 @@ class JWTAlgorithms(models.TextChoices):
HS256 = "HS256", _("HS256 (Symmetric Encryption)")
RS256 = "RS256", _("RS256 (Asymmetric Encryption)")
EC256 = "EC256", _("EC256 (Asymmetric Encryption)")
ES256 = "ES256", _("ES256 (Asymmetric Encryption)")
class ScopeMapping(PropertyMapping):
@ -212,7 +212,7 @@ class OAuth2Provider(Provider):
signing_key = models.ForeignKey(
CertificateKeyPair,
verbose_name=_("RSA Key"),
verbose_name=_("Signing Key"),
on_delete=models.SET_NULL,
null=True,
help_text=_(
@ -220,6 +220,17 @@ class OAuth2Provider(Provider):
),
)
verification_keys = models.ManyToManyField(
CertificateKeyPair,
verbose_name=_("Allowed certificates for JWT-based client_credentials"),
help_text=_(
"JWTs created with the configured certificates can authenticate with this provider."
),
related_name="+",
default=None,
blank=True,
)
def create_refresh_token(
self, user: User, scope: list[str], request: HttpRequest
) -> "RefreshToken":
@ -244,7 +255,7 @@ class OAuth2Provider(Provider):
if isinstance(private_key, RSAPrivateKey):
return key.key_data, JWTAlgorithms.RS256
if isinstance(private_key, EllipticCurvePrivateKey):
return key.key_data, JWTAlgorithms.EC256
return key.key_data, JWTAlgorithms.ES256
raise Exception(f"Invalid private key type: {type(private_key)}")
def get_issuer(self, request: HttpRequest) -> Optional[str]:

View File

@ -84,26 +84,6 @@ class TestTokenClientCredentials(OAuthTestCase):
{"error": "invalid_grant", "error_description": TokenError.errors["invalid_grant"]},
)
def test_non_sa(self):
"""test non service-account"""
self.user.attributes[USER_ATTRIBUTE_SA] = False
self.user.save()
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": SCOPE_OPENID,
"client_id": self.provider.client_id,
"username": "sa",
"password": self.token.key,
},
)
self.assertEqual(response.status_code, 400)
self.assertJSONEqual(
response.content.decode(),
{"error": "invalid_grant", "error_description": TokenError.errors["invalid_grant"]},
)
def test_no_provider(self):
"""test no provider"""
self.app.provider = None

View File

@ -0,0 +1,206 @@
"""Test token view"""
from datetime import datetime, timedelta
from json import loads
from django.test import RequestFactory
from django.urls import reverse
from jwt import decode
from authentik.core.models import USER_ATTRIBUTE_SA, Application, Group
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.lib.generators import generate_id, generate_key
from authentik.managed.manager import ObjectManager
from authentik.policies.models import PolicyBinding
from authentik.providers.oauth2.constants import (
GRANT_TYPE_CLIENT_CREDENTIALS,
SCOPE_OPENID,
SCOPE_OPENID_EMAIL,
SCOPE_OPENID_PROFILE,
)
from authentik.providers.oauth2.models import OAuth2Provider, ScopeMapping
from authentik.providers.oauth2.tests.utils import OAuthTestCase
class TestTokenClientCredentialsJWT(OAuthTestCase):
"""Test token (client_credentials, with JWT) view"""
def setUp(self) -> None:
super().setUp()
ObjectManager().run()
self.factory = RequestFactory()
self.cert = create_test_cert()
self.provider: OAuth2Provider = OAuth2Provider.objects.create(
name="test",
client_id=generate_id(),
client_secret=generate_key(),
authorization_flow=create_test_flow(),
redirect_uris="http://testserver",
signing_key=self.cert,
)
self.provider.verification_keys.set([self.cert])
self.provider.property_mappings.set(ScopeMapping.objects.all())
self.app = Application.objects.create(name="test", slug="test", provider=self.provider)
self.user = create_test_admin_user("sa")
self.user.attributes[USER_ATTRIBUTE_SA] = True
self.user.save()
def test_invalid_type(self):
"""test invalid type"""
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"client_assertion_type": "foo",
"client_assertion": "foo.bar",
},
)
self.assertEqual(response.status_code, 400)
body = loads(response.content.decode())
self.assertEqual(body["error"], "invalid_grant")
def test_invalid_jwt(self):
"""test invalid JWT"""
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": "foo.bar",
},
)
self.assertEqual(response.status_code, 400)
body = loads(response.content.decode())
self.assertEqual(body["error"], "invalid_grant")
def test_invalid_signautre(self):
"""test invalid JWT"""
token = self.provider.encode(
{
"sub": "foo",
"exp": datetime.now() + timedelta(hours=2),
}
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": token + "foo",
},
)
self.assertEqual(response.status_code, 400)
body = loads(response.content.decode())
self.assertEqual(body["error"], "invalid_grant")
def test_invalid_expired(self):
"""test invalid JWT"""
token = self.provider.encode(
{
"sub": "foo",
"exp": datetime.now() - timedelta(hours=2),
}
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": token,
},
)
self.assertEqual(response.status_code, 400)
body = loads(response.content.decode())
self.assertEqual(body["error"], "invalid_grant")
def test_invalid_no_app(self):
"""test invalid JWT"""
self.app.provider = None
self.app.save()
token = self.provider.encode(
{
"sub": "foo",
"exp": datetime.now() + timedelta(hours=2),
}
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": token,
},
)
self.assertEqual(response.status_code, 400)
body = loads(response.content.decode())
self.assertEqual(body["error"], "invalid_grant")
def test_invalid_access_denied(self):
"""test invalid JWT"""
group = Group.objects.create(name="foo")
PolicyBinding.objects.create(
group=group,
target=self.app,
order=0,
)
token = self.provider.encode(
{
"sub": "foo",
"exp": datetime.now() + timedelta(hours=2),
}
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": token,
},
)
self.assertEqual(response.status_code, 400)
body = loads(response.content.decode())
self.assertEqual(body["error"], "invalid_grant")
def test_successful(self):
"""test successful"""
token = self.provider.encode(
{
"sub": "foo",
"exp": datetime.now() + timedelta(hours=2),
}
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": token,
},
)
self.assertEqual(response.status_code, 200)
body = loads(response.content.decode())
self.assertEqual(body["token_type"], "bearer")
_, alg = self.provider.get_jwt_key()
jwt = decode(
body["access_token"],
key=self.provider.signing_key.public_key,
algorithms=[alg],
audience=self.provider.client_id,
)
self.assertEqual(
jwt["given_name"], "Autogenerated user from application test (client credentials JWT)"
)
self.assertEqual(jwt["preferred_username"], "test-foo")

View File

@ -27,6 +27,7 @@ from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.lib.utils.time import timedelta_from_string
from authentik.lib.utils.urls import redirect_with_qs
from authentik.lib.views import bad_request_message
from authentik.policies.types import PolicyRequest
from authentik.policies.views import PolicyAccessView, RequestValidationError
from authentik.providers.oauth2.constants import (
PROMPT_CONSNET,
@ -438,6 +439,16 @@ class AuthorizationFlowInitView(PolicyAccessView):
self.provider = get_object_or_404(OAuth2Provider, client_id=client_id)
self.application = self.provider.application
def modify_policy_request(self, request: PolicyRequest) -> PolicyRequest:
request.context["oauth_scopes"] = self.params.scope
request.context["oauth_grant_type"] = self.params.grant_type
request.context["oauth_code_challenge"] = self.params.code_challenge
request.context["oauth_code_challenge_method"] = self.params.code_challenge_method
request.context["oauth_max_age"] = self.params.max_age
request.context["oauth_redirect_uri"] = self.params.redirect_uri
request.context["oauth_response_type"] = self.params.response_type
return request
# pylint: disable=unused-argument
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""Start FlowPLanner, return to flow executor shell"""

View File

@ -1,5 +1,7 @@
"""authentik pretend GitHub Views"""
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.utils.text import slugify
from django.views import View
from authentik.providers.oauth2.models import RefreshToken
@ -66,4 +68,57 @@ class GitHubUserTeamsView(View):
# pylint: disable=unused-argument
def get(self, request: HttpRequest, token: RefreshToken) -> HttpResponse:
"""Emulate GitHub's /user/teams API Endpoint"""
return JsonResponse([], safe=False)
user = token.user
orgs_response = []
for org in user.ak_groups.all():
_org = {
"id": org.num_pk,
"node_id": "",
"url": "",
"html_url": "",
"name": org.name,
"slug": slugify(org.name),
"description": "",
"privacy": "",
"permission": "",
"members_url": "",
"repositories_url": "",
"parent": None,
"members_count": 0,
"repos_count": 0,
"created_at": "",
"updated_at": "",
"organization": {
"login": slugify(request.tenant.branding_title),
"id": 1,
"node_id": "",
"url": "",
"repos_url": "",
"events_url": "",
"hooks_url": "",
"issues_url": "",
"members_url": "",
"public_members_url": "",
"avatar_url": "",
"description": "",
"name": request.tenant.branding_title,
"company": "",
"blog": "",
"location": "",
"email": "",
"is_verified": True,
"has_organization_projects": True,
"has_repository_projects": True,
"public_repos": 0,
"public_gists": 0,
"followers": 0,
"following": 0,
"html_url": "",
"created_at": "",
"updated_at": "",
"type": "Organization",
},
}
orgs_response.append(_org)
return JsonResponse(orgs_response, safe=False)

View File

@ -36,7 +36,6 @@ class JWKSView(View):
if signing_key:
private_key = signing_key.private_key
print(type(private_key))
if isinstance(private_key, RSAPrivateKey):
public_key: RSAPublicKey = private_key.public_key()
public_numbers = public_key.public_numbers()
@ -56,7 +55,7 @@ class JWKSView(View):
response_data["keys"] = [
{
"kty": "EC",
"alg": JWTAlgorithms.EC256,
"alg": JWTAlgorithms.ES256,
"use": "sig",
"kid": signing_key.kid,
"n": b64_enc(public_numbers.n),

View File

@ -11,15 +11,12 @@ from authentik.providers.oauth2.constants import (
ACR_AUTHENTIK_DEFAULT,
GRANT_TYPE_AUTHORIZATION_CODE,
GRANT_TYPE_CLIENT_CREDENTIALS,
GRANT_TYPE_IMPLICIT,
GRANT_TYPE_PASSWORD,
GRANT_TYPE_REFRESH_TOKEN,
SCOPE_OPENID,
)
from authentik.providers.oauth2.models import (
GrantTypes,
OAuth2Provider,
ResponseTypes,
ScopeMapping,
)
from authentik.providers.oauth2.models import OAuth2Provider, ResponseTypes, ScopeMapping
from authentik.providers.oauth2.utils import cors_allow
LOGGER = get_logger()
@ -78,8 +75,9 @@ class ProviderInfoView(View):
"grant_types_supported": [
GRANT_TYPE_AUTHORIZATION_CODE,
GRANT_TYPE_REFRESH_TOKEN,
GrantTypes.IMPLICIT,
GRANT_TYPE_IMPLICIT,
GRANT_TYPE_CLIENT_CREDENTIALS,
GRANT_TYPE_PASSWORD,
],
"id_token_signing_alg_values_supported": [supported_alg],
# See: http://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes

View File

@ -5,22 +5,37 @@ from hashlib import sha256
from typing import Any, Optional
from django.http import HttpRequest, HttpResponse
from django.utils.timezone import datetime, now
from django.views import View
from jwt import InvalidTokenError, decode
from structlog.stdlib import get_logger
from authentik.core.models import USER_ATTRIBUTE_SA, Application, Token, TokenIntents, User
from authentik.core.models import (
USER_ATTRIBUTE_EXPIRES,
USER_ATTRIBUTE_GENERATED,
Application,
Token,
TokenIntents,
User,
)
from authentik.crypto.models import CertificateKeyPair
from authentik.events.models import Event, EventAction
from authentik.lib.utils.time import timedelta_from_string
from authentik.policies.engine import PolicyEngine
from authentik.providers.oauth2.constants import (
CLIENT_ASSERTION,
CLIENT_ASSERTION_TYPE,
CLIENT_ASSERTION_TYPE_JWT,
GRANT_TYPE_AUTHORIZATION_CODE,
GRANT_TYPE_CLIENT_CREDENTIALS,
GRANT_TYPE_PASSWORD,
GRANT_TYPE_REFRESH_TOKEN,
)
from authentik.providers.oauth2.errors import TokenError, UserAuthError
from authentik.providers.oauth2.models import (
AuthorizationCode,
ClientTypes,
JWTAlgorithms,
OAuth2Provider,
RefreshToken,
)
@ -78,6 +93,18 @@ class TokenParams:
code_verifier=request.POST.get("code_verifier"),
)
def __check_policy_access(self, app: Application, request: HttpRequest, **kwargs):
engine = PolicyEngine(app, self.user, request)
engine.request.context["oauth_scopes"] = self.scope
engine.request.context["oauth_grant_type"] = self.grant_type
engine.request.context["oauth_code_verifier"] = self.code_verifier
engine.request.context.update(kwargs)
engine.build()
result = engine.result
if not result.passing:
LOGGER.info("User not authenticated for application", user=self.user, app=app)
raise TokenError("invalid_grant")
def __post_init__(self, raw_code: str, raw_token: str, request: HttpRequest):
if self.grant_type in [GRANT_TYPE_AUTHORIZATION_CODE, GRANT_TYPE_REFRESH_TOKEN]:
if (
@ -94,7 +121,7 @@ class TokenParams:
self.__post_init_code(raw_code)
elif self.grant_type == GRANT_TYPE_REFRESH_TOKEN:
self.__post_init_refresh(raw_token, request)
elif self.grant_type == GRANT_TYPE_CLIENT_CREDENTIALS:
elif self.grant_type in [GRANT_TYPE_CLIENT_CREDENTIALS, GRANT_TYPE_PASSWORD]:
self.__post_init_client_credentials(request)
else:
LOGGER.warning("Invalid grant type", grant_type=self.grant_type)
@ -187,6 +214,8 @@ class TokenParams:
raise TokenError("invalid_grant")
def __post_init_client_credentials(self, request: HttpRequest):
if request.POST.get(CLIENT_ASSERTION_TYPE, "") != "":
return self.__post_init_client_credentials_jwt(request)
# Authenticate user based on credentials
username = request.POST.get("username")
password = request.POST.get("password")
@ -199,10 +228,6 @@ class TokenParams:
if not token or token.user.uid != user.uid:
raise TokenError("invalid_grant")
self.user = user
if not self.user.attributes.get(USER_ATTRIBUTE_SA, False):
# Non-service accounts are not allowed
LOGGER.info("Non-service-account tried to use client credentials", user=self.user)
raise TokenError("invalid_grant")
Event.new(
action=EventAction.LOGIN,
@ -216,13 +241,74 @@ class TokenParams:
app = Application.objects.filter(provider=self.provider).first()
if not app or not app.provider:
raise TokenError("invalid_grant")
engine = PolicyEngine(app, self.user, request)
engine.build()
result = engine.result
if not result.passing:
LOGGER.info("User not authenticated for application", user=self.user, app=app)
self.__check_policy_access(app, request)
return None
def __post_init_client_credentials_jwt(self, request: HttpRequest):
assertion_type = request.POST.get(CLIENT_ASSERTION_TYPE, "")
if assertion_type != CLIENT_ASSERTION_TYPE_JWT:
raise TokenError("invalid_grant")
client_secret = request.POST.get("client_secret", None)
assertion = request.POST.get(CLIENT_ASSERTION, client_secret)
if not assertion:
raise TokenError("invalid_grant")
token = None
for cert in self.provider.verification_keys.all():
LOGGER.debug("verifying jwt with key", key=cert.name)
cert: CertificateKeyPair
public_key = cert.certificate.public_key()
if cert.private_key:
public_key = cert.private_key.public_key()
try:
token = decode(
assertion,
public_key,
algorithms=[JWTAlgorithms.RS256, JWTAlgorithms.ES256],
options={
"verify_aud": False,
},
)
except (InvalidTokenError, ValueError, TypeError) as last_exc:
LOGGER.warning("failed to validate jwt", last_exc=last_exc)
if not token:
raise TokenError("invalid_grant")
if "exp" in token:
exp = datetime.fromtimestamp(token["exp"])
# Non-timezone aware check since we assume `exp` is in UTC
if datetime.now() >= exp:
LOGGER.info("JWT token expired")
raise TokenError("invalid_grant")
app = Application.objects.filter(provider=self.provider).first()
if not app or not app.provider:
LOGGER.info("client_credentials grant for provider without application")
raise TokenError("invalid_grant")
self.__check_policy_access(app, request, oauth_jwt=token)
self.user, _ = User.objects.update_or_create(
username=f"{self.provider.name}-{token.get('sub')}",
defaults={
"attributes": {
USER_ATTRIBUTE_GENERATED: True,
USER_ATTRIBUTE_EXPIRES: token.get("exp"),
},
"last_login": now(),
"name": f"Autogenerated user from application {app.name} (client credentials JWT)",
},
)
Event.new(
action=EventAction.LOGIN,
PLAN_CONTEXT_METHOD="jwt",
PLAN_CONTEXT_METHOD_ARGS={
"jwt": token,
},
).from_http(request, user=self.user)
class TokenView(View):
"""Generate tokens for clients"""

View File

@ -8,7 +8,7 @@ SCOPE_AK_PROXY_EXPRESSION = """
# which are used for example for the HTTP-Basic Authentication mapping.
return {
"ak_proxy": {
"user_attributes": request.user.group_attributes()
"user_attributes": request.user.group_attributes(request)
}
}"""

View File

@ -30,7 +30,7 @@ class SessionMiddleware(UpstreamSessionMiddleware):
# Since go does not consider localhost with http a secure origin
# we can't set the secure flag.
user_agent = request.META.get("HTTP_USER_AGENT", "")
if user_agent.startswith("authentik-outpost@") or "safari" in user_agent.lower():
if user_agent.startswith("goauthentik.io/outpost/") or "safari" in user_agent.lower():
return False
return True
return False

View File

@ -345,6 +345,11 @@ CELERY_BEAT_SCHEDULE = {
"schedule": crontab(hour="*/24", minute=0),
"options": {"queue": "authentik_scheduled"},
},
"user_cleanup": {
"task": "authentik.core.tasks.clean_temporary_users",
"schedule": crontab(minute="*/5"),
"options": {"queue": "authentik_scheduled"},
},
}
CELERY_TASK_CREATE_MISSING_QUEUES = True
CELERY_TASK_DEFAULT_QUEUE = "authentik"

View File

@ -6,7 +6,7 @@ from django.apps import AppConfig
class AuthentikSourceSAMLConfig(AppConfig):
"""authentik saml_idp app config"""
"""authentik saml source app config"""
name = "authentik.sources.saml"
label = "authentik_sources_saml"

View File

@ -1,5 +1,6 @@
"""authentik saml source processor"""
from base64 import b64decode
from time import mktime
from typing import TYPE_CHECKING, Any
import xmlsec
@ -7,9 +8,16 @@ from defusedxml.lxml import fromstring
from django.core.cache import cache
from django.core.exceptions import SuspiciousOperation
from django.http import HttpRequest, HttpResponse
from django.utils.timezone import now
from structlog.stdlib import get_logger
from authentik.core.models import User
from authentik.core.models import (
USER_ATTRIBUTE_DELETE_ON_LOGOUT,
USER_ATTRIBUTE_EXPIRES,
USER_ATTRIBUTE_GENERATED,
USER_ATTRIBUTE_SOURCES,
User,
)
from authentik.flows.models import Flow
from authentik.flows.planner import (
PLAN_CONTEXT_PENDING_USER,
@ -19,6 +27,7 @@ from authentik.flows.planner import (
FlowPlanner,
)
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
from authentik.lib.utils.time import timedelta_from_string
from authentik.lib.utils.urls import redirect_with_qs
from authentik.policies.utils import delete_none_keys
from authentik.sources.saml.exceptions import (
@ -124,9 +133,19 @@ class ResponseProcessor:
on logout and periodically."""
# Create a temporary User
name_id = self._get_name_id().text
expiry = mktime(
(now() + timedelta_from_string(self._source.temporary_user_delete_after)).timetuple()
)
user: User = User.objects.create(
username=name_id,
attributes={"saml": {"source": self._source.pk.hex, "delete_on_logout": True}},
attributes={
USER_ATTRIBUTE_GENERATED: True,
USER_ATTRIBUTE_SOURCES: [
self._source.name,
],
USER_ATTRIBUTE_DELETE_ON_LOGOUT: True,
USER_ATTRIBUTE_EXPIRES: expiry,
},
)
LOGGER.debug("Created temporary user for NameID Transient", username=name_id)
user.set_unusable_password()

View File

@ -1,10 +0,0 @@
"""saml source settings"""
from celery.schedules import crontab
CELERY_BEAT_SCHEDULE = {
"saml_source_cleanup": {
"task": "authentik.sources.saml.tasks.clean_temporary_users",
"schedule": crontab(minute="*/5"),
"options": {"queue": "authentik_scheduled"},
}
}

View File

@ -4,7 +4,7 @@ from django.dispatch import receiver
from django.http import HttpRequest
from structlog.stdlib import get_logger
from authentik.core.models import User
from authentik.core.models import USER_ATTRIBUTE_DELETE_ON_LOGOUT, User
LOGGER = get_logger()
@ -15,8 +15,6 @@ def on_user_logged_out(sender, request: HttpRequest, user: User, **_):
"""Delete temporary user if the `delete_on_logout` flag is enabled"""
if not user:
return
if "saml" in user.attributes:
if "delete_on_logout" in user.attributes["saml"]:
if user.attributes["saml"]["delete_on_logout"]:
LOGGER.debug("Deleted temporary user", user=user)
user.delete()
if user.attributes.get(USER_ATTRIBUTE_DELETE_ON_LOGOUT, False):
LOGGER.debug("Deleted temporary user", user=user)
user.delete()

View File

@ -1,42 +0,0 @@
"""authentik saml source tasks"""
from django.utils.timezone import now
from structlog.stdlib import get_logger
from authentik.core.models import AuthenticatedSession, User
from authentik.events.monitored_tasks import (
MonitoredTask,
TaskResult,
TaskResultStatus,
prefill_task,
)
from authentik.lib.utils.time import timedelta_from_string
from authentik.root.celery import CELERY_APP
from authentik.sources.saml.models import SAMLSource
LOGGER = get_logger()
@CELERY_APP.task(bind=True, base=MonitoredTask)
@prefill_task
def clean_temporary_users(self: MonitoredTask):
"""Remove temporary users created by SAML Sources"""
_now = now()
messages = []
deleted_users = 0
for user in User.objects.filter(attributes__saml__isnull=False):
sources = SAMLSource.objects.filter(pk=user.attributes.get("saml", {}).get("source", ""))
if not sources.exists():
LOGGER.warning("User has an invalid SAML Source and won't be deleted!", user=user)
messages.append(f"User {user} has an invalid SAML Source and won't be deleted!")
continue
source = sources.first()
source_delta = timedelta_from_string(source.temporary_user_delete_after)
if (
_now - user.last_login >= source_delta
and not AuthenticatedSession.objects.filter(user=user).exists()
):
LOGGER.debug("User is expired and will be deleted.", user=user, delta=source_delta)
user.delete()
deleted_users += 1
messages.append(f"Successfully deleted {deleted_users} users.")
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages))

View File

@ -68,6 +68,8 @@ class AuthenticatorDuoStageViewSet(UsedByMixin, ModelViewSet):
client = stage.client
user_id = self.request.session.get(SESSION_KEY_DUO_USER_ID)
activation_code = self.request.session.get(SESSION_KEY_DUO_ACTIVATION_CODE)
if not user_id or not activation_code:
return Response(status=420)
status = client.enroll_status(user_id, activation_code)
if status == "success":
return Response(status=204)
@ -95,18 +97,20 @@ class AuthenticatorDuoStageViewSet(UsedByMixin, ModelViewSet):
def import_devices(self, request: Request, pk: str) -> Response:
"""Import duo devices into authentik"""
stage: AuthenticatorDuoStage = self.get_object()
users = get_objects_for_user(request.user, "authentik_core.view_user").filter(
username=request.query_params.get("username", "")
user = (
get_objects_for_user(request.user, "authentik_core.view_user")
.filter(username=request.query_params.get("username", ""))
.first()
)
if not users.exists():
if not user:
return Response(data={"non_field_errors": ["user does not exist"]}, status=400)
devices = DuoDevice.objects.filter(
duo_user_id=request.query_params.get("duo_user_id"), user=users.first(), stage=stage
)
if devices.exists():
device = DuoDevice.objects.filter(
duo_user_id=request.query_params.get("duo_user_id"), user=user, stage=stage
).first()
if device:
return Response(data={"non_field_errors": ["device exists already"]}, status=400)
DuoDevice.objects.create(
duo_user_id=request.query_params.get("duo_user_id"), user=users.first(), stage=stage
duo_user_id=request.query_params.get("duo_user_id"), user=user, stage=stage
)
return Response(status=204)

View File

@ -23,6 +23,7 @@ from authentik.stages.email.utils import TemplateEmailMessage
LOGGER = get_logger()
PLAN_CONTEXT_EMAIL_SENT = "email_sent"
PLAN_CONTEXT_EMAIL_OVERRIDE = "email"
class EmailChallenge(Challenge):
@ -83,13 +84,16 @@ class EmailStageView(ChallengeStageView):
"""Helper function that sends the actual email. Implies that you've
already checked that there is a pending user."""
pending_user = self.executor.plan.context[PLAN_CONTEXT_PENDING_USER]
email = self.executor.plan.context.get(PLAN_CONTEXT_EMAIL_OVERRIDE, None)
if not email:
email = pending_user.email
current_stage: EmailStage = self.executor.current_stage
token = self.get_token()
# Send mail to user
message = TemplateEmailMessage(
subject=_(current_stage.subject),
template_name=current_stage.template,
to=[pending_user.email],
to=[email],
template_context={
"url": self.get_full_url(**{QS_KEY_TOKEN: token.key}),
"user": pending_user,

View File

@ -14,7 +14,7 @@ from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
from authentik.flows.tests import FlowTestCase
from authentik.flows.views.executor import SESSION_KEY_PLAN
from authentik.stages.email.models import EmailStage
from authentik.stages.email.stage import QS_KEY_TOKEN
from authentik.stages.email.stage import PLAN_CONTEXT_EMAIL_OVERRIDE, QS_KEY_TOKEN
class TestEmailStage(FlowTestCase):
@ -75,6 +75,27 @@ class TestEmailStage(FlowTestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject, "authentik")
self.assertEqual(mail.outbox[0].to, ["test@beryju.org"])
def test_pending_user_override(self):
"""Test with pending user (override to)"""
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
plan.context[PLAN_CONTEXT_PENDING_USER] = self.user
plan.context[PLAN_CONTEXT_EMAIL_OVERRIDE] = "foo@bar.baz"
session = self.client.session
session[SESSION_KEY_PLAN] = plan
session.save()
url = reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug})
with patch(
"authentik.stages.email.models.EmailStage.backend_class",
PropertyMock(return_value=EmailBackend),
):
response = self.client.post(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject, "authentik")
self.assertEqual(mail.outbox[0].to, ["foo@bar.baz"])
def test_use_global_settings(self):
"""Test use_global_settings"""

View File

@ -54,6 +54,7 @@ class InvitationSerializer(ModelSerializer):
model = Invitation
fields = [
"pk",
"name",
"expires",
"fixed_data",
"created_by",
@ -67,8 +68,8 @@ class InvitationViewSet(UsedByMixin, ModelViewSet):
queryset = Invitation.objects.all()
serializer_class = InvitationSerializer
ordering = ["-expires"]
search_fields = ["created_by__username", "expires"]
filterset_fields = ["created_by__username", "expires"]
search_fields = ["name", "created_by__username", "expires"]
filterset_fields = ["name", "created_by__username", "expires"]
def perform_create(self, serializer: InvitationSerializer):
serializer.save(created_by=self.request.user)

View File

@ -0,0 +1,122 @@
# Generated by Django 4.0.3 on 2022-03-26 17:24
import uuid
import django.db.models.deletion
from django.apps.registry import Apps
from django.conf import settings
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
import authentik.core.models
def migrate_add_name(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
Invitation = apps.get_model("authentik_stages_invitation", "invitation")
for invite in Invitation.objects.using(db_alias).all():
invite.name = invite.pk.hex
invite.save()
class Migration(migrations.Migration):
replaces = [
("authentik_stages_invitation", "0001_initial"),
("authentik_stages_invitation", "0002_auto_20201225_2143"),
("authentik_stages_invitation", "0003_auto_20201227_1210"),
("authentik_stages_invitation", "0004_invitation_single_use"),
("authentik_stages_invitation", "0005_auto_20210901_1211"),
("authentik_stages_invitation", "0006_invitation_name"),
]
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("authentik_flows", "0001_initial"),
]
operations = [
migrations.CreateModel(
name="InvitationStage",
fields=[
(
"stage_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="authentik_flows.stage",
),
),
(
"continue_flow_without_invitation",
models.BooleanField(
default=False,
help_text="If this flag is set, this Stage will jump to the next Stage when no Invitation is given. By default this Stage will cancel the Flow when no invitation is given.",
),
),
],
options={
"verbose_name": "Invitation Stage",
"verbose_name_plural": "Invitation Stages",
},
bases=("authentik_flows.stage",),
),
migrations.CreateModel(
name="Invitation",
fields=[
(
"invite_uuid",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
(
"expires",
models.DateTimeField(default=authentik.core.models.default_token_duration),
),
(
"fixed_data",
models.JSONField(
blank=True,
default=dict,
help_text="Optional fixed data to enforce on user enrollment.",
),
),
(
"created_by",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
),
(
"single_use",
models.BooleanField(
default=False,
help_text="When enabled, the invitation will be deleted after usage.",
),
),
("expiring", models.BooleanField(default=True)),
("name", models.SlugField(default="")),
],
options={
"verbose_name": "Invitation",
"verbose_name_plural": "Invitations",
},
),
migrations.RunPython(
code=migrate_add_name,
),
migrations.AlterField(
model_name="invitation",
name="name",
field=models.SlugField(),
preserve_default=False,
),
]

View File

@ -0,0 +1,38 @@
# Generated by Django 4.0.3 on 2022-03-26 17:22
from django.apps.registry import Apps
from django.db import migrations, models
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
def migrate_add_name(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
db_alias = schema_editor.connection.alias
Invitation = apps.get_model("authentik_stages_invitation", "invitation")
for invite in Invitation.objects.using(db_alias).all():
invite.name = invite.pk.hex
invite.save()
class Migration(migrations.Migration):
dependencies = [
("authentik_stages_invitation", "0005_auto_20210901_1211"),
]
operations = [
migrations.AddField(
model_name="invitation",
name="name",
field=models.SlugField(default=""),
preserve_default=False,
),
migrations.RunPython(migrate_add_name),
migrations.AlterField(
model_name="invitation",
name="name",
field=models.SlugField(),
preserve_default=False,
),
]

View File

@ -52,6 +52,8 @@ class Invitation(ExpiringModel):
invite_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
name = models.SlugField()
single_use = models.BooleanField(
default=False,
help_text=_("When enabled, the invitation will be deleted after usage."),

View File

@ -145,7 +145,7 @@ class TestInvitationsAPI(APITestCase):
"""Test Invitations creation endpoint"""
response = self.client.post(
reverse("authentik_api:invitation-list"),
{"identifier": "test-token", "fixed_data": {}},
{"name": "test-token", "fixed_data": {}},
format="json",
)
self.assertEqual(response.status_code, 201)

View File

@ -119,10 +119,12 @@ class Prompt(SerializerModel):
}
if self.type == FieldTypes.TEXT:
kwargs["trim_whitespace"] = False
kwargs["allow_blank"] = not self.required
if self.type == FieldTypes.TEXT_READ_ONLY:
field_class = ReadOnlyField
if self.type == FieldTypes.EMAIL:
field_class = EmailField
kwargs["allow_blank"] = not self.required
if self.type == FieldTypes.NUMBER:
field_class = IntegerField
if self.type == FieldTypes.CHECKBOX:

View File

@ -8,7 +8,7 @@ from django.http import HttpRequest, HttpResponse
from django.http.request import QueryDict
from django.utils.translation import gettext_lazy as _
from guardian.shortcuts import get_anonymous_user
from rest_framework.fields import BooleanField, CharField, ChoiceField, IntegerField
from rest_framework.fields import BooleanField, CharField, ChoiceField, IntegerField, empty
from rest_framework.serializers import ValidationError
from structlog.stdlib import get_logger
@ -55,6 +55,7 @@ class PromptChallengeResponse(ChallengeResponse):
stage: PromptStage = kwargs.pop("stage", None)
plan: FlowPlan = kwargs.pop("plan", None)
request: HttpRequest = kwargs.pop("request", None)
user: User = kwargs.pop("user", None)
super().__init__(*args, **kwargs)
self.stage = stage
self.plan = plan
@ -65,7 +66,9 @@ class PromptChallengeResponse(ChallengeResponse):
fields = list(self.stage.fields.all())
for field in fields:
field: Prompt
current = plan.context.get(PLAN_CONTEXT_PROMPT, {}).get(field.field_key)
current = field.get_placeholder(
plan.context.get(PLAN_CONTEXT_PROMPT, {}), user, self.request
)
self.fields[field.field_key] = field.field(current)
# Special handling for fields with username type
# these check for existing users with the same username
@ -101,7 +104,11 @@ class PromptChallengeResponse(ChallengeResponse):
)
for static_hidden in static_hidden_fields:
field = self.fields[static_hidden.field_key]
attrs[static_hidden.field_key] = field.default
default = field.default
# Prevent rest_framework.fields.empty from ending up in policies and events
if default == empty:
default = ""
attrs[static_hidden.field_key] = default
# Check if we have two password fields, and make sure they are the same
password_fields: QuerySet[Prompt] = self.stage.fields.filter(type=FieldTypes.PASSWORD)
@ -112,7 +119,6 @@ class PromptChallengeResponse(ChallengeResponse):
engine = ListPolicyEngine(self.stage.validation_policies.all(), user, self.request)
engine.mode = PolicyEngineMode.MODE_ALL
engine.request.context[PLAN_CONTEXT_PROMPT] = attrs
engine.request.context.update(attrs)
engine.build()
result = engine.result
if not result.passing:
@ -191,6 +197,7 @@ class PromptStageView(ChallengeStageView):
request=self.request,
stage=self.executor.current_stage,
plan=self.executor.plan,
user=self.get_pending_user(),
)
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:

View File

@ -129,7 +129,10 @@ class TestPromptStage(FlowTestCase):
def test_valid_challenge_with_policy(self) -> PromptChallengeResponse:
"""Test challenge_response validation"""
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
expr = "return request.context['password_prompt'] == request.context['password2_prompt']"
expr = (
"return request.context['prompt_data']['password_prompt'] "
"== request.context['prompt_data']['password2_prompt']"
)
expr_policy = ExpressionPolicy.objects.create(name="validate-form", expression=expr)
self.stage.validation_policies.set([expr_policy])
self.stage.save()
@ -274,9 +277,6 @@ class TestPromptStage(FlowTestCase):
prompt.get_placeholder(context, self.user, self.factory.get("/")), prompt.placeholder
)
def test_field_types(self):
"""Ensure all field types can successfully be created"""
def test_invalid_save(self):
"""Ensure field can't be saved with invalid type"""
prompt: Prompt = Prompt(
@ -292,7 +292,7 @@ class TestPromptStage(FlowTestCase):
prompt.save()
def field_type_tester_factory(field_type: FieldTypes):
def field_type_tester_factory(field_type: FieldTypes, required: bool):
"""Test field for field_type"""
def tester(self: TestPromptStage):
@ -304,11 +304,16 @@ def field_type_tester_factory(field_type: FieldTypes):
placeholder_expression=False,
sub_text="test",
order=123,
required=required,
)
self.assertIsNotNone(prompt.field("foo"))
return tester
for _type in FieldTypes:
setattr(TestPromptStage, f"test_field_type_{_type}", field_type_tester_factory(_type))
for _required in (True, False):
for _type in FieldTypes:
test_name = f"test_field_type_{_type}"
if _required:
test_name += "_required"
setattr(TestPromptStage, test_name, field_type_tester_factory(_type, _required))

View File

@ -98,7 +98,6 @@ class UserWriteStageView(StageView):
LOGGER.debug("discarding key", key=key)
continue
UserWriteStageView.write_attribute(user, key, value)
print(user.attributes)
# Extra check to prevent flows from saving a user with a blank username
if user.username == "":
LOGGER.warning("Aborting write to empty username", user=user)

View File

@ -53,6 +53,7 @@ class TenantSerializer(ModelSerializer):
"flow_user_settings",
"event_retention",
"web_certificate",
"attributes",
]
@ -86,7 +87,21 @@ class TenantViewSet(UsedByMixin, ModelViewSet):
"branding_title",
"web_certificate__name",
]
filterset_fields = "__all__"
filterset_fields = [
"tenant_uuid",
"domain",
"default",
"branding_title",
"branding_logo",
"branding_favicon",
"flow_authentication",
"flow_invalidation",
"flow_recovery",
"flow_unenrollment",
"flow_user_settings",
"event_retention",
"web_certificate",
]
ordering = ["domain"]
@extend_schema(

View File

@ -17,21 +17,21 @@ from authentik.core.models import (
)
prompt_data = request.context.get("prompt_data")
if not request.user.group_attributes().get(
if not request.user.group_attributes(request.http_request).get(
USER_ATTRIBUTE_CHANGE_EMAIL, CONFIG.y_bool("default_user_change_email", True)
):
if prompt_data.get("email") != request.user.email:
ak_message("Not allowed to change email address.")
return False
if not request.user.group_attributes().get(
if not request.user.group_attributes(request.http_request).get(
USER_ATTRIBUTE_CHANGE_NAME, CONFIG.y_bool("default_user_change_name", True)
):
if prompt_data.get("name") != request.user.name:
ak_message("Not allowed to change name.")
return False
if not request.user.group_attributes().get(
if not request.user.group_attributes(request.http_request).get(
USER_ATTRIBUTE_CHANGE_USERNAME, CONFIG.y_bool("default_user_change_username", True)
):
if prompt_data.get("username") != request.user.username:

View File

@ -0,0 +1,18 @@
# Generated by Django 4.0.3 on 2022-04-06 08:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_tenants", "0002_tenant_flow_user_settings"),
]
operations = [
migrations.AddField(
model_name="tenant",
name="attributes",
field=models.JSONField(blank=True, default=dict),
),
]

View File

@ -63,6 +63,8 @@ class Tenant(models.Model):
help_text=_(("Web Certificate used by the authentik Core webserver.")),
)
attributes = models.JSONField(default=dict, blank=True)
def __str__(self) -> str:
if self.default:
return "Default tenant"

View File

@ -27,6 +27,7 @@ func main() {
log.FieldKeyMsg: "event",
log.FieldKeyTime: "timestamp",
},
DisableHTMLEscape: true,
})
go debug.EnableDebugServer()
akURL, found := os.LookupEnv("AUTHENTIK_HOST")

View File

@ -32,6 +32,7 @@ func main() {
log.FieldKeyMsg: "event",
log.FieldKeyTime: "timestamp",
},
DisableHTMLEscape: true,
})
go debug.EnableDebugServer()
akURL, found := os.LookupEnv("AUTHENTIK_HOST")

View File

@ -28,6 +28,7 @@ func main() {
log.FieldKeyMsg: "event",
log.FieldKeyTime: "timestamp",
},
DisableHTMLEscape: true,
})
go debug.EnableDebugServer()
l := log.WithField("logger", "authentik.root")

View File

@ -17,7 +17,7 @@ services:
image: redis:alpine
restart: unless-stopped
server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.3.3}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.4.1}
restart: unless-stopped
command: server
environment:
@ -38,7 +38,7 @@ services:
- "0.0.0.0:${AUTHENTIK_PORT_HTTP:-9000}:9000"
- "0.0.0.0:${AUTHENTIK_PORT_HTTPS:-9443}:9443"
worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.3.3}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.4.1}
restart: unless-stopped
command: worker
environment:

4
go.mod
View File

@ -7,7 +7,7 @@ require (
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/garyburd/redigo v1.6.2 // indirect
github.com/getsentry/sentry-go v0.13.0
github.com/go-ldap/ldap/v3 v3.4.2
github.com/go-ldap/ldap/v3 v3.4.3
github.com/go-openapi/runtime v0.23.3
github.com/go-openapi/strfmt v0.21.2
github.com/golang-jwt/jwt v3.2.2+incompatible
@ -27,7 +27,7 @@ require (
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.1
goauthentik.io/api/v3 v3.2022032.1
goauthentik.io/api/v3 v3.2022033.11
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c

21
go.sum
View File

@ -32,8 +32,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e h1:ZU22z/2YRFLyf/P4ZwUYSdNCWsMEI0VeyrFoI2rAhJQ=
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
@ -102,8 +102,8 @@ github.com/getsentry/sentry-go v0.13.0 h1:20dgTiUSfxRB/EhMPtxcL9ZEbM1ZdR+W/7f7NW
github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
@ -113,8 +113,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-ldap/ldap/v3 v3.4.2 h1:zFZKcXKLqZpFMrMQGHeHWKXbDTdNCmhGY9AK41zPh+8=
github.com/go-ldap/ldap/v3 v3.4.2/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
github.com/go-ldap/ldap/v3 v3.4.3 h1:JCKUtJPIcyOuG7ctGabLKMgIlKnGumD/iGjuWeEruDI=
github.com/go-ldap/ldap/v3 v3.4.3/go.mod h1:7LdHfVt6iIOESVEe3Bs4Jp2sHEKgDeduAhgM1/f9qmo=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
@ -461,8 +461,8 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
goauthentik.io/api/v3 v3.2022032.1 h1:8PCy0tHUNjGVNJ7nuKPsK64vURFTeqUifyj68RlqMK4=
goauthentik.io/api/v3 v3.2022032.1/go.mod h1:QM9J32HgYE4gL71lWAfAoXSPdSmLVLW08itfLI3Mo10=
goauthentik.io/api/v3 v3.2022033.11 h1:BCE1LgppO135/qAY607EukQBI4Bbta8N23FYUvpSPws=
goauthentik.io/api/v3 v3.2022033.11/go.mod h1:QM9J32HgYE4gL71lWAfAoXSPdSmLVLW08itfLI3Mo10=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -473,12 +473,12 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -545,6 +545,7 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=

View File

@ -86,6 +86,6 @@ func ConfigureLogger() {
if G.Debug {
log.SetFormatter(&log.TextFormatter{FieldMap: fm})
} else {
log.SetFormatter(&log.JSONFormatter{FieldMap: fm})
log.SetFormatter(&log.JSONFormatter{FieldMap: fm, DisableHTMLEscape: true})
}
}

View File

@ -22,7 +22,7 @@ func FullVersion() string {
}
func OutpostUserAgent() string {
return fmt.Sprintf("authentik-outpost@%s", FullVersion())
return fmt.Sprintf("goauthentik.io/outpost/%s", FullVersion())
}
const VERSION = "2022.3.3"
const VERSION = "2022.4.1"

View File

@ -70,7 +70,7 @@ func (g *GoUnicorn) Start() error {
func (g *GoUnicorn) healthcheck() {
g.log.Debug("starting healthcheck")
h := &http.Client{
Transport: ak.NewUserAgentTransport("goauthentik.io go proxy healthcheck", http.DefaultTransport),
Transport: ak.NewUserAgentTransport("goauthentik.io/proxy/healthcheck", http.DefaultTransport),
}
check := func() bool {
res, err := h.Get("http://localhost:8000/-/health/live/")

View File

@ -16,8 +16,12 @@ import (
func doGlobalSetup(outpost api.Outpost, globalConfig api.Config) {
l := log.WithField("logger", "authentik.outpost")
m := outpost.Managed.Get()
level, ok := outpost.Config[ConfigLogLevel]
if !ok {
level = "info"
}
if m == nil || *m == "" {
switch outpost.Config[ConfigLogLevel].(string) {
switch level.(string) {
case "trace":
log.SetLevel(log.TraceLevel)
case "debug":

View File

@ -140,7 +140,7 @@ func (ms *MemorySearcher) Search(req *search.Request) (ldap.ServerSearchResult,
for _, u := range g.UsersObj {
if flags.UserPk == u.Pk {
//TODO: Is there a better way to clone this object?
fg := api.NewGroup(g.Pk, g.Name, g.Parent, g.ParentName, []int32{flags.UserPk}, []api.GroupMember{u})
fg := api.NewGroup(g.Pk, g.NumPk, g.Name, g.Parent, g.ParentName, []int32{flags.UserPk}, []api.GroupMember{u})
fg.SetAttributes(*g.Attributes)
fg.SetIsSuperuser(*g.IsSuperuser)
groups = append(groups, group.FromAPIGroup(*fg, ms.si))

View File

@ -2,9 +2,7 @@ package ldap
import (
"fmt"
"math/big"
"strconv"
"strings"
"goauthentik.io/api/v3"
)
@ -54,20 +52,5 @@ func (pi *ProviderInstance) GetGidNumber(group api.Group) string {
return gidNumber
}
return strconv.FormatInt(int64(pi.gidStartNumber+pi.GetRIDForGroup(group.Pk)), 10)
}
func (pi *ProviderInstance) GetRIDForGroup(uid string) int32 {
var i big.Int
i.SetString(strings.Replace(uid, "-", "", -1), 16)
intStr := i.String()
// Get the last 5 characters/digits of the int-version of the UUID
gid, err := strconv.Atoi(intStr[len(intStr)-5:])
if err != nil {
panic(err)
}
return int32(gid)
return strconv.FormatInt(int64(pi.gidStartNumber+group.NumPk), 10)
}

View File

@ -29,7 +29,9 @@ func ldapResolveTypeSingle(in interface{}) *string {
s := BoolToString(*t)
return &s
default:
log.WithField("type", reflect.TypeOf(in).String()).Warning("Type can't be mapped to LDAP yet")
if in != nil {
log.WithField("type", reflect.TypeOf(in).String()).Warning("Type can't be mapped to LDAP yet")
}
return nil
}
}

View File

@ -7,6 +7,11 @@ import (
"goauthentik.io/api/v3"
)
func Test_ldapResolveTypeSingle_nil(t *testing.T) {
var ex *string
assert.Equal(t, ex, ldapResolveTypeSingle(nil))
}
func TestAKAttrsToLDAP_String(t *testing.T) {
var d *map[string]interface{}
@ -54,7 +59,7 @@ func TestAKAttrsToLDAP_Dict(t *testing.T) {
assert.Equal(t, 1, len(AKAttrsToLDAP(d)))
assert.Equal(t, "foo", AKAttrsToLDAP(d)[0].Name)
// Dicts are currently unsupported, but make sure we don't crash
// assert.Equal(t, []string{nil}, AKAttrsToLDAP(d)[0].Values)
assert.Equal(t, []string([]string(nil)), AKAttrsToLDAP(d)[0].Values)
}
func TestAKAttrsToLDAP_Mixed(t *testing.T) {
@ -68,5 +73,5 @@ func TestAKAttrsToLDAP_Mixed(t *testing.T) {
assert.Equal(t, 1, len(AKAttrsToLDAP(d)))
assert.Equal(t, "foo", AKAttrsToLDAP(d)[0].Name)
// Dicts are currently unsupported, but make sure we don't crash
// assert.Equal(t, []string{nil}, AKAttrsToLDAP(d)[0].Values)
assert.Equal(t, []string{"foo", ""}, AKAttrsToLDAP(d)[0].Values)
}

View File

@ -5,6 +5,7 @@ from json import dumps
from sys import exit as sysexit
from sys import stderr
from time import sleep, time
from urllib.parse import quote_plus
from psycopg2 import OperationalError, connect
from redis import Redis
@ -58,7 +59,7 @@ if CONFIG.y_bool("redis.tls", False):
REDIS_PROTOCOL_PREFIX = "rediss://"
REDIS_URL = (
f"{REDIS_PROTOCOL_PREFIX}:"
f"{CONFIG.y('redis.password')}@{CONFIG.y('redis.host')}:"
f"{quote_plus(CONFIG.y('redis.password'))}@{quote_plus(CONFIG.y('redis.host'))}:"
f"{int(CONFIG.y('redis.port'))}/{CONFIG.y('redis.ws_db')}"
)
while True:

Binary file not shown.

View File

@ -2,7 +2,7 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#
# Translators:
# Vri, 2021
# Lars Lehmann <lars@lars-lehmann.net>, 2021
@ -11,7 +11,7 @@
# Rhea Alleen, 2021
# David <david@techniknews.net>, 2021
# Steve Oswald, 2022
#
#
#, fuzzy
msgid ""
msgstr ""
@ -734,7 +734,7 @@ msgid "RS256 (Asymmetric Encryption)"
msgstr "RS256 (Asymmetrische Verschlüsselung)"
#: authentik/providers/oauth2/models.py:93
msgid "EC256 (Asymmetric Encryption)"
msgid "ES256 (Asymmetric Encryption)"
msgstr "RS256 (Asymmetrische Verschlüsselung)"
#: authentik/providers/oauth2/models.py:99

View File

@ -678,7 +678,7 @@ msgid "RS256 (Asymmetric Encryption)"
msgstr ""
#: authentik/providers/oauth2/models.py:93
msgid "EC256 (Asymmetric Encryption)"
msgid "ES256 (Asymmetric Encryption)"
msgstr ""
#: authentik/providers/oauth2/models.py:99

View File

@ -2,10 +2,10 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#
# Translators:
# jcamat, 2022
#
#
#, fuzzy
msgid ""
msgstr ""
@ -726,8 +726,8 @@ msgid "RS256 (Asymmetric Encryption)"
msgstr "RS256 (cifrado asimétrico)"
#: authentik/providers/oauth2/models.py:93
msgid "EC256 (Asymmetric Encryption)"
msgstr "EC256 (cifrado asimétrico)"
msgid "ES256 (Asymmetric Encryption)"
msgstr "ES256 (cifrado asimétrico)"
#: authentik/providers/oauth2/models.py:99
msgid "Scope used by the client"

View File

@ -2,10 +2,10 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#
# Translators:
# Oktay Altunergil, 2022
#
#
#, fuzzy
msgid ""
msgstr ""
@ -719,8 +719,8 @@ msgid "RS256 (Asymmetric Encryption)"
msgstr "RS256 (Asimetrik Şifreleme)"
#: authentik/providers/oauth2/models.py:93
msgid "EC256 (Asymmetric Encryption)"
msgstr "EC256 (Asimetrik Şifreleme)"
msgid "ES256 (Asymmetric Encryption)"
msgstr "ES256 (Asimetrik Şifreleme)"
#: authentik/providers/oauth2/models.py:99
msgid "Scope used by the client"

Binary file not shown.

View File

@ -2,11 +2,11 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#
# Translators:
# Chen Zhikai, 2022
# 刘松, 2022
#
#
#, fuzzy
msgid ""
msgstr ""
@ -696,8 +696,8 @@ msgid "RS256 (Asymmetric Encryption)"
msgstr "RS256非对称加密"
#: authentik/providers/oauth2/models.py:93
msgid "EC256 (Asymmetric Encryption)"
msgstr "EC256非对称加密"
msgid "ES256 (Asymmetric Encryption)"
msgstr "ES256非对称加密"
#: authentik/providers/oauth2/models.py:99
msgid "Scope used by the client"

Some files were not shown because too many files have changed in this diff Show More